From 16e78d07b1d4abd83e28f927bab9f34c3eb44e1d Mon Sep 17 00:00:00 2001 From: OmniaX-Dev Date: Fri, 1 May 2026 21:17:23 +0200 Subject: [PATCH] Various bugfixes --- extra/DefaultThemeDark.oss | 10 +-- other/TODO.txt | 5 +- src/ogfx/gui/widgets/TreeView.cpp | 71 +++++++++++++++++- src/ogfx/gui/widgets/TreeView.hpp | 7 ++ src/ogfx/utils/Animation.cpp | 1 + src/ogfx/utils/Animation.hpp | 1 + src/ostd/data/Color.cpp | 93 +++++++++++++++++++----- src/ostd/io/Stylesheet.cpp | 116 +++++++++++++++++++----------- src/ostd/io/Stylesheet.hpp | 16 +++++ src/ostd/string/String.cpp | 25 +++++-- src/ostd/string/String.hpp | 3 +- src/test/GuiTest.cpp | 11 ++- 12 files changed, 286 insertions(+), 73 deletions(-) diff --git a/extra/DefaultThemeDark.oss b/extra/DefaultThemeDark.oss index 7e22ec5..417aa3c 100644 --- a/extra/DefaultThemeDark.oss +++ b/extra/DefaultThemeDark.oss @@ -39,7 +39,7 @@ const $backgroundColor = rgba(30, 30, 30, 255) showBorder = true showBackground = true titlebarType = full - titlebarHeight = 18 + titlebarHeight = 35 titlebarFontSize = 18 titlebarTextAlign = text_left scrollSpeed = 1.2 @@ -362,18 +362,20 @@ const $backgroundColor = rgba(30, 30, 30, 255) % ====== TreeView ====== (tree) { - linePadding = rect(5, 5, 20, 0) + linePadding = rect(5, 5, 0, 0) selectionColor = $accentColor selectionTextColor = color_white iconSpacing = 0.0 - iconInset = 4.0 + iconInset = 4 showIcons = true fontSize = 16 textColor = $textColor backgroundColor = $darkColor + backgroundColor2 = $darkColor.lighten(0.06) + showAlternatingBackground = true borderColor = $borderColor separatorLineColor = $borderColor - showSeparatorLine = true + showSeparatorLine = false iconTintColor = $accentColorLight indentWidth = 16.0 chevronColor = $accentColorDark diff --git a/other/TODO.txt b/other/TODO.txt index 102b24a..7c88f2a 100644 --- a/other/TODO.txt +++ b/other/TODO.txt @@ -23,12 +23,12 @@ ***Add Dark Mode Default theme ***FIX: Optimize ListView ***FIX: Limit FPS even when continuous events happen +***FIX: Float values in stylesheet needing to have decimal point to be read as float +***Make parsing of vec2 and rect in stylesheet, consistent with other value functions like gradients and animations Implement cursors in Stylesheet FIX: Window getting exponentially bigger when Desktop scale changes FIX: Refreshing scroll when content extent changes See if I can query OS theme mode to saelect the right theme -FIX: Float values in stylesheet needing to have decimal point to be read as float -Make parsing of vec2 and rect in stylesheet, consistent with other value functions like gradients and animations Add buttons to scroll tabs in TabPanel Add Font class, to handle different font attributes Add multi-selection to TreeView @@ -51,6 +51,7 @@ Add setSelectedMenuItem() function to ComboBox FIX: ContextMenu rendering partly outside the screen when showing it close to the right Window border Update theme for visible TreeView lines Add ToolBar::addButton overload that takes a ogfx::Icon wrapper +Redesign theme overrides to be more robust diff --git a/src/ogfx/gui/widgets/TreeView.cpp b/src/ogfx/gui/widgets/TreeView.cpp index 290d33a..a1d8b95 100644 --- a/src/ogfx/gui/widgets/TreeView.cpp +++ b/src/ogfx/gui/widgets/TreeView.cpp @@ -135,6 +135,8 @@ namespace ogfx void TreeView::applyTheme(const ostd::Stylesheet& theme) { + enableAlternatingBackground(getThemeValue(theme, "showAlternatingBackground", isAlternatingBackgroundEnabled())); + setBackgroundColor2(getThemeValue(theme, "backgroundColor2", getBackgroundColor2())); setSeparatorLineColor(getThemeValue(theme, "separatorLineColor", getSeparatorLineColor())); enableShowSeparatorLine(getThemeValue(theme, "showSeparatorLine", isShowSeparatorLineEnabled())); setLinePadding(getThemeValue(theme, "linePadding", getLinePadding())); @@ -165,6 +167,7 @@ namespace ogfx // Walk forward through visible items only, advancing y for each one we draw. f32 y = 0; i32 startIdx = 0; + u32 visibleRow = 0; // counts only items whose row is actually drawn for (auto& itemPtr : m_list) { auto& item = *itemPtr; @@ -172,6 +175,7 @@ namespace ogfx f32 itemH = item.getDimensions().y; if (y + itemH > visibleStart) break; y += itemH; + visibleRow++; startIdx++; } @@ -186,14 +190,21 @@ namespace ogfx if (content.w < bounds.w) lineW = bounds.w; auto pad = getLinePadding(); - Rectangle lineRect = { Vec2 { bounds.x, bounds.y + y + pad.h } + getScrollOffset(), { lineW, itemH } }; + Rectangle lineRect = { Vec2 { bounds.x, bounds.y + y + pad.right() } + getScrollOffset(), { lineW, itemH } }; if (item.isSelected()) { textColor = getLineSelectionTextColor(); gfx.fillRect(lineRect, getLineSelectionColor()); } else + { textColor = getTextColor(); + if (isAlternatingBackgroundEnabled()) + { + bool lineColorToggle = visibleRow % 2 == 0; + gfx.fillRect(lineRect, (lineColorToggle ? getBackgroundColor() : getBackgroundColor2())); + } + } // Branch lines (drawn under everything else for this row). if (isBranchLinesEnabled()) @@ -215,7 +226,7 @@ namespace ogfx }; if (m_chevronDrawCallback) { - gfx.pushClippingRect(chevronBounds); + gfx.pushClippingRect(chevronBounds, true); m_chevronDrawCallback(*this, item, chevronBounds, gfx); gfx.popClippingRect(); } @@ -234,9 +245,43 @@ namespace ogfx getIconTintColor()); textX += itemH + getIconSpacing(); } - gfx.drawLine({ Vec2 { bounds.x, bounds.y + y + itemH + pad.h } + getScrollOffset(), Vec2 { bounds.x + lineW, bounds.y + y + itemH + pad.h } + getScrollOffset() }, getSeparatorLineColor(), 1); + if (isShowSeparatorLineEnabled()) + gfx.drawLine({ Vec2 { bounds.x, bounds.y + y + itemH + pad.h } + getScrollOffset(), Vec2 { bounds.x + lineW, bounds.y + y + itemH + pad.h } + getScrollOffset() }, getSeparatorLineColor(), 1); gfx.drawVCenteredString(item, lineRect + Rectangle { textX, 0, -(textX * 2), 0 }, textColor, getFontSize()); y += itemH; + visibleRow++; + } + } + + void TreeView::onUpdate(void) + { + ScrollableWidget::onUpdate(); + const f32 scrollY = -getScrollOffset().y; + const f32 visibleH = getContentBounds().h; + const f32 visibleStart = scrollY; + const f32 visibleEnd = scrollY + visibleH; + f32 y = 0; + i32 startIdx = 0; + for (auto& itemPtr : m_list) + { + auto& item = *itemPtr; + if (!item.isValid() || !item.isVisible()) { startIdx++; continue; } + f32 itemH = item.getDimensions().y; + if (y + itemH > visibleStart) break; + y += itemH; + startIdx++; + } + + for (i32 i = startIdx; i < (i32)m_list.size(); i++) + { + auto& item = *m_list[i]; + if (!item.isValid()) continue; + if (!item.isVisible()) continue; + if (!item.isIconEnabled()) continue; + if (y > visibleEnd) break; + if (item.getIcon().getFrameCount() != 1) + std::cout << item.getIcon().getCurrentFrame() << "\n"; + item.getIcon().update(); } } @@ -490,6 +535,26 @@ namespace ogfx return m_list.size() > index; } + void TreeView::expandAll(void) + { + for (auto& itemPtr : m_list) + { + if (itemPtr->m_hasChildren && !itemPtr->m_expanded) + itemPtr->m_expanded = true; + } + m_extentsDirty = true; + } + + void TreeView::collapseAll(void) + { + for (auto& itemPtr : m_list) + { + if (itemPtr->m_hasChildren && itemPtr->m_expanded) + itemPtr->m_expanded = false; + } + m_extentsDirty = true; + } + void TreeView::render_chevron(ogfx::BasicRenderer2D& gfx, const Item& item, const Rectangle& bounds) { // Default chevron: a small triangle. Right-pointing when collapsed, down-pointing when expanded. diff --git a/src/ogfx/gui/widgets/TreeView.hpp b/src/ogfx/gui/widgets/TreeView.hpp index dd0a9f2..118709e 100644 --- a/src/ogfx/gui/widgets/TreeView.hpp +++ b/src/ogfx/gui/widgets/TreeView.hpp @@ -90,6 +90,7 @@ namespace ogfx TreeView& create(void); void applyTheme(const ostd::Stylesheet& theme) override; void onDraw(ogfx::BasicRenderer2D& gfx) override; + void onUpdate(void) override; void afterDraw(ogfx::BasicRenderer2D& gfx) override; void onMouseReleased(const Event& event) override; Rectangle getContentExtents(void) const override; @@ -103,11 +104,15 @@ namespace ogfx bool removeLine(u32 index); bool hasLine(const String& text); bool hasLine(u32 index); + void expandAll(void); + void collapseAll(void); inline stdvec& getSelection(void) { return m_selectedList; } inline void setSelectionChangedCallback(std::function& selection)> callback) { callback_onSelectionChanged = std::move(callback); } inline void setChevronDrawCallback(ChevronDrawCallback cb) { m_chevronDrawCallback = std::move(cb); } + OSTD_PARAM_GETSET(Color, BackgroundColor2, m_backgroundColor2); + OSTD_BOOL_PARAM_GETSET_E(AlternatingBackground, m_showAlternatingBackground); OSTD_PARAM_GETSET(Color, SeparatorLineColor, m_lineColor); OSTD_BOOL_PARAM_GETSET_E(ShowSeparatorLine, m_showLine); OSTD_BOOL_PARAM_GETSET_E(Icons, m_showIcons); @@ -149,6 +154,8 @@ namespace ogfx Color m_selectionColor { Colors::Crimson }; Color m_selectionTextColor{ Colors::White }; Color m_iconTintColor { Colors::White }; + Color m_backgroundColor2 { Colors::Black }; + bool m_showAlternatingBackground { true }; f32 m_indentWidth { 16 }; Color m_chevronColor { Colors::White }; diff --git a/src/ogfx/utils/Animation.cpp b/src/ogfx/utils/Animation.cpp index 40701be..301c72e 100644 --- a/src/ogfx/utils/Animation.cpp +++ b/src/ogfx/utils/Animation.cpp @@ -77,6 +77,7 @@ namespace ogfx else m_currentFrame = 0; } + if (m_animData.columns <= 0) return; i32 col = m_currentFrame % m_animData.columns; i32 row = (m_animData.rows > 1) ? cast(m_currentFrame / m_animData.columns) : 0; diff --git a/src/ogfx/utils/Animation.hpp b/src/ogfx/utils/Animation.hpp index 04994de..198f8c1 100644 --- a/src/ogfx/utils/Animation.hpp +++ b/src/ogfx/utils/Animation.hpp @@ -36,6 +36,7 @@ namespace ogfx inline Animation(const AnimationData& ad, Image& spriteSheet) { create(ad, spriteSheet); }; inline Animation& create(const AnimationData& ad) { return create(ad, InvalidImage); } inline void update(void) { m_timer.update(); } + inline i32 getCurrentFrame(void) const { return m_currentFrame; } Animation& create(const AnimationData& ad, Image& spriteSheet); void resetAnimation(void); diff --git a/src/ostd/data/Color.cpp b/src/ostd/data/Color.cpp index b2d9336..26eac37 100755 --- a/src/ostd/data/Color.cpp +++ b/src/ostd/data/Color.cpp @@ -8,35 +8,35 @@ namespace ostd Color::Color(void) : r(*this), g(*this), b(*this), a(*this) { set(); - setTypeName("ox::Color"); + setTypeName("ostd::Color"); BaseObject::setValid(true); } Color::Color(u8 rgb_single_value, u8 alpha) : r(*this), g(*this), b(*this), a(*this) { set(rgb_single_value, alpha); - setTypeName("ox::Color"); + setTypeName("ostd::Color"); BaseObject::setValid(true); } Color::Color(u8 _r, u8 _g, u8 _b, u8 alpha) : r(*this), g(*this), b(*this), a(*this) { set(_r, _g, _b, alpha); - setTypeName("ox::Color"); + setTypeName("ostd::Color"); BaseObject::setValid(true); } Color::Color(const String& color_string) : r(*this), g(*this), b(*this), a(*this) { set(color_string); - setTypeName("ox::Color"); + setTypeName("ostd::Color"); BaseObject::setValid(true); } Color::Color(const FloatCol& normalized_color) : r(*this), g(*this), b(*this), a(*this) { set(normalized_color); - setTypeName("ox::Color"); + setTypeName("ostd::Color"); BaseObject::setValid(true); } @@ -99,37 +99,88 @@ namespace ostd { String se(color_string); se.trim(); + + // --- Step 1: Extract optional .lighten(x) or .darken(x) suffix --- + enum class Modifier { None, Lighten, Darken }; + Modifier mod = Modifier::None; + f32 modAmount = 0.0f; + + { + stdvec matches; + auto positions = se.regexFind("\\.(lighten|darken)\\(\\s*([0-9]*\\.?[0-9]+)\\s*\\)\\s*$", true, &matches); + + if (!positions.empty()) + { + const String& whole = matches[0]; + mod = whole.contains("lighten") ? Modifier::Lighten : Modifier::Darken; + + // Extract the number between '(' and ')' + u32 openParen = whole.indexOf("("); + u32 closeParen = whole.indexOf(")"); + String numStr = whole.new_substr(openParen + 1, closeParen); + numStr.trim(); + modAmount = numStr.toFloat(); + + // Clamp to [0, 1] + if (modAmount < 0.0f) modAmount = 0.0f; + if (modAmount > 1.0f) modAmount = 1.0f; + + // Strip the suffix from se + se = se.substr(0, positions[0]); + se.trim(); + } + } + + // --- Step 2: Default values --- r = 0; g = 0; b = 0; a = 255; + + // --- Step 3: Normalize "#" prefix to "0x" --- if (se.startsWith("#")) { String tmp = se.new_substr(1); tmp.trim(); se = String("0x") + tmp; } + + // --- Step 4: Parse the color body --- if (se.startsWith("0x")) { + String hexPart = se.new_substr(2); + hexPart.trim(); + u32 hexDigits = hexPart.len(); + i64 ic = se.toInt(); - union uC32 { - u8 data[4]; - u32 value; - } c32_u; - c32_u.value = cast(ic); - a = c32_u.data[0]; - b = c32_u.data[1]; - g = c32_u.data[2]; - r = c32_u.data[3]; + u32 c = static_cast(ic); + + if (hexDigits <= 6) + { + // RGB only — alpha defaults to 255 + r = (c >> 16) & 0xFF; + g = (c >> 8) & 0xFF; + b = (c >> 0) & 0xFF; + a = 255; + } + else + { + // RGBA — 7 or 8 digits + r = (c >> 24) & 0xFF; + g = (c >> 16) & 0xFF; + b = (c >> 8) & 0xFF; + a = (c >> 0) & 0xFF; + } } - else if ((se.startsWith("(") || se.startsWith("rgba(") || se.startsWith("rgb(")) && se.endsWith(")") && se.contains(",")) + else if ((se.startsWith("(") || se.startsWith("rgba(") || se.startsWith("rgb(")) + && se.endsWith(")") && se.contains(",")) { se = se.substr(se.indexOf("(") + 1, se.len() - 1); se.trim(); auto tokens = se.tokenize(",", true, false); if (tokens.count() < 3 || tokens.count() > 4) { - OX_WARN("ox::Color::set(const String&) -> Invalid rgb string format: %s.", color_string.c_str()); + OX_WARN("ostd::Color::set(const String&) -> Invalid rgb string format: %s.", color_string.c_str()); return *this; } r = tokens.next().toInt(); @@ -140,8 +191,16 @@ namespace ostd } else { - OX_WARN("ox::Color::set(const String&) -> Unkown color string format: %s", color_string.c_str()); + OX_WARN("ostd::Color::set(const String&) -> Unknown color string format: %s", color_string.c_str()); + return *this; } + + // --- Step 5: Apply lighten/darken modifier --- + if (mod == Modifier::Lighten) + lighten(modAmount); + else if (mod == Modifier::Darken) + darken(modAmount); + return *this; } diff --git a/src/ostd/io/Stylesheet.cpp b/src/ostd/io/Stylesheet.cpp index 42fab78..4b62838 100644 --- a/src/ostd/io/Stylesheet.cpp +++ b/src/ostd/io/Stylesheet.cpp @@ -257,20 +257,38 @@ namespace ostd if (key == "") return false; String themeID = ""; - auto l_parseColor = [this](const String& _value) -> String { - String value = _value.new_toLower().trim(); - if (value.startsWith("color(") && value.endsWith(")")) + auto l_isVec2 = [this](String& value) -> bool { + value.trim(); + if (value.new_toLower().startsWith("vec2(") && value.new_toLower().endsWith(")")) + { + value.substr(5, value.len() - 1).trim(); + return true; + } + return false; + }; + auto l_isRect = [this](String& value) -> bool { + value.trim(); + if (value.new_toLower().startsWith("rect(") && value.new_toLower().endsWith(")")) + { + value.substr(5, value.len() - 1).trim(); + return true; + } + return false; + }; + auto l_isColor = [this](String& value) -> bool { + value.trim(); + if (value.new_toLower().startsWith("color(") && value.new_toLower().endsWith(")")) { value.substr(6, value.len() - 1).trim(); - return value; + return true; } - else if ((value.startsWith("#") && (value.len() == 7 || value.len() == 9)) || - (value.startsWith("rgb(") && value.endsWith(")")) || - (value.startsWith("rgba(") && value.endsWith(")"))) + else if ((value.new_toLower().startsWith("#")) || + (value.new_toLower().startsWith("rgb(")) || + (value.new_toLower().startsWith("rgba("))) { - return value; + return true; } - return ""; + return false; }; auto l_isColorGradientValue = [this](const String& _value) -> bool { String value = _value.new_toLower().trim(); @@ -314,10 +332,10 @@ namespace ostd themeID = key.new_substr(1, key.indexOf(" ")).trim(); key.substr(key.indexOf(" ") + 1).trim(); } - if (value.isInt()) - set(key, cast(value.toInt()), themeID); - else if (value.isNumeric(true)) + if (value.isNumeric(true)) set(key, value.toFloat(), themeID); + else if (value.isInt()) + set(key, cast(value.toInt()), themeID); else if (value == "true" || value == "false") set(key, value == "true", themeID); else if (value.startsWith("\"") && value.endsWith("\"")) @@ -325,9 +343,9 @@ namespace ostd valuePreserveCase.substr(1, value.len() - 1); set(key, valuePreserveCase, themeID); } - else if (String v = l_parseColor(value); v != "") + else if (l_isColor(value)) { - set(key, Color(v), themeID); + set(key, parseColor(value, variables), themeID); } else if (l_isColorGradientValue(value)) { @@ -353,35 +371,13 @@ namespace ostd else return false; } - else if (value.startsWith("vec2(") && value.endsWith(")")) + else if (l_isVec2(value)) { - value.substr(5, value.len() - 1).trim(); - auto tokens = value.tokenize(","); - if (tokens.count() != 2) - return false; - stdvec vec; - for (const auto& tok : tokens) - { - if (!tok.isNumeric(true)) - return false; - vec.push_back(tok.toFloat()); - } - set(key, Vec2(vec[0], vec[1]), themeID); + set(key, parseVec2(value, variables), themeID); } - else if (value.startsWith("rect(") && value.endsWith(")")) + else if (l_isRect(value)) { - value.substr(5, value.len() - 1).trim(); - auto tokens = value.tokenize(","); - if (tokens.count() != 4) - return false; - stdvec vec; - for (const auto& tok : tokens) - { - if (!tok.isNumeric(true)) - return false; - vec.push_back(tok.toFloat()); - } - set(key, Rectangle(vec[0], vec[1], vec[2], vec[3]), themeID); + set(key, parseRect(value, variables), themeID); } else if (exitCondition) { @@ -464,6 +460,44 @@ namespace ostd return newLines; } + Color Stylesheet::parseColor(const String& _value, const VariableList& variables) + { + String value = replaceVariables(_value, variables); + return Color(value); + } + + Vec2 Stylesheet::parseVec2(const String& _value, const VariableList& variables) + { + String value = replaceVariables(_value, variables); + auto tokens = value.tokenize(","); + if (tokens.count() != 2) + return { 0, 0 }; + stdvec vec; + for (const auto& tok : tokens) + { + if (!tok.isNumeric(true)) + return { 0, 0 }; + vec.push_back(tok.toFloat()); + } + return { vec[0], vec[1] }; + } + + Rectangle Stylesheet::parseRect(const String& _value, const VariableList& variables) + { + String value = replaceVariables(_value, variables); + auto tokens = value.tokenize(","); + if (tokens.count() != 4) + return { 0, 0, 0, 0 }; + stdvec vec; + for (const auto& tok : tokens) + { + if (!tok.isNumeric(true)) + return { 0, 0, 0, 0 }; + vec.push_back(tok.toFloat()); + } + return { vec[0], vec[1], vec[2], vec[3] }; + } + ColorGradient Stylesheet::parseColorGradient(const String& _value, const VariableList& variables) { String value = _value.new_trim(); @@ -671,7 +705,7 @@ namespace ostd break; } } - return lineCopy; + return lineCopy.trim(); } void Stylesheet::debugPrint(void) diff --git a/src/ostd/io/Stylesheet.hpp b/src/ostd/io/Stylesheet.hpp index 303caca..ef8f6c7 100644 --- a/src/ostd/io/Stylesheet.hpp +++ b/src/ostd/io/Stylesheet.hpp @@ -50,8 +50,21 @@ namespace ostd { if (auto v = getVariant(key, themeIDList, qualifierList)) { + // Direct match: return as-is if (auto p = std::get_if(v)) return *p; + + // Cross-cast for numeric types + if constexpr (std::is_same_v) + { + if (auto p = std::get_if(v)) + return cast(*p); + } + else if constexpr (std::is_same_v) + { + if (auto p = std::get_if(v)) + return cast(*p); + } } return fallback; } @@ -63,6 +76,9 @@ namespace ostd bool parseThemeFileLine(const String& line, const VariableList& variables, bool exitCondition = false); String parseGroupSelector(const String& rawSelector) const; stdvec parseGroup(const String& selector, const stdvec& group); + Color parseColor(const String& _value, const VariableList& variables); + Vec2 parseVec2(const String& _value, const VariableList& variables); + Rectangle parseRect(const String& _value, const VariableList& variables); ColorGradient parseColorGradient(const String& _value, const VariableList& variables); AnimationData parseAnim(const String& _value, bool& outError, const VariableList& variables); String replaceVariables(const String& line, const VariableList& variables, bool stop_at_first_match = true); diff --git a/src/ostd/string/String.cpp b/src/ostd/string/String.cpp index 88498ae..6d37454 100644 --- a/src/ostd/string/String.cpp +++ b/src/ostd/string/String.cpp @@ -523,18 +523,35 @@ namespace ostd return m_data.ends_with(str); } - bool String::regexMatches(const String& regex_pattern, bool case_insensitive) const + stdvec String::regexFind(const String& regex_pattern, bool case_insensitive, stdvec* outMatchList) const { + stdvec indices; + if (outMatchList) + outMatchList->clear(); + try { - boost::regex rgx(regex_pattern.cpp_str(), case_insensitive ? boost::regex_constants::icase : boost::regex_constants::normal); - return boost::regex_search(m_data, rgx); + boost::regex rgx(regex_pattern.cpp_str(), + case_insensitive ? boost::regex_constants::icase : boost::regex_constants::normal); + + auto begin = boost::sregex_iterator(m_data.begin(), m_data.end(), rgx); + auto end = boost::sregex_iterator(); + + for (auto it = begin; it != end; ++it) + { + const boost::smatch& match = *it; + indices.push_back(static_cast(match.position())); + if (outMatchList) + outMatchList->emplace_back(match.str(0)); + } } catch (const boost::regex_error& err) { std::cerr << err.what() << '\n'; //TODO: Better error handling - return false; + // indices is already empty, outMatchList already cleared } + + return indices; } u32 String::count(const String& str) const diff --git a/src/ostd/string/String.hpp b/src/ostd/string/String.hpp index 5485ad2..38c4bd9 100644 --- a/src/ostd/string/String.hpp +++ b/src/ostd/string/String.hpp @@ -167,7 +167,8 @@ namespace ostd bool contains(const String& str) const; bool startsWith(const String& str) const; bool endsWith(const String& str) const; - bool regexMatches(const String& regex_pattern, bool case_insensitive = false) const; + stdvec regexFind(const String& regex_pattern, bool case_insensitive = false, stdvec* outMatchList = nullptr) const; + inline bool regexMatches(const String& regex_pattern, bool case_insensitive = false) const { return regexFind(regex_pattern, case_insensitive, nullptr).size() > 0; } u32 count(const String& str) const; i32 indexOf(char c, u32 start = 0) const; i32 indexOf(const String& str, u32 start = 0) const; diff --git a/src/test/GuiTest.cpp b/src/test/GuiTest.cpp index ca1a106..39dc0bb 100644 --- a/src/test/GuiTest.cpp +++ b/src/test/GuiTest.cpp @@ -152,7 +152,7 @@ class TestWindow : public Window // std::cout << *(selection[0]) << "\n"; // }); - auto l_randomIcon = [&](const ogfx::AnimationData& src) -> ogfx::Icon { + auto l_randomIcon = [this](const ogfx::AnimationData& src) -> ogfx::Icon { static ogfx::Image img("./icons.png", getGFX()); ogfx::AnimationData ad = src; ad.columnOffset = ostd::Random::getui32(0, 7); @@ -175,6 +175,8 @@ class TestWindow : public Window auto& item = m_list.getLine(index); auto& child = m_list.addChild(item, String("Child ").add(i + 1), l_randomIcon(iconsAD)); } + static ogfx::Image animImg("./img.png", getGFX()); + m_list.getLine(20).setIcon({ animImg, ad }); m_list.setSelectionChangedCallback([&](stdvec& selection) -> void { auto path = (selection[0])->getFullPath(); for (i32 i = 0; i < path.size(); i++) @@ -185,6 +187,13 @@ class TestWindow : public Window } std::cout << "\n"; }); + // m_list.setChevronDrawCallback([&](TreeView& sender, const TreeView::Item& item, const ostd::Rectangle& bounds, ogfx::BasicRenderer2D& gfx) -> void { + // if (item.isExpanded()) + // gfx.fillRect(bounds, Colors::Red); + // else + // gfx.fillRect(bounds, Colors::Blue); + // }); + m_list.collapseAll(); for (i32 i = 0; i < 20; i++) {