From b7a605c44cbc3bbe5d396c88ebe9c8cdf3161f90 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sat, 19 Nov 2016 11:27:21 +0000 Subject: [PATCH] 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. --- src/confscreen.cpp | 10 ++++ src/draw.cpp | 22 ++++--- src/graphicswin.cpp | 6 +- src/mouse.cpp | 59 ++++++++++++++++-- src/platform/cocoamain.mm | 39 ++++++------ src/platform/gtkmain.cpp | 123 +++++++++++++++----------------------- src/platform/w32main.cpp | 15 +---- src/render/render.h | 2 +- src/render/rendergl1.cpp | 10 ++-- src/render/rendergl2.cpp | 10 ++-- src/solvespace.cpp | 6 ++ src/solvespace.h | 5 +- src/textwin.cpp | 58 ++++++++++++++---- src/ui.h | 14 ++++- 14 files changed, 228 insertions(+), 151 deletions(-) diff --git a/src/confscreen.cpp b/src/confscreen.cpp index 8fca9c0..a41f85e 100644 --- a/src/confscreen.cpp +++ b/src/confscreen.cpp @@ -161,6 +161,11 @@ void TextWindow::ScreenChangeAutosaveInterval(int link, uint32_t v) { 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() { int i; Printf(true, "%Ft user color (r, g, b)"); @@ -303,6 +308,11 @@ void TextWindow::ShowConfiguration() { Printf(false, "%Ba %d %Fl%Ll%f[change]%E", 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) { const char *gl_vendor, *gl_renderer, *gl_version; canvas->GetIdent(&gl_vendor, &gl_renderer, &gl_version); diff --git a/src/draw.cpp b/src/draw.cpp index 548df02..81c9f16 100644 --- a/src/draw.cpp +++ b/src/draw.cpp @@ -751,14 +751,7 @@ void GraphicsWindow::Paint() { havePainted = true; - int w, h; - GetGraphicsWindowSize(&w, &h); - width = w; - height = h; - - Camera camera = GetCamera(); Lighting lighting = GetLighting(); - if(!SS.ActiveGroupsOkay()) { // Draw a different background whenever we're having solve problems. RgbaColor bgColor = Style::Color(Style::DRAW_ERROR); @@ -770,9 +763,22 @@ void GraphicsWindow::Paint() { 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(); - canvas->NewFrame(); + canvas->NewFrame(0, 0, width, height); canvas->SetCamera(camera); canvas->SetLighting(lighting); Draw(canvas.get()); diff --git a/src/graphicswin.cpp b/src/graphicswin.cpp index 2dc1a2f..a956a40 100644 --- a/src/graphicswin.cpp +++ b/src/graphicswin.cpp @@ -235,7 +235,7 @@ void GraphicsWindow::Init() { drawOccludedAs = DrawOccludedAs::INVISIBLE; showTextWindow = true; - ShowTextWindow(showTextWindow); + ShowTextWindow(showTextWindow && !dockTextWindow); showSnapGrid = false; context.active = false; @@ -687,7 +687,7 @@ void GraphicsWindow::EnsureValidActives() { RadioMenuByCmd(Command::UNITS_MM, SS.viewUnits == Unit::MM); RadioMenuByCmd(Command::UNITS_INCHES, SS.viewUnits == Unit::INCHES); - ShowTextWindow(SS.GW.showTextWindow); + ShowTextWindow(showTextWindow && !dockTextWindow); CheckMenuByCmd(Command::SHOW_TEXT_WND, /*checked=*/SS.GW.showTextWindow); #if defined(__APPLE__) @@ -722,7 +722,7 @@ void GraphicsWindow::ForceTextWindowShown() { if(!showTextWindow) { showTextWindow = true; CheckMenuByCmd(Command::SHOW_TEXT_WND, /*checked=*/true); - ShowTextWindow(true); + ShowTextWindow(!dockTextWindow); } } diff --git a/src/mouse.cpp b/src/mouse.cpp index 7b0ff4b..35a431d 100644 --- a/src/mouse.cpp +++ b/src/mouse.cpp @@ -75,9 +75,27 @@ void GraphicsWindow::StartDraggingBySelection() { 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, - 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(context.active) return; @@ -470,6 +488,11 @@ void GraphicsWindow::ClearPending() { } void GraphicsWindow::MouseMiddleOrRightDown(double x, double y) { + if(ConvertMouseCoords(&x, &y)) { + SS.TW.MouseEvent(/*isClick=*/true, /*leftDown=*/false, x, y); + return; + } + if(GraphicsEditControlIsVisible()) return; orig.offset = offset; @@ -498,6 +521,8 @@ void GraphicsWindow::ContextMenuListStyles() { } void GraphicsWindow::MouseRightUp(double x, double y) { + if(ConvertMouseCoords(&x, &y)) return; + SS.extraLine.draw = false; InvalidateGraphics(); @@ -893,7 +918,14 @@ bool GraphicsWindow::ConstrainPointByHovered(hEntity pt) { 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; if(GraphicsEditControlIsVisible()) { @@ -912,7 +944,7 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { bool hasConstraintSuggestion = SS.GW.pending.hasSuggestion; // 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); orig.mouse.x = mx; orig.mouse.y = my; @@ -1232,6 +1264,8 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { } void GraphicsWindow::MouseLeftUp(double mx, double my) { + if(ConvertMouseCoords(&mx, &my)) return; + orig.mouseDown = false; hoverWasSelectedOnMousedown = false; @@ -1272,6 +1306,8 @@ void GraphicsWindow::MouseLeftUp(double mx, double my) { } void GraphicsWindow::MouseLeftDoubleClick(double mx, double my) { + if(ConvertMouseCoords(&mx, &my)) return; + if(GraphicsEditControlIsVisible()) return; SS.TW.HideEditControl(); @@ -1335,13 +1371,19 @@ void GraphicsWindow::MouseLeftDoubleClick(double mx, double my) { } hStyle hs = c->disp.style; 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))), - editMinWidthChar, editValue); + editMinWidthChar, editValue, /*forDock=*/false); } } void GraphicsWindow::EditControlDone(const char *s) { + if(SS.TW.editControl.inDock) { + SS.TW.EditControlDone(s); + return; + } + HideGraphicsEditControl(); Constraint *c = SK.GetConstraint(constraintBeingEdited); @@ -1416,6 +1458,11 @@ bool GraphicsWindow::KeyDown(int c) { } 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 offsetUp = offset.Dot(projUp); diff --git a/src/platform/cocoamain.mm b/src/platform/cocoamain.mm index 9b87514..2de41d7 100644 --- a/src/platform/cocoamain.mm +++ b/src/platform/cocoamain.mm @@ -287,7 +287,7 @@ CONVERT(Rect) } - (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 buttons = [NSEvent pressedMouseButtons]; SolveSpace::SS.GW.MouseMoved(point.x, point.y, @@ -311,7 +311,7 @@ CONVERT(Rect) } - (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) SolveSpace::SS.GW.MouseLeftDown(point.x, point.y); else if([event clickCount] == 2) @@ -319,7 +319,7 @@ CONVERT(Rect) } - (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); } @@ -328,18 +328,18 @@ CONVERT(Rect) } - (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); } - (void)rightMouseUp:(NSEvent*)event { - NSPoint point = [self ij_to_xy:[self convertPoint:[event locationInWindow] fromView:nil]]; + NSPoint point = [self xyFromEvent:event]; self->_lastContextMenuEvent = event; SolveSpace::SS.GW.MouseRightUp(point.x, point.y); } - (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]); } @@ -369,16 +369,11 @@ CONVERT(Rect) } - (void)startEditing:(NSString*)text at:(NSPoint)xy withHeight:(double)fontHeight - withMinWidthInChars:(int)minWidthChars { - // Convert to ij (vs. xy) style coordinates + withMinWidthInChars:(int)minWidthChars usingMonospace:(BOOL)isMonospace { NSSize size = [self convertSizeToBacking:[self bounds].size]; - NSPoint point = { - .x = xy.x + size.width / 2, - .y = xy.y - size.height / 2 - }; + NSPoint point = [self convertPointFromBacking:NSMakePoint(xy.x, size.height - xy.y)]; [[self window] makeKeyWindow]; - [super startEditing:text at:[self convertPointFromBacking:point] - withHeight:fontHeight usingMonospace:FALSE]; + [super startEditing:text at:point withHeight:fontHeight usingMonospace:isMonospace]; [self prepareEditorWithMinWidthInChars:minWidthChars]; } @@ -404,12 +399,11 @@ CONVERT(Rect) [self stopEditing]; } -- (NSPoint)ij_to_xy:(NSPoint)ij { - // Convert to xy (vs. ij) style coordinates, - // with (0, 0) at center +- (NSPoint)xyFromEvent:event { + NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil] NSSize size = [self bounds].size; 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 @@ -492,11 +486,12 @@ bool FullScreenIsActive(void) { } 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()] at:(NSPoint){(CGFloat)x, (CGFloat)y} - withHeight:fontHeight - withMinWidthInChars:minWidthChars]; + withHeight:(forDock ? 15 : fontHeight) + withMinWidthInChars:minWidthChars + usingMonospace:(forDock ? TRUE : FALSE)]; } void HideGraphicsEditControl(void) { @@ -1062,7 +1057,7 @@ double GetScreenDpi() { return (displayPixelSize.width / displayPhysicalSize.width) * 25.4f; } -void InvalidateText(void) { +void InvalidateText() { NSSize size = [TWView convertSizeToBacking:[TWView frame].size]; size.height = (SS.TW.top[SS.TW.rows - 1] + 1) * TextWindow::LINE_HEIGHT / 2; [TWView setFrameSize:[TWView convertSizeFromBacking:size]]; diff --git a/src/platform/gtkmain.cpp b/src/platform/gtkmain.cpp index 60e210a..5021814 100644 --- a/src/platform/gtkmain.cpp +++ b/src/platform/gtkmain.cpp @@ -298,6 +298,17 @@ public: 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: /* 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. */ @@ -353,6 +364,11 @@ public: _entry.signal_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, @@ -451,6 +467,14 @@ protected: _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: Gtk::Widget &_underlay; Gtk::Entry _entry; @@ -495,22 +519,12 @@ public: } 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 { SS.GW.Paint(); } bool on_motion_notify_event(GdkEventMotion *event) override { - int x, y; - ij_to_xy(event->x, event->y, x, y); - - SS.GW.MouseMoved(x, y, + SS.GW.MouseMoved(event->x, event->y, event->state & GDK_BUTTON1_MASK, event->state & GDK_BUTTON2_MASK, event->state & GDK_BUTTON3_MASK, @@ -521,20 +535,17 @@ protected: } bool on_button_press_event(GdkEventButton *event) override { - int x, y; - ij_to_xy(event->x, event->y, x, y); - switch(event->button) { case 1: 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) - SS.GW.MouseLeftDoubleClick(x, y); + SS.GW.MouseLeftDoubleClick(event->x, event->y); break; case 2: case 3: - SS.GW.MouseMiddleOrRightDown(x, y); + SS.GW.MouseMiddleOrRightDown(event->x, event->y); break; } @@ -542,16 +553,13 @@ protected: } bool on_button_release_event(GdkEventButton *event) override { - int x, y; - ij_to_xy(event->x, event->y, x, y); - switch(event->button) { case 1: - SS.GW.MouseLeftUp(x, y); + SS.GW.MouseLeftUp(event->x, event->y); break; case 3: - SS.GW.MouseRightUp(x, y); + SS.GW.MouseRightUp(event->x, event->y); break; } @@ -559,10 +567,7 @@ protected: } bool on_scroll_event(GdkEventScroll *event) override { - int x, y; - ij_to_xy(event->x, event->y, x, y); - - SS.GW.MouseScroll(x, y, (int)-DeltaYOfScrollEvent(event)); + SS.GW.MouseScroll(event->x, event->y, (int)-DeltaYOfScrollEvent(event)); return true; } @@ -572,15 +577,6 @@ protected: 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 { @@ -748,16 +744,9 @@ bool FullScreenIsActive(void) { } 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, - // 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); + const std::string &val, bool forDock) { + GW->get_overlay().start_editing(x, y, fontHeight, + /*is_monospace=*/forDock, minWidthChars, val); } void HideGraphicsEditControl(void) { @@ -1260,34 +1249,22 @@ public: Gdk::LEAVE_NOTIFY_MASK); } - void set_cursor_hand(bool is_hand) { - Glib::RefPtr 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: void on_gl_draw() override { SS.TW.Paint(); } bool on_motion_notify_event(GdkEventMotion *event) override { - SS.TW.MouseEvent(/*leftClick*/ false, - /*leftDown*/ event->state & GDK_BUTTON1_MASK, + SS.TW.MouseEvent(/*isClick=*/false, + /*leftDown=*/event->state & GDK_BUTTON1_MASK, event->x, event->y); return true; } bool on_button_press_event(GdkEventButton *event) override { - SS.TW.MouseEvent(/*leftClick*/ event->type == GDK_BUTTON_PRESS, - /*leftDown*/ event->state & GDK_BUTTON1_MASK, + SS.TW.MouseEvent(/*isClick=*/event->type == GDK_BUTTON_PRESS, + /*leftDown=*/event->state & GDK_BUTTON1_MASK, event->x, event->y); return true; @@ -1333,11 +1310,6 @@ public: _overlay.signal_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() { @@ -1388,14 +1360,6 @@ protected: 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: Gtk::VScrollbar _scrollbar; TextWidget _widget; @@ -1423,6 +1387,11 @@ double GetScreenDpi() { } void InvalidateText(void) { + if(SS.GW.dockTextWindow) { + InvalidateGraphics(); + return; + } + TW->get_widget().queue_draw(); } @@ -1431,7 +1400,11 @@ void MoveTextScrollbarTo(int pos, int maxPos, int page) { } 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) { diff --git a/src/platform/w32main.cpp b/src/platform/w32main.cpp index d0837be..0c4d21f 100644 --- a/src/platform/w32main.cpp +++ b/src/platform/w32main.cpp @@ -908,17 +908,12 @@ bool SolveSpace::TextEditControlIsVisible() return IsWindowVisible(TextEditControl) ? true : false; } void SolveSpace::ShowGraphicsEditControl(int x, int y, int fontHeight, int minWidthChars, - const std::string &str) + const std::string &str, bool forDock) { 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, - /*isMonospace=*/false, Widen(str)); + /*isMonospace=*/forDock, Widen(str)); } void SolveSpace::HideGraphicsEditControl() { @@ -971,12 +966,6 @@ LRESULT CALLBACK GraphicsWndProc(HWND hwnd, UINT msg, WPARAM wParam, tme.hwndTrack = GraphicsWnd; 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.y = y; diff --git a/src/render/render.h b/src/render/render.h index 1717f83..00d5ba0 100644 --- a/src/render/render.h +++ b/src/render/render.h @@ -175,7 +175,7 @@ public: virtual void SetCamera(const Camera &camera, bool filp = FLIP_FRAMEBUFFER) = 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 std::shared_ptr ReadFrame() = 0; diff --git a/src/render/rendergl1.cpp b/src/render/rendergl1.cpp index f977daf..54242b3 100644 --- a/src/render/rendergl1.cpp +++ b/src/render/rendergl1.cpp @@ -219,7 +219,7 @@ public: void SetCamera(const Camera &camera, bool filp = FLIP_FRAMEBUFFER) override; void SetLighting(const Lighting &lighting) override; - void NewFrame() override; + void NewFrame(int left, int top, int width, int height) override; void FlushFrame() override; std::shared_ptr ReadFrame() override; @@ -705,8 +705,6 @@ void OpenGl1Renderer::InvalidatePixmap(std::shared_ptr pm) { void OpenGl1Renderer::UpdateProjection(bool flip) { UnSelectPrimitive(); - glViewport(0, 0, camera.width, camera.height); - glMatrixMode(GL_PROJECTION); glLoadIdentity(); @@ -753,7 +751,11 @@ void OpenGl1Renderer::UpdateProjection(bool flip) { 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); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); diff --git a/src/render/rendergl2.cpp b/src/render/rendergl2.cpp index eae9a96..ab72acd 100644 --- a/src/render/rendergl2.cpp +++ b/src/render/rendergl2.cpp @@ -134,7 +134,7 @@ public: void SetCamera(const Camera &c, bool flip) override; void SetLighting(const Lighting &l) override; - void NewFrame() override; + void NewFrame(int left, int top, int width, int height) override; void FlushFrame() override; std::shared_ptr ReadFrame() override; @@ -542,8 +542,6 @@ void OpenGl2Renderer::DrawPixmap(std::shared_ptr pm, } void OpenGl2Renderer::UpdateProjection(bool flip) { - glViewport(0, 0, camera.width, camera.height); - double mat1[16]; double mat2[16]; @@ -611,12 +609,16 @@ void OpenGl2Renderer::UpdateProjection(bool flip) { glClear(GL_DEPTH_BUFFER_BIT); } -void OpenGl2Renderer::NewFrame() { +void OpenGl2Renderer::NewFrame(int left, int top, int width, int height) { if(!initialized) { Init(); 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); glEnable(GL_BLEND); diff --git a/src/solvespace.cpp b/src/solvespace.cpp index 50efb5f..52b20ab 100644 --- a/src/solvespace.cpp +++ b/src/solvespace.cpp @@ -102,6 +102,9 @@ void SolveSpaceUI::Init() { RefreshRecentMenus(); // Autosave timer 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 // configuration file, but we will automatically load those as we need @@ -217,6 +220,9 @@ void SolveSpaceUI::Exit() { CnfFreezeBool(showToolbar, "ShowToolbar"); // Autosave timer CnfFreezeInt(autosaveInterval, "AutosaveInterval"); + // Single-window UI + CnfFreezeBool(GW.dockTextWindow, "DockTextWindow"); + CnfFreezeInt(GW.textDockWidth, "TextDockWidth"); // And the default styles, colors and line widths and such. Style::FreezeDefaultStyles(); diff --git a/src/solvespace.h b/src/solvespace.h index faa9bd8..9b9f17a 100644 --- a/src/solvespace.h +++ b/src/solvespace.h @@ -244,7 +244,7 @@ void RadioMenuByCmd(Command id, bool selected); void EnableMenuByCmd(Command id, bool enabled); void ShowGraphicsEditControl(int x, int y, int fontHeight, int minWidthChars, - const std::string &str); + const std::string &str, bool forDock); void HideGraphicsEditControl(); bool GraphicsEditControlIsVisible(); 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); bool ReadFile(const std::string &filename, std::string *data); bool WriteFile(const std::string &filename, const std::string &data); + void Message(const char *str, ...); void Error(const char *str, ...); + void CnfFreezeBool(bool v, const std::string &name); void CnfFreezeColor(RgbaColor v, const std::string &name); bool CnfThawBool(bool v, const std::string &name); @@ -749,6 +751,7 @@ public: Unit viewUnits; int afterDecimalMm; int afterDecimalInch; + int autosaveInterval; // in minutes std::string MmToString(double v); diff --git a/src/textwin.cpp b/src/textwin.cpp index 78fafaf..5f15ce3 100644 --- a/src/textwin.cpp +++ b/src/textwin.cpp @@ -251,7 +251,11 @@ void TextWindow::ClearSuper() { void TextWindow::HideEditControl() { editControl.colorPicker.show = false; - HideTextEditControl(); + if(editControl.inDock) { + HideGraphicsEditControl(); + } else { + HideTextEditControl(); + } } 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 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) @@ -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() { tooltippedButton = hoveredButton; - InvalidateText(); + Invalidate(); } void TextWindow::DrawOrHitTestIcons(UiCanvas *uiCanvas, TextWindow::DrawOrHitHow how, double mx, double my) { 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; y -= scrollPos*(LINE_HEIGHT/2); @@ -562,7 +585,7 @@ void TextWindow::DrawOrHitTestIcons(UiCanvas *uiCanvas, TextWindow::DrawOrHitHow } if(how != PAINT && hoveredButton != oldHovered) { - InvalidateText(); + Invalidate(); } if(tooltippedButton && !tooltippedButton->Tooltip().empty()) { @@ -673,7 +696,7 @@ bool TextWindow::DrawOrHitTestColorPicker(UiCanvas *uiCanvas, DrawOrHitHow how, } 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] = { RGBi(255, 0, 0), @@ -693,7 +716,13 @@ bool TextWindow::DrawOrHitTestColorPicker(UiCanvas *uiCanvas, DrawOrHitHow how, }; 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 py = (editControl.halfRow - SS.TW.scrollPos)*(LINE_HEIGHT/2); @@ -858,6 +887,11 @@ void TextWindow::Paint() { int width, height; GetTextWindowSize(&width, &height); + Paint(canvas, 0, 0, width, height); +} + +void TextWindow::Paint(std::shared_ptr canvas, + int viewportLeft, int viewportTop, int width, int height) { Camera camera = {}; camera.width = width; camera.height = height; @@ -865,7 +899,7 @@ void TextWindow::Paint() { camera.offset.x = -(double)camera.width / 2.0; camera.offset.y = -(double)camera.height / 2.0; - canvas->NewFrame(); + canvas->NewFrame(viewportLeft, viewportTop, width, height); canvas->SetCamera(camera); UiCanvas uiCanvas = {}; @@ -1062,7 +1096,7 @@ void TextWindow::MouseEvent(bool leftClick, bool leftDown, double x, double y) { prevHoveredCol != hoveredCol) { InvalidateGraphics(); - InvalidateText(); + Invalidate(); } } @@ -1071,7 +1105,7 @@ void TextWindow::MouseLeave() { hoveredButton = NULL; hoveredRow = 0; hoveredCol = 0; - InvalidateText(); + Invalidate(); } void TextWindow::ScrollbarEvent(int newPos) { @@ -1085,7 +1119,7 @@ void TextWindow::ScrollbarEvent(int newPos) { if(newPos != scrollPos) { scrollPos = newPos; MoveTextScrollbarTo(scrollPos, top[rows - 1] + 1, halfRows); - InvalidateText(); + Invalidate(); } } diff --git a/src/ui.h b/src/ui.h index 2ad10b5..75a8588 100644 --- a/src/ui.h +++ b/src/ui.h @@ -203,6 +203,7 @@ public: // These are called by the platform-specific code. void Paint(); + void Paint(std::shared_ptr canvas, int left, int top, int width, int height); void MouseEvent(bool isClick, bool leftDown, double x, double y); void MouseScroll(double x, double y, int delta); void MouseLeave(); @@ -232,6 +233,7 @@ public: void ClearScreen(); void Show(); + void Invalidate(); // State for the screen that we are showing in the text window. enum class Screen : uint32_t { @@ -341,6 +343,8 @@ public: bool picker1dActive; bool picker2dActive; } colorPicker; + + bool inDock; } editControl; void HideEditControl(); @@ -445,6 +449,7 @@ public: static void ScreenChangeExportOffset(int link, uint32_t v); static void ScreenChangeGCodeParameter(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 ScreenChangeStyleMetric(int link, uint32_t v); static void ScreenChangeStyleTextAngle(int link, uint32_t v); @@ -715,12 +720,16 @@ public: Command toolbarTooltipped; int toolbarMouseX, toolbarMouseY; + // Text window interaction + bool showTextWindow; + bool dockTextWindow; + double textDockWidth; + // This sets what gets displayed. bool showWorkplanes; bool showNormals; bool showPoints; bool showConstraints; - bool showTextWindow; bool showShaded; bool showEdges; bool showOutlines; @@ -746,8 +755,9 @@ public: // These are called by the platform-specific code. void Paint(); + bool ConvertMouseCoords(double *x, double *y); 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 MouseLeftUp(double x, double y); void MouseLeftDoubleClick(double x, double y);