Make graphics window edit control width fit the content.

Before this commit, the graphics window edit control always had
a width of 30 average character widths.

After this commit, the edit control has a width of 5 average
character widths (for numeric constraints) or 30 average character
widths (for comment constraints), or just enough to display
the entire value being edited, whichever is greater.

This makes the edit control overlap the sketch less in case of
editing numeric constraints (since in most cases, the numbers being
edited are short), and removes annoying scrolling in case of editing
long comments.
pull/4/head
whitequark 2016-04-16 00:10:32 +00:00
parent d17771064a
commit 23dc36da9b
5 changed files with 74 additions and 51 deletions

View File

@ -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];
}

View File

@ -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<Pango::Layout> 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) {

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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)