Ensure edit control font size matches font size of text being edited.
Before this commit, the position of the edit box was adjusted by trial and error, as far as I can tell. This commit changes the positioning machinery for edit controls as follows: The coordinates passed to ShowTextEditControl/ShowGraphicsEditControl now denote: X the left bound, and Y the baseline. The font height passed to ShowGraphicsEditControl denotes the absolute font height in pixels, i.e. ascent plus descent. Platform-dependent code uses these coordinates, the font metrics for the font appropriate for the platform, and the knowledge of the decorations drawn around the text by the native edit control to position the edit control in a way that overlays the text inside the edit control with the rendered text. On OS X, GNU Unifont (of height 16) has metrics identical to Monaco (of height 15) and so as an exception, the edit control is nudged slightly for a pixel-perfect fit. Also, since the built-in vector font is proportional, this commit also switches the edit control font to proportional when editing constraints.pull/4/head
parent
0f304b4c64
commit
d17771064a
|
@ -133,7 +133,8 @@ void SolveSpace::ScheduleLater() {
|
||||||
@property BOOL wantsBackingStoreScaling;
|
@property BOOL wantsBackingStoreScaling;
|
||||||
|
|
||||||
@property(readonly, getter=isEditing) BOOL editing;
|
@property(readonly, getter=isEditing) BOOL editing;
|
||||||
- (void)startEditing:(NSString*)text at:(NSPoint)origin;
|
- (void)startEditing:(NSString*)text at:(NSPoint)origin
|
||||||
|
withSize:(double)fontSize usingMonospace:(BOOL)isMonospace;
|
||||||
- (void)stopEditing;
|
- (void)stopEditing;
|
||||||
- (void)didEdit:(NSString*)text;
|
- (void)didEdit:(NSString*)text;
|
||||||
@end
|
@end
|
||||||
|
@ -160,6 +161,8 @@ void SolveSpace::ScheduleLater() {
|
||||||
|
|
||||||
editor = [[NSTextField alloc] init];
|
editor = [[NSTextField alloc] init];
|
||||||
[editor setEditable:YES];
|
[editor setEditable:YES];
|
||||||
|
[[editor cell] setUsesSingleLineMode:YES];
|
||||||
|
[editor setBezeled:NO];
|
||||||
[editor setTarget:self];
|
[editor setTarget:self];
|
||||||
[editor setAction:@selector(editorAction:)];
|
[editor setAction:@selector(editorAction:)];
|
||||||
|
|
||||||
|
@ -202,7 +205,6 @@ CONVERT(Rect)
|
||||||
offscreen = new GLOffscreen;
|
offscreen = new GLOffscreen;
|
||||||
|
|
||||||
NSSize size = [self convertSizeToBacking:[self bounds].size];
|
NSSize size = [self convertSizeToBacking:[self bounds].size];
|
||||||
NSRect bounds = [self convertRectToBacking:[self bounds]];
|
|
||||||
offscreen->begin(size.width, size.height);
|
offscreen->begin(size.width, size.height);
|
||||||
|
|
||||||
[self drawGL];
|
[self drawGL];
|
||||||
|
@ -225,12 +227,24 @@ CONVERT(Rect)
|
||||||
|
|
||||||
@synthesize editing;
|
@synthesize editing;
|
||||||
|
|
||||||
- (void)startEditing:(NSString*)text at:(NSPoint)origin {
|
- (void)startEditing:(NSString*)text at:(NSPoint)origin
|
||||||
|
withSize:(double)fontSize usingMonospace:(BOOL)isMonospace {
|
||||||
if(!self->editing) {
|
if(!self->editing) {
|
||||||
[self addSubview:editor];
|
[self addSubview:editor];
|
||||||
self->editing = YES;
|
self->editing = YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NSFont *font;
|
||||||
|
if(isMonospace)
|
||||||
|
font = [NSFont fontWithName:@"Monaco" size:fontSize];
|
||||||
|
else
|
||||||
|
font = [NSFont controlContentFontOfSize:fontSize];
|
||||||
|
[editor setFont:font];
|
||||||
|
|
||||||
|
origin.x -= 3; /* left padding; no way to get it from NSTextField */
|
||||||
|
origin.y -= [editor intrinsicContentSize].height;
|
||||||
|
origin.y += [editor baselineOffsetFromBottom];
|
||||||
|
|
||||||
[editor setFrameOrigin:origin];
|
[editor setFrameOrigin:origin];
|
||||||
[editor setStringValue:text];
|
[editor setStringValue:text];
|
||||||
[self prepareEditor];
|
[self prepareEditor];
|
||||||
|
@ -251,9 +265,10 @@ CONVERT(Rect)
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)prepareEditor {
|
- (void)prepareEditor {
|
||||||
|
CGFloat intrinsicContentHeight = [editor intrinsicContentSize].height;
|
||||||
[editor setFrameSize:(NSSize){
|
[editor setFrameSize:(NSSize){
|
||||||
.width = 100,
|
.width = intrinsicContentHeight * 12,
|
||||||
.height = [editor intrinsicContentSize].height }];
|
.height = intrinsicContentHeight }];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)didEdit:(NSString*)text {
|
- (void)didEdit:(NSString*)text {
|
||||||
|
@ -382,18 +397,20 @@ CONVERT(Rect)
|
||||||
[super keyDown:event];
|
[super keyDown:event];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)startEditing:(NSString*)text at:(NSPoint)xy {
|
- (void)startEditing:(NSString*)text at:(NSPoint)xy withSize:(double)fontSize {
|
||||||
// Convert to ij (vs. xy) style coordinates
|
// Convert to ij (vs. xy) style coordinates
|
||||||
NSSize size = [self convertSizeToBacking:[self bounds].size];
|
NSSize size = [self convertSizeToBacking:[self bounds].size];
|
||||||
NSPoint point = {
|
NSPoint point = {
|
||||||
.x = xy.x + size.width / 2,
|
.x = xy.x + size.width / 2,
|
||||||
.y = xy.y - size.height / 2 + [editor intrinsicContentSize].height
|
.y = xy.y - size.height / 2
|
||||||
};
|
};
|
||||||
[super startEditing:text at:[self convertPointFromBacking:point]];
|
[super startEditing:text at:[self convertPointFromBacking:point]
|
||||||
|
withSize:fontSize usingMonospace:FALSE];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)didEdit:(NSString*)text {
|
- (void)didEdit:(NSString*)text {
|
||||||
SolveSpace::SS.GW.EditControlDone([text UTF8String]);
|
SolveSpace::SS.GW.EditControlDone([text UTF8String]);
|
||||||
|
[self setNeedsDisplay:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)cancelOperation:(id)sender {
|
- (void)cancelOperation:(id)sender {
|
||||||
|
@ -487,9 +504,10 @@ bool FullScreenIsActive(void) {
|
||||||
return [GWDelegate isFullscreen];
|
return [GWDelegate isFullscreen];
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShowGraphicsEditControl(int x, int y, const std::string &str) {
|
void ShowGraphicsEditControl(int x, int y, int fontSize, const std::string &str) {
|
||||||
[GWView startEditing:[NSString stringWithUTF8String:str.c_str()]
|
[GWView startEditing:[NSString stringWithUTF8String:str.c_str()]
|
||||||
at:(NSPoint){(CGFloat)x, (CGFloat)y}];
|
at:(NSPoint){(CGFloat)x, (CGFloat)y}
|
||||||
|
withSize:fontSize];
|
||||||
}
|
}
|
||||||
|
|
||||||
void HideGraphicsEditControl(void) {
|
void HideGraphicsEditControl(void) {
|
||||||
|
@ -937,8 +955,8 @@ SolveSpace::DialogChoice SolveSpace::LocateImportedFileYesNoCancel(
|
||||||
|
|
||||||
- (void)startEditing:(NSString*)text at:(NSPoint)point {
|
- (void)startEditing:(NSString*)text at:(NSPoint)point {
|
||||||
point = [self convertPointFromBacking:point];
|
point = [self convertPointFromBacking:point];
|
||||||
point.y = -point.y;
|
point.y = -point.y + 2;
|
||||||
[super startEditing:text at:point];
|
[super startEditing:text at:point withSize:15.0 usingMonospace:TRUE];
|
||||||
[[self window] makeKeyWindow];
|
[[self window] makeKeyWindow];
|
||||||
[[self window] makeFirstResponder:editor];
|
[[self window] makeFirstResponder:editor];
|
||||||
}
|
}
|
||||||
|
|
|
@ -372,24 +372,44 @@ public:
|
||||||
EditorOverlay(Gtk::Widget &underlay) : _underlay(underlay) {
|
EditorOverlay(Gtk::Widget &underlay) : _underlay(underlay) {
|
||||||
add(_underlay);
|
add(_underlay);
|
||||||
|
|
||||||
Pango::FontDescription desc;
|
|
||||||
desc.set_family("monospace");
|
|
||||||
desc.set_size(7000);
|
|
||||||
#ifdef HAVE_GTK3
|
|
||||||
_entry.override_font(desc);
|
|
||||||
#else
|
|
||||||
_entry.modify_font(desc);
|
|
||||||
#endif
|
|
||||||
_entry.set_width_chars(30);
|
_entry.set_width_chars(30);
|
||||||
_entry.set_no_show_all(true);
|
_entry.set_no_show_all(true);
|
||||||
|
_entry.set_has_frame(false);
|
||||||
add(_entry);
|
add(_entry);
|
||||||
|
|
||||||
_entry.signal_activate().
|
_entry.signal_activate().
|
||||||
connect(sigc::mem_fun(this, &EditorOverlay::on_activate));
|
connect(sigc::mem_fun(this, &EditorOverlay::on_activate));
|
||||||
}
|
}
|
||||||
|
|
||||||
void start_editing(int x, int y, const std::string &val) {
|
void start_editing(int x, int y, int font_height,
|
||||||
move(_entry, x, y - 4);
|
bool is_monospace, const std::string &val) {
|
||||||
|
Pango::FontDescription font_desc;
|
||||||
|
font_desc.set_family(is_monospace ? "monospace" : "normal");
|
||||||
|
font_desc.set_absolute_size(font_height * Pango::SCALE);
|
||||||
|
|
||||||
|
#ifdef HAVE_GTK3
|
||||||
|
_entry.override_font(font_desc);
|
||||||
|
#else
|
||||||
|
_entry.modify_font(font_desc);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* y coordinate denotes baseline */
|
||||||
|
Pango::FontMetrics font_metrics = get_pango_context()->get_metrics(font_desc);
|
||||||
|
y -= font_metrics.get_ascent() / Pango::SCALE;
|
||||||
|
|
||||||
|
#ifdef HAVE_GTK3
|
||||||
|
Gtk::Border border = _entry.get_style_context()->get_padding();
|
||||||
|
move(_entry, x - border.get_left(), y - border.get_top());
|
||||||
|
#else
|
||||||
|
/* We need _gtk_entry_effective_inner_border, but it's not
|
||||||
|
in the public API, so emulate its logic. */
|
||||||
|
Gtk::Border border = { 2, 2, 2, 2 }, *style_border;
|
||||||
|
gtk_widget_style_get(GTK_WIDGET(_entry.gobj()), "inner-border",
|
||||||
|
&style_border, NULL);
|
||||||
|
if(style_border) border = *style_border;
|
||||||
|
move(_entry, x - border.left, y - border.top);
|
||||||
|
#endif
|
||||||
|
|
||||||
_entry.set_text(val);
|
_entry.set_text(val);
|
||||||
if(!_entry.is_visible()) {
|
if(!_entry.is_visible()) {
|
||||||
_entry.show();
|
_entry.show();
|
||||||
|
@ -721,16 +741,16 @@ bool FullScreenIsActive(void) {
|
||||||
return GW->is_fullscreen();
|
return GW->is_fullscreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShowGraphicsEditControl(int x, int y, const std::string &val) {
|
void ShowGraphicsEditControl(int x, int y, int fontHeight, const std::string &val) {
|
||||||
Gdk::Rectangle rect = GW->get_widget().get_allocation();
|
Gdk::Rectangle rect = GW->get_widget().get_allocation();
|
||||||
|
|
||||||
// Convert to ij (vs. xy) style coordinates,
|
// Convert to ij (vs. xy) style coordinates,
|
||||||
// and compensate for the input widget height due to inverse coord
|
// and compensate for the input widget height due to inverse coord
|
||||||
int i, j;
|
int i, j;
|
||||||
i = x + rect.get_width() / 2;
|
i = x + rect.get_width() / 2;
|
||||||
j = -y + rect.get_height() / 2 - 24;
|
j = -y + rect.get_height() / 2;
|
||||||
|
|
||||||
GW->get_overlay().start_editing(i, j, val);
|
GW->get_overlay().start_editing(i, j, fontHeight, /*is_monospace=*/false, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HideGraphicsEditControl(void) {
|
void HideGraphicsEditControl(void) {
|
||||||
|
@ -1414,7 +1434,8 @@ void SetMousePointerToHand(bool is_hand) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShowTextEditControl(int x, int y, const std::string &val) {
|
void ShowTextEditControl(int x, int y, const std::string &val) {
|
||||||
TW->get_overlay().start_editing(x, y, val);
|
TW->get_overlay().start_editing(x, y, TextWindow::CHAR_HEIGHT,
|
||||||
|
/*is_monospace=*/true, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HideTextEditControl(void) {
|
void HideTextEditControl(void) {
|
||||||
|
|
|
@ -1292,7 +1292,11 @@ void GraphicsWindow::MouseLeftDoubleClick(double mx, double my) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ShowGraphicsEditControl((int)p2.x, (int)p2.y-4, edit_value);
|
hStyle hs = c->disp.style;
|
||||||
|
if(hs.v == 0) hs.v = Style::CONSTRAINT;
|
||||||
|
ShowGraphicsEditControl((int)p2.x, (int)p2.y,
|
||||||
|
ssglStrFontSize(Style::TextHeight(hs)) * scale,
|
||||||
|
edit_value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -235,7 +235,7 @@ void CheckMenuById(int id, bool checked);
|
||||||
void RadioMenuById(int id, bool selected);
|
void RadioMenuById(int id, bool selected);
|
||||||
void EnableMenuById(int id, bool enabled);
|
void EnableMenuById(int id, bool enabled);
|
||||||
|
|
||||||
void ShowGraphicsEditControl(int x, int y, const std::string &str);
|
void ShowGraphicsEditControl(int x, int y, int fontHeight, const std::string &str);
|
||||||
void HideGraphicsEditControl(void);
|
void HideGraphicsEditControl(void);
|
||||||
bool GraphicsEditControlIsVisible(void);
|
bool GraphicsEditControlIsVisible(void);
|
||||||
void ShowTextEditControl(int x, int y, const std::string &str);
|
void ShowTextEditControl(int x, int y, const std::string &str);
|
||||||
|
|
|
@ -88,7 +88,7 @@ void TextWindow::ShowEditControl(int col, const std::string &str, int halfRow) {
|
||||||
int x = LEFT_MARGIN + CHAR_WIDTH*col;
|
int x = LEFT_MARGIN + CHAR_WIDTH*col;
|
||||||
int y = (halfRow - SS.TW.scrollPos)*(LINE_HEIGHT/2);
|
int y = (halfRow - SS.TW.scrollPos)*(LINE_HEIGHT/2);
|
||||||
|
|
||||||
ShowTextEditControl(x - 3, y + 2, str);
|
ShowTextEditControl(x, y + 18, str);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextWindow::ShowEditControlWithColorPicker(int col, RgbaColor rgb)
|
void TextWindow::ShowEditControlWithColorPicker(int col, RgbaColor rgb)
|
||||||
|
|
|
@ -21,10 +21,6 @@
|
||||||
# undef uint32_t // thanks but no thanks
|
# undef uint32_t // thanks but no thanks
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// For the edit controls
|
|
||||||
#define EDIT_WIDTH 220
|
|
||||||
#define EDIT_HEIGHT 21
|
|
||||||
|
|
||||||
HINSTANCE Instance;
|
HINSTANCE Instance;
|
||||||
|
|
||||||
HWND TextWnd;
|
HWND TextWnd;
|
||||||
|
@ -803,8 +799,29 @@ void SolveSpace::InvalidateText(void)
|
||||||
InvalidateRect(TextWnd, NULL, false);
|
InvalidateRect(TextWnd, NULL, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ShowEditControl(HWND h, int x, int y, const std::wstring &s) {
|
static void ShowEditControl(HWND h, int x, int y, int fontHeight,
|
||||||
MoveWindow(h, x, y, EDIT_WIDTH, EDIT_HEIGHT, true);
|
bool isMonospace, const std::wstring &s) {
|
||||||
|
static HFONT hf;
|
||||||
|
if(hf) DeleteObject(hf);
|
||||||
|
hf = CreateFontW(-fontHeight, 0, 0, 0,
|
||||||
|
FW_REGULAR, false, false, false, ANSI_CHARSET,
|
||||||
|
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
|
||||||
|
DEFAULT_QUALITY, FF_DONTCARE, isMonospace ? L"Lucida Console" : L"Arial");
|
||||||
|
if(hf) SendMessage(h, WM_SETFONT, (WPARAM)hf, false);
|
||||||
|
else SendMessage(h, WM_SETFONT, (WPARAM)(HFONT)GetStockObject(SYSTEM_FONT), false);
|
||||||
|
SendMessage(h, EM_SETMARGINS, EC_LEFTMARGIN|EC_RIGHTMARGIN, 0);
|
||||||
|
|
||||||
|
TEXTMETRICW tm;
|
||||||
|
HDC hdc = GetDC(h);
|
||||||
|
SelectObject(hdc, hf);
|
||||||
|
GetTextMetrics(hdc, &tm);
|
||||||
|
ReleaseDC(h, hdc);
|
||||||
|
y -= tm.tmAscent; /* y coordinate denotes baseline */
|
||||||
|
|
||||||
|
RECT rc = { x, y, x + tm.tmAveCharWidth * 30, y + tm.tmHeight };
|
||||||
|
AdjustWindowRectEx(&rc, 0, false, WS_EX_CLIENTEDGE);
|
||||||
|
|
||||||
|
MoveWindow(h, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, true);
|
||||||
ShowWindow(h, SW_SHOW);
|
ShowWindow(h, SW_SHOW);
|
||||||
if(!s.empty()) {
|
if(!s.empty()) {
|
||||||
SendMessage(h, WM_SETTEXT, 0, (LPARAM)s.c_str());
|
SendMessage(h, WM_SETTEXT, 0, (LPARAM)s.c_str());
|
||||||
|
@ -816,7 +833,8 @@ void SolveSpace::ShowTextEditControl(int x, int y, const std::string &str)
|
||||||
{
|
{
|
||||||
if(GraphicsEditControlIsVisible()) return;
|
if(GraphicsEditControlIsVisible()) return;
|
||||||
|
|
||||||
ShowEditControl(TextEditControl, x, y, Widen(str));
|
ShowEditControl(TextEditControl, x, y, TextWindow::CHAR_HEIGHT,
|
||||||
|
/*isMonospace=*/true, Widen(str));
|
||||||
}
|
}
|
||||||
void SolveSpace::HideTextEditControl(void)
|
void SolveSpace::HideTextEditControl(void)
|
||||||
{
|
{
|
||||||
|
@ -826,7 +844,8 @@ bool SolveSpace::TextEditControlIsVisible(void)
|
||||||
{
|
{
|
||||||
return IsWindowVisible(TextEditControl) ? true : false;
|
return IsWindowVisible(TextEditControl) ? true : false;
|
||||||
}
|
}
|
||||||
void SolveSpace::ShowGraphicsEditControl(int x, int y, const std::string &str)
|
void SolveSpace::ShowGraphicsEditControl(int x, int y, int fontHeight,
|
||||||
|
const std::string &str)
|
||||||
{
|
{
|
||||||
if(GraphicsEditControlIsVisible()) return;
|
if(GraphicsEditControlIsVisible()) return;
|
||||||
|
|
||||||
|
@ -835,11 +854,8 @@ void SolveSpace::ShowGraphicsEditControl(int x, int y, const std::string &str)
|
||||||
x = x + (r.right - r.left)/2;
|
x = x + (r.right - r.left)/2;
|
||||||
y = (r.bottom - r.top)/2 - y;
|
y = (r.bottom - r.top)/2 - y;
|
||||||
|
|
||||||
// (x, y) are the bottom left, but the edit control is placed by its
|
ShowEditControl(GraphicsEditControl, x, y, fontHeight,
|
||||||
// top left corner
|
/*isMonospace=*/false, Widen(str));
|
||||||
y -= 20;
|
|
||||||
|
|
||||||
ShowEditControl(GraphicsEditControl, x, y, Widen(str));
|
|
||||||
}
|
}
|
||||||
void SolveSpace::HideGraphicsEditControl(void)
|
void SolveSpace::HideGraphicsEditControl(void)
|
||||||
{
|
{
|
||||||
|
@ -1250,7 +1266,6 @@ static void CreateMainWindows(void)
|
||||||
GraphicsEditControl = CreateWindowExW(WS_EX_CLIENTEDGE, WC_EDIT, L"",
|
GraphicsEditControl = CreateWindowExW(WS_EX_CLIENTEDGE, WC_EDIT, L"",
|
||||||
WS_CHILD | ES_AUTOHSCROLL | WS_TABSTOP | WS_CLIPSIBLINGS,
|
WS_CHILD | ES_AUTOHSCROLL | WS_TABSTOP | WS_CLIPSIBLINGS,
|
||||||
50, 50, 100, 21, GraphicsWnd, NULL, Instance, NULL);
|
50, 50, 100, 21, GraphicsWnd, NULL, Instance, NULL);
|
||||||
SendMessage(GraphicsEditControl, WM_SETFONT, (WPARAM)FixedFont, true);
|
|
||||||
|
|
||||||
// The text window, with a comand line and some textual information
|
// The text window, with a comand line and some textual information
|
||||||
// about the sketch.
|
// about the sketch.
|
||||||
|
@ -1277,7 +1292,6 @@ static void CreateMainWindows(void)
|
||||||
TextEditControl = CreateWindowExW(WS_EX_CLIENTEDGE, WC_EDIT, L"",
|
TextEditControl = CreateWindowExW(WS_EX_CLIENTEDGE, WC_EDIT, L"",
|
||||||
WS_CHILD | ES_AUTOHSCROLL | WS_TABSTOP | WS_CLIPSIBLINGS,
|
WS_CHILD | ES_AUTOHSCROLL | WS_TABSTOP | WS_CLIPSIBLINGS,
|
||||||
50, 50, 100, 21, TextWnd, NULL, Instance, NULL);
|
50, 50, 100, 21, TextWnd, NULL, Instance, NULL);
|
||||||
SendMessage(TextEditControl, WM_SETFONT, (WPARAM)FixedFont, true);
|
|
||||||
|
|
||||||
// Now that all our windows exist, set up gl contexts.
|
// Now that all our windows exist, set up gl contexts.
|
||||||
CreateGlContext(TextWnd, &TextGl);
|
CreateGlContext(TextWnd, &TextGl);
|
||||||
|
|
Loading…
Reference in New Issue