diff --git a/.clangd b/.clangd index c60968a..e6defe1 100644 --- a/.clangd +++ b/.clangd @@ -1,5 +1,4 @@ CompileFlags: CompilationDatabase: bin - Add: ["--target=x86_64-w64-mingw32"] Diagnostics: MissingIncludes: None \ No newline at end of file diff --git a/extra/DefaultTheme.oss b/extra/DefaultTheme.oss index 24108ab..e1b6ca1 100644 --- a/extra/DefaultTheme.oss +++ b/extra/DefaultTheme.oss @@ -258,7 +258,7 @@ const $accentColorDark = #6B0A1DFF -% ====== Slider ====== +% ====== ListView ====== (list) { (item) { defaultTextColor = #202020FF @@ -273,4 +273,21 @@ const $accentColorDark = #6B0A1DFF showSeparatorLine = true } -% =================== +% ====================== + + + +% ====== Context Menu ====== +(context) { + padding = rect(16, 0, 16, 0) + itemSpacing = 6.0 + fontSize = 18 + backgroundColor = #CCAAAAFF + selectionColor = $accentColor + selectionTextColor = #FFFFFFFF + separatorLineColor = #700000FF + textColor = $textColor + submenuIndicatorColor = $accentColorDark + borderColor = #400000FF +} +% ========================== diff --git a/other/TODO.txt b/other/TODO.txt index d784ef7..922e103 100644 --- a/other/TODO.txt +++ b/other/TODO.txt @@ -33,6 +33,7 @@ Add multi-selection to ListView Cache getStringDimensions() call in TabPanel::draw_tabs FIX: Optimize ListView FIX: Padding for ListView items +Add icons to ListView diff --git a/src/ogfx/gui/ContextMenu.cpp b/src/ogfx/gui/ContextMenu.cpp index 793f8c8..b2a9f03 100644 --- a/src/ogfx/gui/ContextMenu.cpp +++ b/src/ogfx/gui/ContextMenu.cpp @@ -33,7 +33,7 @@ namespace ogfx { auto s = gfx.getStringDimensions(entry.text, fontSize); if (s.x > size.x) - size.x = s.x; + size.x = s.x + (entry.submenus.size() > 0 ? 40 : 0); size.y += s.y; entry.update_size(gfx, fontSize); } @@ -45,19 +45,53 @@ namespace ogfx return *this; } + void ContextMenu::applyTheme(const ostd::Stylesheet& theme) + { + setPadding(theme.get("context.padding", getPadding(), {}, {})); + setItemSpacing(theme.get("context.itemSpacing", getItemSpacing(), {}, {})); + setFontSize(theme.get("context.fontSize", getFontSize(), {}, {})); + setBackgroundColor(theme.get("context.backgroundColor", getBackgroundColor(), {}, {})); + setSelectionColor(theme.get("context.selectionColor", getSelectionColor(), {}, {})); + setSelectionTextColor(theme.get("context.selectionTextColor", getSelectionTextColor(), {}, {})); + setSeparatorLineColor(theme.get("context.separatorLineColor", getSeparatorLineColor(), {}, {})); + setTextColor(theme.get("context.textColor", getTextColor(), {}, {})); + setSubmenuIndicatorColor(theme.get("context.submenuIndicatorColor", getSubmenuIndicatorColor(), {}, {})); + setBorderColor(theme.get("context.borderColor", getBorderColor(), {}, {})); + } + void ContextMenu::draw(BasicRenderer2D& gfx) { - gfx.outlinedRect(*this, Colors::Crimson, Colors::Black, 2); + gfx.fillRect(*this, getBackgroundColor()); + const f32 triPad = 14; f32 y = 0; + i32 i = 0; for (auto& entry : m_data.entries) { Vec2 entryPos = getPosition() + Vec2 { m_padding.x, y + m_padding.y }; Rectangle rect = { entryPos - Vec2 { m_padding.x, 0 }, getw(), m_entryHeight }; if (rect.contains(m_mousePos, true)) - gfx.outlinedRect(rect, Colors::Chocolate, Colors::White, 1); - gfx.drawString(entry.text, entryPos, Colors::Black, getFontSize()); + { + gfx.outlinedRect(rect, getSelectionColor(), getSeparatorLineColor(), 1, false, false, i != m_data.entries.size() , false); + gfx.drawVCenteredString(entry.text, { entryPos, getw(), m_entryHeight }, getSelectionTextColor(), getFontSize()); + } + else + { + gfx.drawRect(rect, getSeparatorLineColor(), 1, false, false, i != m_data.entries.size() , false); + gfx.drawVCenteredString(entry.text, { entryPos, getw(), m_entryHeight }, getTextColor(), getFontSize()); + } + if (entry.submenus.size() > 0) + { + Triangle tri { + { rect.x + rect.w - m_entryHeight + triPad, rect.y + (triPad * 0.5f) }, + { rect.x + rect.w - (triPad * 0.5f), rect.y + (rect.h * 0.5f) }, + { rect.x + rect.w - m_entryHeight + triPad, rect.y + rect.h - (triPad * 0.5f) } + }; + gfx.fillTriangle(tri, getSubmenuIndicatorColor()); + } y += m_entryHeight; + i++; } + gfx.drawRect(*this, getBorderColor(), 2); } void ContextMenu::onMouseReleased(const Event& event) @@ -84,6 +118,16 @@ namespace ogfx event.handle(); } + void ContextMenu::onMousePressed(const Event& event) + { + event.handle(); + } + + void ContextMenu::onMouseScrolled(const Event& event) + { + event.handle(); + } + void ContextMenu::show(void) { m_visible = true; @@ -119,10 +163,11 @@ namespace ogfx for (auto& entry : m_data.entries) { auto s = gfx.getStringDimensions(entry.text, getFontSize()); - if (s.x > getw()) - setw(s.x); - addh(s.y); - m_entryHeight = s.y; + f32 extra = (entry.submenus.size() > 0 ? s.y : 0); + if (s.x + extra > getw()) + setw(s.x + extra); + addh(s.y + m_spacing); + m_entryHeight = s.y + m_spacing; entry.update_size(gfx, getFontSize()); } addSize({ m_padding.x + m_padding.w, m_padding.y + m_padding.h }); diff --git a/src/ogfx/gui/ContextMenu.hpp b/src/ogfx/gui/ContextMenu.hpp index 9505846..bb5be85 100644 --- a/src/ogfx/gui/ContextMenu.hpp +++ b/src/ogfx/gui/ContextMenu.hpp @@ -30,7 +30,7 @@ namespace ogfx { public: struct Entry { - inline Entry(const String& t, const stdvec& sub = {}) { text = t; } + inline Entry(const String& t, const stdvec& sub = {}) { text = t; submenus = sub; } String text { "" }; stdvec submenus; @@ -47,9 +47,12 @@ namespace ogfx public: inline ContextMenu(WindowCore& window) : m_window(window) { create(); } ContextMenu& create(void); + void applyTheme(const ostd::Stylesheet& theme); void draw(BasicRenderer2D& gfx); void onMouseReleased(const Event& event); void onMouseMoved(const Event& event); + void onMousePressed(const Event& event); + void onMouseScrolled(const Event& event); void show(void); void show(const Vec2& pos); void hide(void); @@ -59,6 +62,16 @@ namespace ogfx inline bool isVisible(void) const { return m_visible; } inline i32 getFontSize(void) const { return m_fontSize; } + OSTD_PARAM_GETSET(Rectangle, Padding, m_padding); + OSTD_PARAM_GETSET(f32, ItemSpacing, m_spacing); + OSTD_PARAM_GETSET(Color, BackgroundColor, m_backgroundColor); + OSTD_PARAM_GETSET(Color, SelectionColor, m_selectionColor); + OSTD_PARAM_GETSET(Color, SelectionTextColor, m_selectionTextColor); + OSTD_PARAM_GETSET(Color, SeparatorLineColor, m_separatorLineColor); + OSTD_PARAM_GETSET(Color, TextColor, m_textColor); + OSTD_PARAM_GETSET(Color, SubmenuIndicatorColor, m_submenuIndicatorColor); + OSTD_PARAM_GETSET(Color, BorderColor, m_borderColor); + private: void update_size(void); @@ -67,10 +80,19 @@ namespace ogfx bool m_visible { false }; Instance m_data; f32 m_entryHeight { 0 }; - Rectangle m_padding { 10, 10, 10, 10 }; - Vec2 m_mousePos; + Vec2 m_mousePos { 0, 0 }; + Rectangle m_padding { 16, 0, 16, 0 }; + f32 m_spacing { 8 }; i32 m_fontSize { 18 }; + Color m_backgroundColor { "#6B0A1DFF" }; + Color m_selectionColor { "#DC143CFF" }; + Color m_selectionTextColor { "#F16A85FF" }; + Color m_separatorLineColor { "#700000FF" }; + Color m_textColor { "#F16A85FF" }; + Color m_submenuIndicatorColor { "#111111FF" }; + Color m_borderColor { "#400000FF" }; + friend class Instance; }; diff --git a/src/ogfx/gui/Window.cpp b/src/ogfx/gui/Window.cpp index 90f0966..639173a 100644 --- a/src/ogfx/gui/Window.cpp +++ b/src/ogfx/gui/Window.cpp @@ -829,6 +829,7 @@ namespace ogfx m_guiTheme = &theme; m_rootWidget.__applyTheme(theme, true); m_rootWidget.reloadTheme(true); + m_cmenu.applyTheme(theme); } void Window::__on_window_init(i32 width, i32 height, const String& title) @@ -871,9 +872,11 @@ namespace ogfx m_rootWidget.__draw(m_gfx); onRedraw(m_gfx); if (m_cmenu.isVisible()) + { + stopTooltipTimer(); m_cmenu.draw(m_gfx); - - if (isTooltipShown()) + } + else if (isTooltipShown()) { auto textSize = m_gfx.getStringDimensions(getTooltipText(), m_rootWidget.getTooltipFontSize()); Rectangle textBounds = { getMousePosition(), textSize }; @@ -902,6 +905,8 @@ namespace ogfx } else if (signal.ID == ostd::BuiltinSignals::WindowLostFocus) { + if (m_cmenu.isVisible()) + m_cmenu.hide(); evt.__original_signal_id = ostd::BuiltinSignals::WindowLostFocus; m_rootWidget.__onWindowFocusLost(evt); } @@ -925,6 +930,8 @@ namespace ogfx } else if (signal.ID == ostd::BuiltinSignals::WindowResized) { + if (m_cmenu.isVisible()) + m_cmenu.hide(); evt.windowResized = &(ogfx::WindowResizedData&)signal.userData; evt.__original_signal_id = ostd::BuiltinSignals::WindowResized; m_rootWidget.__onWindowResized(evt); @@ -941,12 +948,16 @@ namespace ogfx { evt.mouse = &(ogfx::MouseEventData&)signal.userData; evt.__original_signal_id = ostd::BuiltinSignals::MouseScrolled; + if (m_cmenu.isVisible()) + m_cmenu.onMouseScrolled(evt); m_rootWidget.__onMouseScrolled(evt); } else if (signal.ID == ostd::BuiltinSignals::MousePressed) { evt.mouse = &(ogfx::MouseEventData&)signal.userData; evt.__original_signal_id = ostd::BuiltinSignals::MousePressed; + if (m_cmenu.isVisible()) + m_cmenu.onMousePressed(evt); m_rootWidget.__onMousePressed(evt); } else if (signal.ID == ostd::BuiltinSignals::MouseReleased) diff --git a/src/ogfx/gui/widgets/Widget.cpp b/src/ogfx/gui/widgets/Widget.cpp index b3d3e88..45b89c8 100644 --- a/src/ogfx/gui/widgets/Widget.cpp +++ b/src/ogfx/gui/widgets/Widget.cpp @@ -272,6 +272,7 @@ namespace ogfx void Widget::__onMousePressed(const Event& event) { + if (event.isHandled()) return; setThemeQualifier("pressed"); if (hasChildren()) m_widgets.onMousePressed(event); @@ -286,6 +287,7 @@ namespace ogfx void Widget::__onMouseReleased(const Event& event) { + if (event.isHandled()) return; setThemeQualifier("pressed", false); m_pressedButton = ogfx::MouseEventData::eButton::None; if (hasChildren()) @@ -312,6 +314,7 @@ namespace ogfx void Widget::__onMouseMoved(const Event& event) { + if (event.isHandled()) return; if (hasChildren()) m_widgets.onMouseMoved(event); if (!event.isHandled()) @@ -326,6 +329,7 @@ namespace ogfx void Widget::__onMouseScrolled(const Event& event) { + if (event.isHandled()) return; if (hasChildren()) m_widgets.onMouseScrolled(event); if (!event.isHandled()) @@ -338,6 +342,7 @@ namespace ogfx void Widget::__onMouseEntered(const Event& event) { + if (event.isHandled()) return; setThemeQualifier("hover"); if (callback_onMouseEntered) callback_onMouseEntered(event); @@ -348,6 +353,7 @@ namespace ogfx void Widget::__onMouseExited(const Event& event) { + if (event.isHandled()) return; setThemeQualifier("hover", false); if (callback_onMouseExited) callback_onMouseExited(event); @@ -358,6 +364,7 @@ namespace ogfx void Widget::__onMouseDragged(const Event& event) { + if (event.isHandled()) return; if (hasChildren()) m_widgets.onMouseDragged(event); if (!event.isHandled()) @@ -366,10 +373,13 @@ namespace ogfx callback_onMouseDragged(event); onMouseDragged(event); } + if (isTooltipEnabled() && isMouseInside()) + getWindow().restartTooltipTimer(); } void Widget::__onKeyPressed(const Event& event) { + if (event.isHandled()) return; if (hasChildren()) m_widgets.onKeyPressed(event); if (!event.isHandled()) @@ -382,6 +392,7 @@ namespace ogfx void Widget::__onKeyReleased(const Event& event) { + if (event.isHandled()) return; m_pressedButton = ogfx::MouseEventData::eButton::None; if (hasChildren()) m_widgets.onKeyReleased(event); @@ -395,6 +406,7 @@ namespace ogfx void Widget::__onTextEntered(const Event& event) { + if (event.isHandled()) return; if (hasChildren()) m_widgets.onTextEntered(event); if (!event.isHandled()) diff --git a/src/ogfx/render/BasicRenderer.cpp b/src/ogfx/render/BasicRenderer.cpp index e21b2d7..bacb400 100644 --- a/src/ogfx/render/BasicRenderer.cpp +++ b/src/ogfx/render/BasicRenderer.cpp @@ -436,13 +436,25 @@ namespace ogfx void BasicRenderer2D::drawCenteredString(const String& str, const Vec2& center, const Color& color, i32 fontSize, f32 scale) { auto dims = getStringDimensions(str, fontSize); - drawString(str, { center.x - (dims.x * scale) * 0.5f, center.y - (dims.y * scale) * 0.5f }, color, fontSize, scale); + drawString(str, { center.x - dims.x * 0.5f, center.y - dims.y * 0.5f }, color, fontSize, scale); } void BasicRenderer2D::drawCenteredString(const String& str, const Rectangle& bounds, const Color& color, i32 fontSize, f32 scale) { drawCenteredString(str, Vec2 { bounds.x + bounds.w * 0.5f, bounds.y + bounds.h * 0.5f }, color, fontSize, scale); } + + void BasicRenderer2D::drawVCenteredString(const String& str, const Rectangle& bounds, const Color& color, i32 fontSize, f32 scale) + { + auto dims = getStringDimensions(str, fontSize); + drawString(str, { bounds.x, (bounds.y + bounds.h * 0.5f) - dims.y * 0.5f }, color, fontSize, scale); + } + + void BasicRenderer2D::drawHCenteredString(const String& str, const Rectangle& bounds, const Color& color, i32 fontSize, f32 scale) + { + auto dims = getStringDimensions(str, fontSize); + drawString(str, { (bounds.x + bounds.w * 0.5f) - dims.x * 0.5f, bounds.y }, color, fontSize, scale); + } // ===================================================== SPECIALIZED ===================================================== diff --git a/src/ogfx/render/BasicRenderer.hpp b/src/ogfx/render/BasicRenderer.hpp index b8462f7..9cdc177 100644 --- a/src/ogfx/render/BasicRenderer.hpp +++ b/src/ogfx/render/BasicRenderer.hpp @@ -87,6 +87,8 @@ namespace ogfx void drawString(const String& str, const Vec2& position, const Color& color, i32 fontSize = 0, f32 scale = 1.0f); void drawCenteredString(const String& str, const Vec2& center, const Color& color, i32 fontSize = 0, f32 scale = 1.0f); void drawCenteredString(const String& str, const Rectangle& bounds, const Color& color, i32 fontSize = 0, f32 scale = 1.0f); + void drawVCenteredString(const String& str, const Rectangle& bounds, const Color& color, i32 fontSize = 0, f32 scale = 1.0f); + void drawHCenteredString(const String& str, const Rectangle& bounds, const Color& color, i32 fontSize = 0, f32 scale = 1.0f); void drawLine(const FLine& line, const Color& color, i32 thickness = 1, bool rounded = true); void drawRect(const Rectangle& rect, const Color& color, i32 thickness = 1, bool topEdge = true, bool rightEdge = true, bool bottomEdge = true, bool leftEdge = true); diff --git a/src/test/GuiTest.cpp b/src/test/GuiTest.cpp index 091fb1f..ba8e3c3 100644 --- a/src/test/GuiTest.cpp +++ b/src/test/GuiTest.cpp @@ -152,7 +152,7 @@ class Window : public ogfx::gui::Window t1.addWidget(m_slide, { 30, 250 }); t1.addWidget(m_slideLbl, { 340, 240 }); t1.addWidget(m_list, { 30, 300 }); - t2.addWidget(m_panel2, { 500, 100 }); + t1.addWidget(m_panel2, { 500, 100 }); m_panel3.addWidget(m_label4); @@ -238,8 +238,8 @@ class Window : public ogfx::gui::Window { { "Update" }, { "File" }, - { "Open RAW" }, - { "Mouse Settings" }, + { "Open RAW", { { "Image" }, { "Text File" }, { "Audio" } } }, + { "Mouse Settings", { { "Test" } } }, { "Terror" }, { "Properties" } }