diff --git a/src/cocoa/cocoamain.mm b/src/cocoa/cocoamain.mm index 9931706..fa46b80 100644 --- a/src/cocoa/cocoamain.mm +++ b/src/cocoa/cocoamain.mm @@ -133,8 +133,8 @@ void SolveSpace::ScheduleLater() { @property BOOL wantsBackingStoreScaling; @property(readonly, getter=isEditing) BOOL editing; -- (void)startEditing:(NSString*)text at:(NSPoint)origin - withSize:(double)fontSize usingMonospace:(BOOL)isMonospace; +- (void)startEditing:(NSString*)text at:(NSPoint)origin withHeight:(double)fontHeight + usingMonospace:(BOOL)isMonospace; - (void)stopEditing; - (void)didEdit:(NSString*)text; @end @@ -161,7 +161,8 @@ void SolveSpace::ScheduleLater() { editor = [[NSTextField alloc] init]; [editor setEditable:YES]; - [[editor cell] setUsesSingleLineMode:YES]; + [[editor cell] setWraps:NO]; + [[editor cell] setScrollable:YES]; [editor setBezeled:NO]; [editor setTarget:self]; [editor setAction:@selector(editorAction:)]; @@ -227,8 +228,8 @@ CONVERT(Rect) @synthesize editing; -- (void)startEditing:(NSString*)text at:(NSPoint)origin - withSize:(double)fontSize usingMonospace:(BOOL)isMonospace { +- (void)startEditing:(NSString*)text at:(NSPoint)origin withHeight:(double)fontHeight + usingMonospace:(BOOL)isMonospace { if(!self->editing) { [self addSubview:editor]; self->editing = YES; @@ -236,9 +237,9 @@ CONVERT(Rect) NSFont *font; if(isMonospace) - font = [NSFont fontWithName:@"Monaco" size:fontSize]; + font = [NSFont fontWithName:@"Monaco" size:fontHeight]; else - font = [NSFont controlContentFontOfSize:fontSize]; + font = [NSFont controlContentFontOfSize:fontHeight]; [editor setFont:font]; origin.x -= 3; /* left padding; no way to get it from NSTextField */ @@ -247,7 +248,6 @@ CONVERT(Rect) [editor setFrameOrigin:origin]; [editor setStringValue:text]; - [self prepareEditor]; [[self window] becomeKeyWindow]; [[self window] makeFirstResponder:editor]; } @@ -264,13 +264,6 @@ CONVERT(Rect) [self stopEditing]; } -- (void)prepareEditor { - CGFloat intrinsicContentHeight = [editor intrinsicContentSize].height; - [editor setFrameSize:(NSSize){ - .width = intrinsicContentHeight * 12, - .height = intrinsicContentHeight }]; -} - - (void)didEdit:(NSString*)text { } @end @@ -397,7 +390,8 @@ CONVERT(Rect) [super keyDown:event]; } -- (void)startEditing:(NSString*)text at:(NSPoint)xy withSize:(double)fontSize { +- (void)startEditing:(NSString*)text at:(NSPoint)xy withHeight:(double)fontHeight + withMinWidthInChars:(int)minWidthChars { // Convert to ij (vs. xy) style coordinates NSSize size = [self convertSizeToBacking:[self bounds].size]; NSPoint point = { @@ -405,7 +399,21 @@ CONVERT(Rect) .y = xy.y - size.height / 2 }; [super startEditing:text at:[self convertPointFromBacking:point] - withSize:fontSize usingMonospace:FALSE]; + withHeight:fontHeight usingMonospace:FALSE]; + [self prepareEditorWithMinWidthInChars:minWidthChars]; +} + +- (void)prepareEditorWithMinWidthInChars:(int)minWidthChars { + NSFont *font = [editor font]; + NSGlyph glyphA = [font glyphWithName:@"a"]; + if(glyphA == -1) oops(); + CGFloat glyphAWidth = [font advancementForGlyph:glyphA].width; + + [editor sizeToFit]; + + NSSize frameSize = [editor frame].size; + frameSize.width = std::max(frameSize.width, glyphAWidth * minWidthChars); + [editor setFrameSize:frameSize]; } - (void)didEdit:(NSString*)text { @@ -504,10 +512,12 @@ bool FullScreenIsActive(void) { return [GWDelegate isFullscreen]; } -void ShowGraphicsEditControl(int x, int y, int fontSize, const std::string &str) { +void ShowGraphicsEditControl(int x, int y, int fontHeight, int minWidthChars, + const std::string &str) { [GWView startEditing:[NSString stringWithUTF8String:str.c_str()] at:(NSPoint){(CGFloat)x, (CGFloat)y} - withSize:fontSize]; + withHeight:fontHeight + withMinWidthInChars:minWidthChars]; } void HideGraphicsEditControl(void) { @@ -956,9 +966,10 @@ SolveSpace::DialogChoice SolveSpace::LocateImportedFileYesNoCancel( - (void)startEditing:(NSString*)text at:(NSPoint)point { point = [self convertPointFromBacking:point]; point.y = -point.y + 2; - [super startEditing:text at:point withSize:15.0 usingMonospace:TRUE]; - [[self window] makeKeyWindow]; - [[self window] makeFirstResponder:editor]; + [super startEditing:text at:point withHeight:15.0 usingMonospace:TRUE]; + [editor setFrameSize:(NSSize){ + .width = [self bounds].size.width - [editor frame].origin.x, + .height = [editor intrinsicContentSize].height }]; } - (void)stopEditing { @@ -970,12 +981,6 @@ SolveSpace::DialogChoice SolveSpace::LocateImportedFileYesNoCancel( SolveSpace::SS.TW.EditControlDone([text UTF8String]); } -- (void)prepareEditor { - [editor setFrameSize:(NSSize){ - .width = [self bounds].size.width - [editor frame].origin.x, - .height = [editor intrinsicContentSize].height }]; -} - - (void)cancelOperation:(id)sender { [self stopEditing]; } diff --git a/src/gtk/gtkmain.cpp b/src/gtk/gtkmain.cpp index 2c130f6..ab54d56 100644 --- a/src/gtk/gtkmain.cpp +++ b/src/gtk/gtkmain.cpp @@ -372,7 +372,6 @@ public: EditorOverlay(Gtk::Widget &underlay) : _underlay(underlay) { add(_underlay); - _entry.set_width_chars(30); _entry.set_no_show_all(true); _entry.set_has_frame(false); add(_entry); @@ -381,8 +380,8 @@ public: connect(sigc::mem_fun(this, &EditorOverlay::on_activate)); } - void start_editing(int x, int y, int font_height, - bool is_monospace, const std::string &val) { + void start_editing(int x, int y, int font_height, bool is_monospace, int minWidthChars, + 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); @@ -397,6 +396,12 @@ public: Pango::FontMetrics font_metrics = get_pango_context()->get_metrics(font_desc); y -= font_metrics.get_ascent() / Pango::SCALE; + Glib::RefPtr layout = Pango::Layout::create(get_pango_context()); + layout->set_font_description(font_desc); + layout->set_text(val + " "); /* avoid scrolling */ + int width = std::max(minWidthChars * font_metrics.get_approximate_char_width(), + layout->get_logical_extents().get_width()) / Pango::SCALE; + #ifdef HAVE_GTK3 Gtk::Border border = _entry.get_style_context()->get_padding(); move(_entry, x - border.get_left(), y - border.get_top()); @@ -411,6 +416,7 @@ public: #endif _entry.set_text(val); + _entry.set_size_request(width, -1); if(!_entry.is_visible()) { _entry.show(); _entry.grab_focus(); @@ -741,7 +747,8 @@ bool FullScreenIsActive(void) { return GW->is_fullscreen(); } -void ShowGraphicsEditControl(int x, int y, int fontHeight, const std::string &val) { +void ShowGraphicsEditControl(int x, int y, int fontHeight, int minWidthChars, + const std::string &val) { Gdk::Rectangle rect = GW->get_widget().get_allocation(); // Convert to ij (vs. xy) style coordinates, @@ -750,7 +757,7 @@ void ShowGraphicsEditControl(int x, int y, int fontHeight, const std::string &va i = x + rect.get_width() / 2; j = -y + rect.get_height() / 2; - GW->get_overlay().start_editing(i, j, fontHeight, /*is_monospace=*/false, val); + GW->get_overlay().start_editing(i, j, fontHeight, /*is_monospace=*/false, minWidthChars, val); } void HideGraphicsEditControl(void) { @@ -1434,8 +1441,7 @@ void SetMousePointerToHand(bool is_hand) { } void ShowTextEditControl(int x, int y, const std::string &val) { - TW->get_overlay().start_editing(x, y, TextWindow::CHAR_HEIGHT, - /*is_monospace=*/true, val); + TW->get_overlay().start_editing(x, y, TextWindow::CHAR_HEIGHT, /*is_monospace=*/true, 30, val); } void HideTextEditControl(void) { diff --git a/src/mouse.cpp b/src/mouse.cpp index ae66dd7..8ae1a2e 100644 --- a/src/mouse.cpp +++ b/src/mouse.cpp @@ -1254,16 +1254,19 @@ void GraphicsWindow::MouseLeftDoubleClick(double mx, double my) { Vector p3 = c->GetLabelPos(); Point2d p2 = ProjectPoint(p3); - std::string edit_value; + std::string editValue; + int editMinWidthChar; switch(c->type) { case Constraint::COMMENT: - edit_value = c->comment; + editValue = c->comment; + editMinWidthChar = 30; break; case Constraint::ANGLE: case Constraint::LENGTH_RATIO: case Constraint::LENGTH_DIFFERENCE: - edit_value = ssprintf("%.3f", c->valA); + editValue = ssprintf("%.3f", c->valA); + editMinWidthChar = 5; break; default: { @@ -1278,17 +1281,18 @@ void GraphicsWindow::MouseLeftDoubleClick(double mx, double my) { if(fabs(SS.StringToMm(def) - v) < eps) { // Show value with default number of digits after decimal, // which is at least enough to represent it exactly. - edit_value = def; + editValue = def; } else { // Show value with as many digits after decimal as // required to represent it exactly, up to 10. v /= SS.MmPerUnit(); int i; for(i = 0; i <= 10; i++) { - edit_value = ssprintf("%.*f", i, v); - if(fabs(std::stod(edit_value) - v) < eps) break; + editValue = ssprintf("%.*f", i, v); + if(fabs(std::stod(editValue) - v) < eps) break; } } + editMinWidthChar = 5; break; } } @@ -1296,7 +1300,7 @@ void GraphicsWindow::MouseLeftDoubleClick(double mx, double my) { if(hs.v == 0) hs.v = Style::CONSTRAINT; ShowGraphicsEditControl((int)p2.x, (int)p2.y, ssglStrFontSize(Style::TextHeight(hs)) * scale, - edit_value); + editMinWidthChar, editValue); } } diff --git a/src/solvespace.h b/src/solvespace.h index 1b0c0b8..8786e36 100644 --- a/src/solvespace.h +++ b/src/solvespace.h @@ -235,7 +235,8 @@ void CheckMenuById(int id, bool checked); void RadioMenuById(int id, bool selected); void EnableMenuById(int id, bool enabled); -void ShowGraphicsEditControl(int x, int y, int fontHeight, const std::string &str); +void ShowGraphicsEditControl(int x, int y, int fontHeight, int minWidthChars, + const std::string &str); void HideGraphicsEditControl(void); bool GraphicsEditControlIsVisible(void); void ShowTextEditControl(int x, int y, const std::string &str); diff --git a/src/win32/w32main.cpp b/src/win32/w32main.cpp index f8caaf8..e206dad 100644 --- a/src/win32/w32main.cpp +++ b/src/win32/w32main.cpp @@ -799,7 +799,7 @@ void SolveSpace::InvalidateText(void) InvalidateRect(TextWnd, NULL, false); } -static void ShowEditControl(HWND h, int x, int y, int fontHeight, +static void ShowEditControl(HWND h, int x, int y, int fontHeight, int minWidthChars, bool isMonospace, const std::wstring &s) { static HFONT hf; if(hf) DeleteObject(hf); @@ -811,16 +811,23 @@ static void ShowEditControl(HWND h, int x, int y, int fontHeight, 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); + TEXTMETRICW tm; + SIZE ts; SelectObject(hdc, hf); GetTextMetrics(hdc, &tm); + GetTextExtentPoint32W(hdc, s.c_str(), s.length(), &ts); ReleaseDC(h, hdc); - y -= tm.tmAscent; /* y coordinate denotes baseline */ - RECT rc = { x, y, x + tm.tmAveCharWidth * 30, y + tm.tmHeight }; + RECT rc; + rc.left = x; + rc.top = y - tm.tmAscent; + // Add one extra char width to avoid scrolling. + rc.right = x + std::max(tm.tmAveCharWidth * minWidthChars, + ts.cx + tm.tmAveCharWidth); + rc.bottom = y + tm.tmDescent; + 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); if(!s.empty()) { @@ -833,7 +840,7 @@ void SolveSpace::ShowTextEditControl(int x, int y, const std::string &str) { if(GraphicsEditControlIsVisible()) return; - ShowEditControl(TextEditControl, x, y, TextWindow::CHAR_HEIGHT, + ShowEditControl(TextEditControl, x, y, TextWindow::CHAR_HEIGHT, 30, /*isMonospace=*/true, Widen(str)); } void SolveSpace::HideTextEditControl(void) @@ -844,7 +851,7 @@ bool SolveSpace::TextEditControlIsVisible(void) { return IsWindowVisible(TextEditControl) ? true : false; } -void SolveSpace::ShowGraphicsEditControl(int x, int y, int fontHeight, +void SolveSpace::ShowGraphicsEditControl(int x, int y, int fontHeight, int minWidthChars, const std::string &str) { if(GraphicsEditControlIsVisible()) return; @@ -854,7 +861,7 @@ void SolveSpace::ShowGraphicsEditControl(int x, int y, int fontHeight, x = x + (r.right - r.left)/2; y = (r.bottom - r.top)/2 - y; - ShowEditControl(GraphicsEditControl, x, y, fontHeight, + ShowEditControl(GraphicsEditControl, x, y, fontHeight, minWidthChars, /*isMonospace=*/false, Widen(str)); } void SolveSpace::HideGraphicsEditControl(void)