Allow docking text window into graphics window.

It can be convenient to dock the text window onto the graphics
window, e.g. for fullscreen support, for window managers that
don't handle two side-by-side windows easily, for ease of dragging,
etc. Also, emscripten only provides access to one OpenGL context,
so it's a requirement for a WebGL port.

This isn't yet fully usable: it misses scrollbar support, and
there is no way to resize the dock.
This commit is contained in:
whitequark 2016-11-19 11:27:21 +00:00
parent 43629549c1
commit b7a605c44c
14 changed files with 228 additions and 151 deletions

View File

@ -161,6 +161,11 @@ void TextWindow::ScreenChangeAutosaveInterval(int link, uint32_t v) {
SS.TW.edit.meaning = Edit::AUTOSAVE_INTERVAL; SS.TW.edit.meaning = Edit::AUTOSAVE_INTERVAL;
} }
void TextWindow::ScreenChangeTextPaneEnabled(int link, uint32_t v) {
SS.GW.dockTextWindow = !SS.GW.dockTextWindow;
ShowTextWindow(SS.GW.showTextWindow && !SS.GW.dockTextWindow);
}
void TextWindow::ShowConfiguration() { void TextWindow::ShowConfiguration() {
int i; int i;
Printf(true, "%Ft user color (r, g, b)"); Printf(true, "%Ft user color (r, g, b)");
@ -303,6 +308,11 @@ void TextWindow::ShowConfiguration() {
Printf(false, "%Ba %d %Fl%Ll%f[change]%E", Printf(false, "%Ba %d %Fl%Ll%f[change]%E",
SS.autosaveInterval, &ScreenChangeAutosaveInterval); SS.autosaveInterval, &ScreenChangeAutosaveInterval);
Printf(false, "");
Printf(false, " %Fd%f%Ll%s single window UI%E",
&ScreenChangeTextPaneEnabled,
SS.GW.dockTextWindow ? CHECK_TRUE : CHECK_FALSE);
if(canvas) { if(canvas) {
const char *gl_vendor, *gl_renderer, *gl_version; const char *gl_vendor, *gl_renderer, *gl_version;
canvas->GetIdent(&gl_vendor, &gl_renderer, &gl_version); canvas->GetIdent(&gl_vendor, &gl_renderer, &gl_version);

View File

@ -751,14 +751,7 @@ void GraphicsWindow::Paint() {
havePainted = true; havePainted = true;
int w, h;
GetGraphicsWindowSize(&w, &h);
width = w;
height = h;
Camera camera = GetCamera();
Lighting lighting = GetLighting(); Lighting lighting = GetLighting();
if(!SS.ActiveGroupsOkay()) { if(!SS.ActiveGroupsOkay()) {
// Draw a different background whenever we're having solve problems. // Draw a different background whenever we're having solve problems.
RgbaColor bgColor = Style::Color(Style::DRAW_ERROR); RgbaColor bgColor = Style::Color(Style::DRAW_ERROR);
@ -770,9 +763,22 @@ void GraphicsWindow::Paint() {
ForceTextWindowShown(); ForceTextWindowShown();
} }
int windowWidth, windowHeight;
GetGraphicsWindowSize(&windowWidth, &windowHeight);
if(showTextWindow && dockTextWindow) {
width = windowWidth - textDockWidth;
height = windowHeight;
SS.TW.Paint(canvas, windowWidth - textDockWidth, 0, textDockWidth, windowHeight);
} else {
width = windowWidth;
height = windowHeight;
}
Camera camera = GetCamera();
auto renderStartTime = std::chrono::high_resolution_clock::now(); auto renderStartTime = std::chrono::high_resolution_clock::now();
canvas->NewFrame(); canvas->NewFrame(0, 0, width, height);
canvas->SetCamera(camera); canvas->SetCamera(camera);
canvas->SetLighting(lighting); canvas->SetLighting(lighting);
Draw(canvas.get()); Draw(canvas.get());

View File

@ -235,7 +235,7 @@ void GraphicsWindow::Init() {
drawOccludedAs = DrawOccludedAs::INVISIBLE; drawOccludedAs = DrawOccludedAs::INVISIBLE;
showTextWindow = true; showTextWindow = true;
ShowTextWindow(showTextWindow); ShowTextWindow(showTextWindow && !dockTextWindow);
showSnapGrid = false; showSnapGrid = false;
context.active = false; context.active = false;
@ -687,7 +687,7 @@ void GraphicsWindow::EnsureValidActives() {
RadioMenuByCmd(Command::UNITS_MM, SS.viewUnits == Unit::MM); RadioMenuByCmd(Command::UNITS_MM, SS.viewUnits == Unit::MM);
RadioMenuByCmd(Command::UNITS_INCHES, SS.viewUnits == Unit::INCHES); RadioMenuByCmd(Command::UNITS_INCHES, SS.viewUnits == Unit::INCHES);
ShowTextWindow(SS.GW.showTextWindow); ShowTextWindow(showTextWindow && !dockTextWindow);
CheckMenuByCmd(Command::SHOW_TEXT_WND, /*checked=*/SS.GW.showTextWindow); CheckMenuByCmd(Command::SHOW_TEXT_WND, /*checked=*/SS.GW.showTextWindow);
#if defined(__APPLE__) #if defined(__APPLE__)
@ -722,7 +722,7 @@ void GraphicsWindow::ForceTextWindowShown() {
if(!showTextWindow) { if(!showTextWindow) {
showTextWindow = true; showTextWindow = true;
CheckMenuByCmd(Command::SHOW_TEXT_WND, /*checked=*/true); CheckMenuByCmd(Command::SHOW_TEXT_WND, /*checked=*/true);
ShowTextWindow(true); ShowTextWindow(!dockTextWindow);
} }
} }

View File

@ -75,9 +75,27 @@ void GraphicsWindow::StartDraggingBySelection() {
if(hover.entity.v) StartDraggingByEntity(hover.entity); if(hover.entity.v) StartDraggingByEntity(hover.entity);
} }
bool GraphicsWindow::ConvertMouseCoords(double *x, double *y) {
if(showTextWindow && dockTextWindow) {
if(*x > width) {
*x -= width;
return true;
}
}
// Convert to OpenGL coordinates.
*x = *x - width / 2;
*y = height / 2 - *y;
return false;
}
void GraphicsWindow::MouseMoved(double x, double y, bool leftDown, void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
bool middleDown, bool rightDown, bool shiftDown, bool ctrlDown) bool middleDown, bool rightDown, bool shiftDown, bool ctrlDown) {
{ if(ConvertMouseCoords(&x, &y)) {
SS.TW.MouseEvent(/*isClick=*/(leftDown || middleDown || rightDown), leftDown, x, y);
return;
}
if(GraphicsEditControlIsVisible()) return; if(GraphicsEditControlIsVisible()) return;
if(context.active) return; if(context.active) return;
@ -470,6 +488,11 @@ void GraphicsWindow::ClearPending() {
} }
void GraphicsWindow::MouseMiddleOrRightDown(double x, double y) { void GraphicsWindow::MouseMiddleOrRightDown(double x, double y) {
if(ConvertMouseCoords(&x, &y)) {
SS.TW.MouseEvent(/*isClick=*/true, /*leftDown=*/false, x, y);
return;
}
if(GraphicsEditControlIsVisible()) return; if(GraphicsEditControlIsVisible()) return;
orig.offset = offset; orig.offset = offset;
@ -498,6 +521,8 @@ void GraphicsWindow::ContextMenuListStyles() {
} }
void GraphicsWindow::MouseRightUp(double x, double y) { void GraphicsWindow::MouseRightUp(double x, double y) {
if(ConvertMouseCoords(&x, &y)) return;
SS.extraLine.draw = false; SS.extraLine.draw = false;
InvalidateGraphics(); InvalidateGraphics();
@ -893,7 +918,14 @@ bool GraphicsWindow::ConstrainPointByHovered(hEntity pt) {
return false; return false;
} }
void GraphicsWindow::MouseLeftDown(double mx, double my) { void GraphicsWindow::MouseLeftDown(double origMx, double origMy) {
double mx = origMx,
my = origMy;
if(ConvertMouseCoords(&mx, &my)) {
SS.TW.MouseEvent(/*isClick=*/true, /*leftDown=*/true, mx, my);
return;
}
orig.mouseDown = true; orig.mouseDown = true;
if(GraphicsEditControlIsVisible()) { if(GraphicsEditControlIsVisible()) {
@ -912,7 +944,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) {
bool hasConstraintSuggestion = SS.GW.pending.hasSuggestion; bool hasConstraintSuggestion = SS.GW.pending.hasSuggestion;
// Make sure the hover is up to date. // Make sure the hover is up to date.
MouseMoved(mx, my, /*leftDown=*/false, /*middleDown=*/false, /*rightDown=*/false, MouseMoved(origMx, origMy, /*leftDown=*/false, /*middleDown=*/false, /*rightDown=*/false,
/*shiftDown=*/false, /*ctrlDown=*/false); /*shiftDown=*/false, /*ctrlDown=*/false);
orig.mouse.x = mx; orig.mouse.x = mx;
orig.mouse.y = my; orig.mouse.y = my;
@ -1232,6 +1264,8 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) {
} }
void GraphicsWindow::MouseLeftUp(double mx, double my) { void GraphicsWindow::MouseLeftUp(double mx, double my) {
if(ConvertMouseCoords(&mx, &my)) return;
orig.mouseDown = false; orig.mouseDown = false;
hoverWasSelectedOnMousedown = false; hoverWasSelectedOnMousedown = false;
@ -1272,6 +1306,8 @@ void GraphicsWindow::MouseLeftUp(double mx, double my) {
} }
void GraphicsWindow::MouseLeftDoubleClick(double mx, double my) { void GraphicsWindow::MouseLeftDoubleClick(double mx, double my) {
if(ConvertMouseCoords(&mx, &my)) return;
if(GraphicsEditControlIsVisible()) return; if(GraphicsEditControlIsVisible()) return;
SS.TW.HideEditControl(); SS.TW.HideEditControl();
@ -1335,13 +1371,19 @@ void GraphicsWindow::MouseLeftDoubleClick(double mx, double my) {
} }
hStyle hs = c->disp.style; hStyle hs = c->disp.style;
if(hs.v == 0) hs.v = Style::CONSTRAINT; if(hs.v == 0) hs.v = Style::CONSTRAINT;
ShowGraphicsEditControl((int)p2.x, (int)p2.y, SS.TW.editControl.inDock = false;
ShowGraphicsEditControl((int)p2.x + width / 2, height / 2 - (int)p2.y,
(int)(VectorFont::Builtin()->GetHeight(Style::TextHeight(hs))), (int)(VectorFont::Builtin()->GetHeight(Style::TextHeight(hs))),
editMinWidthChar, editValue); editMinWidthChar, editValue, /*forDock=*/false);
} }
} }
void GraphicsWindow::EditControlDone(const char *s) { void GraphicsWindow::EditControlDone(const char *s) {
if(SS.TW.editControl.inDock) {
SS.TW.EditControlDone(s);
return;
}
HideGraphicsEditControl(); HideGraphicsEditControl();
Constraint *c = SK.GetConstraint(constraintBeingEdited); Constraint *c = SK.GetConstraint(constraintBeingEdited);
@ -1416,6 +1458,11 @@ bool GraphicsWindow::KeyDown(int c) {
} }
void GraphicsWindow::MouseScroll(double x, double y, int delta) { void GraphicsWindow::MouseScroll(double x, double y, int delta) {
if(ConvertMouseCoords(&x, &y)) {
// FIXME SS.TW.MouseScroll(x, y, delta);
return;
}
double offsetRight = offset.Dot(projRight); double offsetRight = offset.Dot(projRight);
double offsetUp = offset.Dot(projUp); double offsetUp = offset.Dot(projUp);

View File

@ -287,7 +287,7 @@ CONVERT(Rect)
} }
- (void)mouseMoved:(NSEvent*)event { - (void)mouseMoved:(NSEvent*)event {
NSPoint point = [self ij_to_xy:[self convertPoint:[event locationInWindow] fromView:nil]]; NSPoint point = [self xyFromEvent:event];
NSUInteger flags = [event modifierFlags]; NSUInteger flags = [event modifierFlags];
NSUInteger buttons = [NSEvent pressedMouseButtons]; NSUInteger buttons = [NSEvent pressedMouseButtons];
SolveSpace::SS.GW.MouseMoved(point.x, point.y, SolveSpace::SS.GW.MouseMoved(point.x, point.y,
@ -311,7 +311,7 @@ CONVERT(Rect)
} }
- (void)mouseDown:(NSEvent*)event { - (void)mouseDown:(NSEvent*)event {
NSPoint point = [self ij_to_xy:[self convertPoint:[event locationInWindow] fromView:nil]]; NSPoint point = [self xyFromEvent:event];
if([event clickCount] == 1) if([event clickCount] == 1)
SolveSpace::SS.GW.MouseLeftDown(point.x, point.y); SolveSpace::SS.GW.MouseLeftDown(point.x, point.y);
else if([event clickCount] == 2) else if([event clickCount] == 2)
@ -319,7 +319,7 @@ CONVERT(Rect)
} }
- (void)rightMouseDown:(NSEvent*)event { - (void)rightMouseDown:(NSEvent*)event {
NSPoint point = [self ij_to_xy:[self convertPoint:[event locationInWindow] fromView:nil]]; NSPoint point = [self xyFromEvent:event];
SolveSpace::SS.GW.MouseMiddleOrRightDown(point.x, point.y); SolveSpace::SS.GW.MouseMiddleOrRightDown(point.x, point.y);
} }
@ -328,18 +328,18 @@ CONVERT(Rect)
} }
- (void)mouseUp:(NSEvent*)event { - (void)mouseUp:(NSEvent*)event {
NSPoint point = [self ij_to_xy:[self convertPoint:[event locationInWindow] fromView:nil]]; NSPoint point = [self xyFromEvent:event];
SolveSpace::SS.GW.MouseLeftUp(point.x, point.y); SolveSpace::SS.GW.MouseLeftUp(point.x, point.y);
} }
- (void)rightMouseUp:(NSEvent*)event { - (void)rightMouseUp:(NSEvent*)event {
NSPoint point = [self ij_to_xy:[self convertPoint:[event locationInWindow] fromView:nil]]; NSPoint point = [self xyFromEvent:event];
self->_lastContextMenuEvent = event; self->_lastContextMenuEvent = event;
SolveSpace::SS.GW.MouseRightUp(point.x, point.y); SolveSpace::SS.GW.MouseRightUp(point.x, point.y);
} }
- (void)scrollWheel:(NSEvent*)event { - (void)scrollWheel:(NSEvent*)event {
NSPoint point = [self ij_to_xy:[self convertPoint:[event locationInWindow] fromView:nil]]; NSPoint point = [self xyFromEvent:event];
SolveSpace::SS.GW.MouseScroll(point.x, point.y, (int)-[event deltaY]); SolveSpace::SS.GW.MouseScroll(point.x, point.y, (int)-[event deltaY]);
} }
@ -369,16 +369,11 @@ CONVERT(Rect)
} }
- (void)startEditing:(NSString*)text at:(NSPoint)xy withHeight:(double)fontHeight - (void)startEditing:(NSString*)text at:(NSPoint)xy withHeight:(double)fontHeight
withMinWidthInChars:(int)minWidthChars { withMinWidthInChars:(int)minWidthChars usingMonospace:(BOOL)isMonospace {
// 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 = [self convertPointFromBacking:NSMakePoint(xy.x, size.height - xy.y)];
.x = xy.x + size.width / 2,
.y = xy.y - size.height / 2
};
[[self window] makeKeyWindow]; [[self window] makeKeyWindow];
[super startEditing:text at:[self convertPointFromBacking:point] [super startEditing:text at:point withHeight:fontHeight usingMonospace:isMonospace];
withHeight:fontHeight usingMonospace:FALSE];
[self prepareEditorWithMinWidthInChars:minWidthChars]; [self prepareEditorWithMinWidthInChars:minWidthChars];
} }
@ -404,12 +399,11 @@ CONVERT(Rect)
[self stopEditing]; [self stopEditing];
} }
- (NSPoint)ij_to_xy:(NSPoint)ij { - (NSPoint)xyFromEvent:event {
// Convert to xy (vs. ij) style coordinates, NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]
// with (0, 0) at center
NSSize size = [self bounds].size; NSSize size = [self bounds].size;
return [self convertPointToBacking:(NSPoint){ return [self convertPointToBacking:(NSPoint){
.x = ij.x - size.width / 2, .y = ij.y - size.height / 2 }]; .x = point.x, .y = size.height - point.y }];
} }
@end @end
@ -492,11 +486,12 @@ bool FullScreenIsActive(void) {
} }
void ShowGraphicsEditControl(int x, int y, int fontHeight, int minWidthChars, void ShowGraphicsEditControl(int x, int y, int fontHeight, int minWidthChars,
const std::string &str) { const std::string &str, bool forDock) {
[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}
withHeight:fontHeight withHeight:(forDock ? 15 : fontHeight)
withMinWidthInChars:minWidthChars]; withMinWidthInChars:minWidthChars
usingMonospace:(forDock ? TRUE : FALSE)];
} }
void HideGraphicsEditControl(void) { void HideGraphicsEditControl(void) {
@ -1062,7 +1057,7 @@ double GetScreenDpi() {
return (displayPixelSize.width / displayPhysicalSize.width) * 25.4f; return (displayPixelSize.width / displayPhysicalSize.width) * 25.4f;
} }
void InvalidateText(void) { void InvalidateText() {
NSSize size = [TWView convertSizeToBacking:[TWView frame].size]; NSSize size = [TWView convertSizeToBacking:[TWView frame].size];
size.height = (SS.TW.top[SS.TW.rows - 1] + 1) * TextWindow::LINE_HEIGHT / 2; size.height = (SS.TW.top[SS.TW.rows - 1] + 1) * TextWindow::LINE_HEIGHT / 2;
[TWView setFrameSize:[TWView convertSizeFromBacking:size]]; [TWView setFrameSize:[TWView convertSizeFromBacking:size]];

View File

@ -298,6 +298,17 @@ public:
glXDestroyContext(_xdisplay, _glcontext); glXDestroyContext(_xdisplay, _glcontext);
} }
void set_cursor_hand(bool is_hand) {
realize();
Gdk::CursorType type = is_hand ? Gdk::HAND1 : Gdk::ARROW;
#ifdef HAVE_GTK3
get_window()->set_cursor(Gdk::Cursor::create(type));
#else
get_window()->set_cursor(Gdk::Cursor(type));
#endif
}
protected: protected:
/* Draw on a GLX framebuffer object, then read pixels out and draw them on /* Draw on a GLX framebuffer object, then read pixels out and draw them on
the Cairo context. Slower, but you get to overlay nice widgets. */ the Cairo context. Slower, but you get to overlay nice widgets. */
@ -353,6 +364,11 @@ public:
_entry.signal_activate(). _entry.signal_activate().
connect(sigc::mem_fun(this, &EditorOverlay::on_activate)); connect(sigc::mem_fun(this, &EditorOverlay::on_activate));
_entry.signal_motion_notify_event().
connect(sigc::mem_fun(this, &EditorOverlay::on_editor_motion_notify_event));
_entry.signal_button_press_event().
connect(sigc::mem_fun(this, &EditorOverlay::on_editor_button_press_event));
} }
void start_editing(int x, int y, int font_height, bool is_monospace, int minWidthChars, void start_editing(int x, int y, int font_height, bool is_monospace, int minWidthChars,
@ -451,6 +467,14 @@ protected:
_signal_editing_done(_entry.get_text()); _signal_editing_done(_entry.get_text());
} }
bool on_editor_motion_notify_event(GdkEventMotion *event) {
return _underlay.event((GdkEvent*) event);
}
bool on_editor_button_press_event(GdkEventButton *event) {
return _underlay.event((GdkEvent*) event);
}
private: private:
Gtk::Widget &_underlay; Gtk::Widget &_underlay;
Gtk::Entry _entry; Gtk::Entry _entry;
@ -495,22 +519,12 @@ public:
} }
protected: protected:
bool on_configure_event(GdkEventConfigure *event) override {
_w = event->width;
_h = event->height;
return GlWidget::on_configure_event(event);;
}
void on_gl_draw() override { void on_gl_draw() override {
SS.GW.Paint(); SS.GW.Paint();
} }
bool on_motion_notify_event(GdkEventMotion *event) override { bool on_motion_notify_event(GdkEventMotion *event) override {
int x, y; SS.GW.MouseMoved(event->x, event->y,
ij_to_xy(event->x, event->y, x, y);
SS.GW.MouseMoved(x, y,
event->state & GDK_BUTTON1_MASK, event->state & GDK_BUTTON1_MASK,
event->state & GDK_BUTTON2_MASK, event->state & GDK_BUTTON2_MASK,
event->state & GDK_BUTTON3_MASK, event->state & GDK_BUTTON3_MASK,
@ -521,20 +535,17 @@ protected:
} }
bool on_button_press_event(GdkEventButton *event) override { bool on_button_press_event(GdkEventButton *event) override {
int x, y;
ij_to_xy(event->x, event->y, x, y);
switch(event->button) { switch(event->button) {
case 1: case 1:
if(event->type == GDK_BUTTON_PRESS) if(event->type == GDK_BUTTON_PRESS)
SS.GW.MouseLeftDown(x, y); SS.GW.MouseLeftDown(event->x, event->y);
else if(event->type == GDK_2BUTTON_PRESS) else if(event->type == GDK_2BUTTON_PRESS)
SS.GW.MouseLeftDoubleClick(x, y); SS.GW.MouseLeftDoubleClick(event->x, event->y);
break; break;
case 2: case 2:
case 3: case 3:
SS.GW.MouseMiddleOrRightDown(x, y); SS.GW.MouseMiddleOrRightDown(event->x, event->y);
break; break;
} }
@ -542,16 +553,13 @@ protected:
} }
bool on_button_release_event(GdkEventButton *event) override { bool on_button_release_event(GdkEventButton *event) override {
int x, y;
ij_to_xy(event->x, event->y, x, y);
switch(event->button) { switch(event->button) {
case 1: case 1:
SS.GW.MouseLeftUp(x, y); SS.GW.MouseLeftUp(event->x, event->y);
break; break;
case 3: case 3:
SS.GW.MouseRightUp(x, y); SS.GW.MouseRightUp(event->x, event->y);
break; break;
} }
@ -559,10 +567,7 @@ protected:
} }
bool on_scroll_event(GdkEventScroll *event) override { bool on_scroll_event(GdkEventScroll *event) override {
int x, y; SS.GW.MouseScroll(event->x, event->y, (int)-DeltaYOfScrollEvent(event));
ij_to_xy(event->x, event->y, x, y);
SS.GW.MouseScroll(x, y, (int)-DeltaYOfScrollEvent(event));
return true; return true;
} }
@ -572,15 +577,6 @@ protected:
return true; return true;
} }
private:
int _w, _h;
void ij_to_xy(double i, double j, int &x, int &y) {
// Convert to xy (vs. ij) style coordinates,
// with (0, 0) at center
x = (int)i - _w / 2;
y = _h / 2 - (int)j;
}
}; };
class GraphicsWindowGtk : public Gtk::Window { class GraphicsWindowGtk : public Gtk::Window {
@ -748,16 +744,9 @@ bool FullScreenIsActive(void) {
} }
void ShowGraphicsEditControl(int x, int y, int fontHeight, int minWidthChars, void ShowGraphicsEditControl(int x, int y, int fontHeight, int minWidthChars,
const std::string &val) { const std::string &val, bool forDock) {
Gdk::Rectangle rect = GW->get_widget().get_allocation(); GW->get_overlay().start_editing(x, y, fontHeight,
/*is_monospace=*/forDock, minWidthChars, val);
// Convert to ij (vs. xy) style coordinates,
// and compensate for the input widget height due to inverse coord
int i, j;
i = x + rect.get_width() / 2;
j = -y + rect.get_height() / 2;
GW->get_overlay().start_editing(i, j, fontHeight, /*is_monospace=*/false, minWidthChars, val);
} }
void HideGraphicsEditControl(void) { void HideGraphicsEditControl(void) {
@ -1260,34 +1249,22 @@ public:
Gdk::LEAVE_NOTIFY_MASK); Gdk::LEAVE_NOTIFY_MASK);
} }
void set_cursor_hand(bool is_hand) {
Glib::RefPtr<Gdk::Window> gdkwin = get_window();
if(gdkwin) { // returns NULL if not realized
Gdk::CursorType type = is_hand ? Gdk::HAND1 : Gdk::ARROW;
#ifdef HAVE_GTK3
gdkwin->set_cursor(Gdk::Cursor::create(type));
#else
gdkwin->set_cursor(Gdk::Cursor(type));
#endif
}
}
protected: protected:
void on_gl_draw() override { void on_gl_draw() override {
SS.TW.Paint(); SS.TW.Paint();
} }
bool on_motion_notify_event(GdkEventMotion *event) override { bool on_motion_notify_event(GdkEventMotion *event) override {
SS.TW.MouseEvent(/*leftClick*/ false, SS.TW.MouseEvent(/*isClick=*/false,
/*leftDown*/ event->state & GDK_BUTTON1_MASK, /*leftDown=*/event->state & GDK_BUTTON1_MASK,
event->x, event->y); event->x, event->y);
return true; return true;
} }
bool on_button_press_event(GdkEventButton *event) override { bool on_button_press_event(GdkEventButton *event) override {
SS.TW.MouseEvent(/*leftClick*/ event->type == GDK_BUTTON_PRESS, SS.TW.MouseEvent(/*isClick=*/event->type == GDK_BUTTON_PRESS,
/*leftDown*/ event->state & GDK_BUTTON1_MASK, /*leftDown=*/event->state & GDK_BUTTON1_MASK,
event->x, event->y); event->x, event->y);
return true; return true;
@ -1333,11 +1310,6 @@ public:
_overlay.signal_editing_done(). _overlay.signal_editing_done().
connect(sigc::mem_fun(this, &TextWindowGtk::on_editing_done)); connect(sigc::mem_fun(this, &TextWindowGtk::on_editing_done));
_overlay.get_entry().signal_motion_notify_event().
connect(sigc::mem_fun(this, &TextWindowGtk::on_editor_motion_notify_event));
_overlay.get_entry().signal_button_press_event().
connect(sigc::mem_fun(this, &TextWindowGtk::on_editor_button_press_event));
} }
Gtk::VScrollbar &get_scrollbar() { Gtk::VScrollbar &get_scrollbar() {
@ -1388,14 +1360,6 @@ protected:
SS.TW.EditControlDone(value.c_str()); SS.TW.EditControlDone(value.c_str());
} }
bool on_editor_motion_notify_event(GdkEventMotion *event) {
return _widget.event((GdkEvent*) event);
}
bool on_editor_button_press_event(GdkEventButton *event) {
return _widget.event((GdkEvent*) event);
}
private: private:
Gtk::VScrollbar _scrollbar; Gtk::VScrollbar _scrollbar;
TextWidget _widget; TextWidget _widget;
@ -1423,6 +1387,11 @@ double GetScreenDpi() {
} }
void InvalidateText(void) { void InvalidateText(void) {
if(SS.GW.dockTextWindow) {
InvalidateGraphics();
return;
}
TW->get_widget().queue_draw(); TW->get_widget().queue_draw();
} }
@ -1431,7 +1400,11 @@ void MoveTextScrollbarTo(int pos, int maxPos, int page) {
} }
void SetMousePointerToHand(bool is_hand) { void SetMousePointerToHand(bool is_hand) {
TW->get_widget().set_cursor_hand(is_hand); if(SS.GW.dockTextWindow) {
GW->get_widget().set_cursor_hand(is_hand);
} else {
TW->get_widget().set_cursor_hand(is_hand);
}
} }
void ShowTextEditControl(int x, int y, const std::string &val) { void ShowTextEditControl(int x, int y, const std::string &val) {

View File

@ -908,17 +908,12 @@ bool SolveSpace::TextEditControlIsVisible()
return IsWindowVisible(TextEditControl) ? true : false; return IsWindowVisible(TextEditControl) ? true : false;
} }
void SolveSpace::ShowGraphicsEditControl(int x, int y, int fontHeight, int minWidthChars, void SolveSpace::ShowGraphicsEditControl(int x, int y, int fontHeight, int minWidthChars,
const std::string &str) const std::string &str, bool forDock)
{ {
if(GraphicsEditControlIsVisible()) return; if(GraphicsEditControlIsVisible()) return;
RECT r;
GetClientRect(GraphicsWnd, &r);
x = x + (r.right - r.left)/2;
y = (r.bottom - r.top)/2 - y;
ShowEditControl(GraphicsEditControl, x, y, fontHeight, minWidthChars, ShowEditControl(GraphicsEditControl, x, y, fontHeight, minWidthChars,
/*isMonospace=*/false, Widen(str)); /*isMonospace=*/forDock, Widen(str));
} }
void SolveSpace::HideGraphicsEditControl() void SolveSpace::HideGraphicsEditControl()
{ {
@ -971,12 +966,6 @@ LRESULT CALLBACK GraphicsWndProc(HWND hwnd, UINT msg, WPARAM wParam,
tme.hwndTrack = GraphicsWnd; tme.hwndTrack = GraphicsWnd;
TrackMouseEvent(&tme); TrackMouseEvent(&tme);
// Convert to xy (vs. ij) style coordinates, with (0, 0) at center
RECT r;
GetClientRect(GraphicsWnd, &r);
x = x - (r.right - r.left)/2;
y = (r.bottom - r.top)/2 - y;
LastMousePos.x = x; LastMousePos.x = x;
LastMousePos.y = y; LastMousePos.y = y;

View File

@ -175,7 +175,7 @@ public:
virtual void SetCamera(const Camera &camera, bool filp = FLIP_FRAMEBUFFER) = 0; virtual void SetCamera(const Camera &camera, bool filp = FLIP_FRAMEBUFFER) = 0;
virtual void SetLighting(const Lighting &lighting) = 0; virtual void SetLighting(const Lighting &lighting) = 0;
virtual void NewFrame() = 0; virtual void NewFrame(int left, int top, int width, int height) = 0;
virtual void FlushFrame() = 0; virtual void FlushFrame() = 0;
virtual std::shared_ptr<Pixmap> ReadFrame() = 0; virtual std::shared_ptr<Pixmap> ReadFrame() = 0;

View File

@ -219,7 +219,7 @@ public:
void SetCamera(const Camera &camera, bool filp = FLIP_FRAMEBUFFER) override; void SetCamera(const Camera &camera, bool filp = FLIP_FRAMEBUFFER) override;
void SetLighting(const Lighting &lighting) override; void SetLighting(const Lighting &lighting) override;
void NewFrame() override; void NewFrame(int left, int top, int width, int height) override;
void FlushFrame() override; void FlushFrame() override;
std::shared_ptr<Pixmap> ReadFrame() override; std::shared_ptr<Pixmap> ReadFrame() override;
@ -705,8 +705,6 @@ void OpenGl1Renderer::InvalidatePixmap(std::shared_ptr<const Pixmap> pm) {
void OpenGl1Renderer::UpdateProjection(bool flip) { void OpenGl1Renderer::UpdateProjection(bool flip) {
UnSelectPrimitive(); UnSelectPrimitive();
glViewport(0, 0, camera.width, camera.height);
glMatrixMode(GL_PROJECTION); glMatrixMode(GL_PROJECTION);
glLoadIdentity(); glLoadIdentity();
@ -753,7 +751,11 @@ void OpenGl1Renderer::UpdateProjection(bool flip) {
glClear(GL_DEPTH_BUFFER_BIT); glClear(GL_DEPTH_BUFFER_BIT);
} }
void OpenGl1Renderer::NewFrame() { void OpenGl1Renderer::NewFrame(int left, int top, int width, int height) {
glEnable(GL_SCISSOR_TEST);
glScissor(left, top, width, height);
glViewport(left, top, width, height);
glEnable(GL_NORMALIZE); glEnable(GL_NORMALIZE);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

View File

@ -134,7 +134,7 @@ public:
void SetCamera(const Camera &c, bool flip) override; void SetCamera(const Camera &c, bool flip) override;
void SetLighting(const Lighting &l) override; void SetLighting(const Lighting &l) override;
void NewFrame() override; void NewFrame(int left, int top, int width, int height) override;
void FlushFrame() override; void FlushFrame() override;
std::shared_ptr<Pixmap> ReadFrame() override; std::shared_ptr<Pixmap> ReadFrame() override;
@ -542,8 +542,6 @@ void OpenGl2Renderer::DrawPixmap(std::shared_ptr<const Pixmap> pm,
} }
void OpenGl2Renderer::UpdateProjection(bool flip) { void OpenGl2Renderer::UpdateProjection(bool flip) {
glViewport(0, 0, camera.width, camera.height);
double mat1[16]; double mat1[16];
double mat2[16]; double mat2[16];
@ -611,12 +609,16 @@ void OpenGl2Renderer::UpdateProjection(bool flip) {
glClear(GL_DEPTH_BUFFER_BIT); glClear(GL_DEPTH_BUFFER_BIT);
} }
void OpenGl2Renderer::NewFrame() { void OpenGl2Renderer::NewFrame(int left, int top, int width, int height) {
if(!initialized) { if(!initialized) {
Init(); Init();
initialized = true; initialized = true;
} }
glEnable(GL_SCISSOR_TEST);
glScissor(left, top, width, height);
glViewport(left, top, width, height);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND); glEnable(GL_BLEND);

View File

@ -102,6 +102,9 @@ void SolveSpaceUI::Init() {
RefreshRecentMenus(); RefreshRecentMenus();
// Autosave timer // Autosave timer
autosaveInterval = CnfThawInt(5, "AutosaveInterval"); autosaveInterval = CnfThawInt(5, "AutosaveInterval");
// Single-window UI
GW.dockTextWindow = CnfThawBool(false, "DockTextWindow");
GW.textDockWidth = CnfThawInt(420, "TextDockWidth");
// The default styles (colors, line widths, etc.) are also stored in the // The default styles (colors, line widths, etc.) are also stored in the
// configuration file, but we will automatically load those as we need // configuration file, but we will automatically load those as we need
@ -217,6 +220,9 @@ void SolveSpaceUI::Exit() {
CnfFreezeBool(showToolbar, "ShowToolbar"); CnfFreezeBool(showToolbar, "ShowToolbar");
// Autosave timer // Autosave timer
CnfFreezeInt(autosaveInterval, "AutosaveInterval"); CnfFreezeInt(autosaveInterval, "AutosaveInterval");
// Single-window UI
CnfFreezeBool(GW.dockTextWindow, "DockTextWindow");
CnfFreezeInt(GW.textDockWidth, "TextDockWidth");
// And the default styles, colors and line widths and such. // And the default styles, colors and line widths and such.
Style::FreezeDefaultStyles(); Style::FreezeDefaultStyles();

View File

@ -244,7 +244,7 @@ void RadioMenuByCmd(Command id, bool selected);
void EnableMenuByCmd(Command id, bool enabled); void EnableMenuByCmd(Command id, bool enabled);
void ShowGraphicsEditControl(int x, int y, int fontHeight, int minWidthChars, void ShowGraphicsEditControl(int x, int y, int fontHeight, int minWidthChars,
const std::string &str); const std::string &str, bool forDock);
void HideGraphicsEditControl(); void HideGraphicsEditControl();
bool GraphicsEditControlIsVisible(); bool GraphicsEditControlIsVisible();
void ShowTextEditControl(int x, int y, const std::string &str); void ShowTextEditControl(int x, int y, const std::string &str);
@ -374,8 +374,10 @@ std::string Basename(std::string filename, bool stripExtension = false);
std::string Dirname(std::string filename); std::string Dirname(std::string filename);
bool ReadFile(const std::string &filename, std::string *data); bool ReadFile(const std::string &filename, std::string *data);
bool WriteFile(const std::string &filename, const std::string &data); bool WriteFile(const std::string &filename, const std::string &data);
void Message(const char *str, ...); void Message(const char *str, ...);
void Error(const char *str, ...); void Error(const char *str, ...);
void CnfFreezeBool(bool v, const std::string &name); void CnfFreezeBool(bool v, const std::string &name);
void CnfFreezeColor(RgbaColor v, const std::string &name); void CnfFreezeColor(RgbaColor v, const std::string &name);
bool CnfThawBool(bool v, const std::string &name); bool CnfThawBool(bool v, const std::string &name);
@ -749,6 +751,7 @@ public:
Unit viewUnits; Unit viewUnits;
int afterDecimalMm; int afterDecimalMm;
int afterDecimalInch; int afterDecimalInch;
int autosaveInterval; // in minutes int autosaveInterval; // in minutes
std::string MmToString(double v); std::string MmToString(double v);

View File

@ -251,7 +251,11 @@ void TextWindow::ClearSuper() {
void TextWindow::HideEditControl() { void TextWindow::HideEditControl() {
editControl.colorPicker.show = false; editControl.colorPicker.show = false;
HideTextEditControl(); if(editControl.inDock) {
HideGraphicsEditControl();
} else {
HideTextEditControl();
}
} }
void TextWindow::ShowEditControl(int col, const std::string &str, int halfRow) { void TextWindow::ShowEditControl(int col, const std::string &str, int halfRow) {
@ -262,7 +266,13 @@ 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, y + 18, str); if(SS.GW.dockTextWindow) {
editControl.inDock = true;
ShowGraphicsEditControl(SS.GW.width + x, y + 18, TextWindow::CHAR_HEIGHT, 30, str,
/*forDock=*/true);
} else {
ShowTextEditControl(x, y + 18, str);
}
} }
void TextWindow::ShowEditControlWithColorPicker(int col, RgbaColor rgb) void TextWindow::ShowEditControlWithColorPicker(int col, RgbaColor rgb)
@ -510,20 +520,33 @@ void TextWindow::Show() {
} }
} }
InvalidateText(); Invalidate();
}
void TextWindow::Invalidate() {
if(SS.GW.dockTextWindow && SS.GW.showTextWindow) {
InvalidateGraphics();
} else {
InvalidateText();
}
} }
void TextWindow::TimerCallback() void TextWindow::TimerCallback()
{ {
tooltippedButton = hoveredButton; tooltippedButton = hoveredButton;
InvalidateText(); Invalidate();
} }
void TextWindow::DrawOrHitTestIcons(UiCanvas *uiCanvas, TextWindow::DrawOrHitHow how, void TextWindow::DrawOrHitTestIcons(UiCanvas *uiCanvas, TextWindow::DrawOrHitHow how,
double mx, double my) double mx, double my)
{ {
int width, height; int width, height;
GetTextWindowSize(&width, &height); if(SS.GW.dockTextWindow) {
GetGraphicsWindowSize(&width, &height);
width = SS.GW.textDockWidth;
} else {
GetTextWindowSize(&width, &height);
}
int x = 20, y = 33 + LINE_HEIGHT; int x = 20, y = 33 + LINE_HEIGHT;
y -= scrollPos*(LINE_HEIGHT/2); y -= scrollPos*(LINE_HEIGHT/2);
@ -562,7 +585,7 @@ void TextWindow::DrawOrHitTestIcons(UiCanvas *uiCanvas, TextWindow::DrawOrHitHow
} }
if(how != PAINT && hoveredButton != oldHovered) { if(how != PAINT && hoveredButton != oldHovered) {
InvalidateText(); Invalidate();
} }
if(tooltippedButton && !tooltippedButton->Tooltip().empty()) { if(tooltippedButton && !tooltippedButton->Tooltip().empty()) {
@ -673,7 +696,7 @@ bool TextWindow::DrawOrHitTestColorPicker(UiCanvas *uiCanvas, DrawOrHitHow how,
} }
if(!editControl.colorPicker.show) return false; if(!editControl.colorPicker.show) return false;
if(how == CLICK || (how == HOVER && leftDown)) InvalidateText(); if(how == CLICK || (how == HOVER && leftDown)) Invalidate();
static const RgbaColor BaseColor[12] = { static const RgbaColor BaseColor[12] = {
RGBi(255, 0, 0), RGBi(255, 0, 0),
@ -693,7 +716,13 @@ bool TextWindow::DrawOrHitTestColorPicker(UiCanvas *uiCanvas, DrawOrHitHow how,
}; };
int width, height; int width, height;
GetTextWindowSize(&width, &height); if(SS.GW.dockTextWindow) {
GetGraphicsWindowSize(&width, &height);
width = SS.GW.textDockWidth;
} else {
GetTextWindowSize(&width, &height);
}
dbp("%d %d %d %d", width, height, (int)x, (int)y);
int px = LEFT_MARGIN + CHAR_WIDTH*editControl.col; int px = LEFT_MARGIN + CHAR_WIDTH*editControl.col;
int py = (editControl.halfRow - SS.TW.scrollPos)*(LINE_HEIGHT/2); int py = (editControl.halfRow - SS.TW.scrollPos)*(LINE_HEIGHT/2);
@ -858,6 +887,11 @@ void TextWindow::Paint() {
int width, height; int width, height;
GetTextWindowSize(&width, &height); GetTextWindowSize(&width, &height);
Paint(canvas, 0, 0, width, height);
}
void TextWindow::Paint(std::shared_ptr<ViewportCanvas> canvas,
int viewportLeft, int viewportTop, int width, int height) {
Camera camera = {}; Camera camera = {};
camera.width = width; camera.width = width;
camera.height = height; camera.height = height;
@ -865,7 +899,7 @@ void TextWindow::Paint() {
camera.offset.x = -(double)camera.width / 2.0; camera.offset.x = -(double)camera.width / 2.0;
camera.offset.y = -(double)camera.height / 2.0; camera.offset.y = -(double)camera.height / 2.0;
canvas->NewFrame(); canvas->NewFrame(viewportLeft, viewportTop, width, height);
canvas->SetCamera(camera); canvas->SetCamera(camera);
UiCanvas uiCanvas = {}; UiCanvas uiCanvas = {};
@ -1062,7 +1096,7 @@ void TextWindow::MouseEvent(bool leftClick, bool leftDown, double x, double y) {
prevHoveredCol != hoveredCol) prevHoveredCol != hoveredCol)
{ {
InvalidateGraphics(); InvalidateGraphics();
InvalidateText(); Invalidate();
} }
} }
@ -1071,7 +1105,7 @@ void TextWindow::MouseLeave() {
hoveredButton = NULL; hoveredButton = NULL;
hoveredRow = 0; hoveredRow = 0;
hoveredCol = 0; hoveredCol = 0;
InvalidateText(); Invalidate();
} }
void TextWindow::ScrollbarEvent(int newPos) { void TextWindow::ScrollbarEvent(int newPos) {
@ -1085,7 +1119,7 @@ void TextWindow::ScrollbarEvent(int newPos) {
if(newPos != scrollPos) { if(newPos != scrollPos) {
scrollPos = newPos; scrollPos = newPos;
MoveTextScrollbarTo(scrollPos, top[rows - 1] + 1, halfRows); MoveTextScrollbarTo(scrollPos, top[rows - 1] + 1, halfRows);
InvalidateText(); Invalidate();
} }
} }

View File

@ -203,6 +203,7 @@ public:
// These are called by the platform-specific code. // These are called by the platform-specific code.
void Paint(); void Paint();
void Paint(std::shared_ptr<ViewportCanvas> canvas, int left, int top, int width, int height);
void MouseEvent(bool isClick, bool leftDown, double x, double y); void MouseEvent(bool isClick, bool leftDown, double x, double y);
void MouseScroll(double x, double y, int delta); void MouseScroll(double x, double y, int delta);
void MouseLeave(); void MouseLeave();
@ -232,6 +233,7 @@ public:
void ClearScreen(); void ClearScreen();
void Show(); void Show();
void Invalidate();
// State for the screen that we are showing in the text window. // State for the screen that we are showing in the text window.
enum class Screen : uint32_t { enum class Screen : uint32_t {
@ -341,6 +343,8 @@ public:
bool picker1dActive; bool picker1dActive;
bool picker2dActive; bool picker2dActive;
} colorPicker; } colorPicker;
bool inDock;
} editControl; } editControl;
void HideEditControl(); void HideEditControl();
@ -445,6 +449,7 @@ public:
static void ScreenChangeExportOffset(int link, uint32_t v); static void ScreenChangeExportOffset(int link, uint32_t v);
static void ScreenChangeGCodeParameter(int link, uint32_t v); static void ScreenChangeGCodeParameter(int link, uint32_t v);
static void ScreenChangeAutosaveInterval(int link, uint32_t v); static void ScreenChangeAutosaveInterval(int link, uint32_t v);
static void ScreenChangeTextPaneEnabled(int link, uint32_t v);
static void ScreenChangeStyleName(int link, uint32_t v); static void ScreenChangeStyleName(int link, uint32_t v);
static void ScreenChangeStyleMetric(int link, uint32_t v); static void ScreenChangeStyleMetric(int link, uint32_t v);
static void ScreenChangeStyleTextAngle(int link, uint32_t v); static void ScreenChangeStyleTextAngle(int link, uint32_t v);
@ -715,12 +720,16 @@ public:
Command toolbarTooltipped; Command toolbarTooltipped;
int toolbarMouseX, toolbarMouseY; int toolbarMouseX, toolbarMouseY;
// Text window interaction
bool showTextWindow;
bool dockTextWindow;
double textDockWidth;
// This sets what gets displayed. // This sets what gets displayed.
bool showWorkplanes; bool showWorkplanes;
bool showNormals; bool showNormals;
bool showPoints; bool showPoints;
bool showConstraints; bool showConstraints;
bool showTextWindow;
bool showShaded; bool showShaded;
bool showEdges; bool showEdges;
bool showOutlines; bool showOutlines;
@ -746,8 +755,9 @@ public:
// These are called by the platform-specific code. // These are called by the platform-specific code.
void Paint(); void Paint();
bool ConvertMouseCoords(double *x, double *y);
void MouseMoved(double x, double y, bool leftDown, bool middleDown, void MouseMoved(double x, double y, bool leftDown, bool middleDown,
bool rightDown, bool shiftDown, bool ctrlDown); bool rightDown, bool shiftDown, bool ctrlDown);
void MouseLeftDown(double x, double y); void MouseLeftDown(double x, double y);
void MouseLeftUp(double x, double y); void MouseLeftUp(double x, double y);
void MouseLeftDoubleClick(double x, double y); void MouseLeftDoubleClick(double x, double y);