Various bugfixes
This commit is contained in:
parent
796b03f08c
commit
16e78d07b1
12 changed files with 286 additions and 73 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -135,6 +135,8 @@ namespace ogfx
|
|||
|
||||
void TreeView::applyTheme(const ostd::Stylesheet& theme)
|
||||
{
|
||||
enableAlternatingBackground(getThemeValue<bool>(theme, "showAlternatingBackground", isAlternatingBackgroundEnabled()));
|
||||
setBackgroundColor2(getThemeValue<Color>(theme, "backgroundColor2", getBackgroundColor2()));
|
||||
setSeparatorLineColor(getThemeValue<Color>(theme, "separatorLineColor", getSeparatorLineColor()));
|
||||
enableShowSeparatorLine(getThemeValue<bool>(theme, "showSeparatorLine", isShowSeparatorLineEnabled()));
|
||||
setLinePadding(getThemeValue<Rectangle>(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.
|
||||
|
|
|
|||
|
|
@ -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<Item*>& getSelection(void) { return m_selectedList; }
|
||||
inline void setSelectionChangedCallback(std::function<void(stdvec<Item*>& 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 };
|
||||
|
|
|
|||
|
|
@ -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<i32>(m_currentFrame / m_animData.columns) : 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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<String> 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<u32>(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<u32>(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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<i32>(value.toInt()), themeID);
|
||||
else if (value.isNumeric(true))
|
||||
if (value.isNumeric(true))
|
||||
set(key, value.toFloat(), themeID);
|
||||
else if (value.isInt())
|
||||
set(key, cast<i32>(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<f32> 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<f32> 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<f32> 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<f32> 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)
|
||||
|
|
|
|||
|
|
@ -50,8 +50,21 @@ namespace ostd
|
|||
{
|
||||
if (auto v = getVariant(key, themeIDList, qualifierList))
|
||||
{
|
||||
// Direct match: return as-is
|
||||
if (auto p = std::get_if<T>(v))
|
||||
return *p;
|
||||
|
||||
// Cross-cast for numeric types
|
||||
if constexpr (std::is_same_v<T, i32>)
|
||||
{
|
||||
if (auto p = std::get_if<f32>(v))
|
||||
return cast<i32>(*p);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, f32>)
|
||||
{
|
||||
if (auto p = std::get_if<i32>(v))
|
||||
return cast<f32>(*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<String> parseGroup(const String& selector, const stdvec<String>& 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);
|
||||
|
|
|
|||
|
|
@ -523,18 +523,35 @@ namespace ostd
|
|||
return m_data.ends_with(str);
|
||||
}
|
||||
|
||||
bool String::regexMatches(const String& regex_pattern, bool case_insensitive) const
|
||||
stdvec<u32> String::regexFind(const String& regex_pattern, bool case_insensitive, stdvec<String>* outMatchList) const
|
||||
{
|
||||
stdvec<u32> 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<u32>(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
|
||||
|
|
|
|||
|
|
@ -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<u32> regexFind(const String& regex_pattern, bool case_insensitive = false, stdvec<String>* 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;
|
||||
|
|
|
|||
|
|
@ -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<TreeView::Item*>& 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++)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue