Sync push
This commit is contained in:
parent
f00f30004c
commit
c47782280b
7 changed files with 284 additions and 61 deletions
1
.clangd
1
.clangd
|
|
@ -1,5 +1,4 @@
|
||||||
CompileFlags:
|
CompileFlags:
|
||||||
CompilationDatabase: bin
|
CompilationDatabase: bin
|
||||||
Add: ["--target=x86_64-w64-mingw32"]
|
|
||||||
Diagnostics:
|
Diagnostics:
|
||||||
MissingIncludes: None
|
MissingIncludes: None
|
||||||
|
|
@ -196,7 +196,7 @@ const $accentColorDark = #6B0A1DFF
|
||||||
showBackground = true
|
showBackground = true
|
||||||
showBorder = true
|
showBorder = true
|
||||||
useBackgroundGradient = true
|
useBackgroundGradient = true
|
||||||
padding = rect(15, 8, 15, 8)
|
padding = rect(5, 5, 5, 5)
|
||||||
margin = rect(0, 0, 0, 0)
|
margin = rect(0, 0, 0, 0)
|
||||||
showIcon = false
|
showIcon = false
|
||||||
(icon) {
|
(icon) {
|
||||||
|
|
@ -308,7 +308,28 @@ const $accentColorDark = #6B0A1DFF
|
||||||
borderColor = #400000FF
|
borderColor = #400000FF
|
||||||
}
|
}
|
||||||
% =====================
|
% =====================
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
% ====== MenuBar ======
|
||||||
|
(toolbar) {
|
||||||
|
height = 30.0
|
||||||
|
fontSize = 16
|
||||||
|
backgroundColor = #AA9999FF
|
||||||
|
textColor = $textColor
|
||||||
|
selectionColor = $accentColor
|
||||||
|
selectionTextColor = #FFFFFFFF
|
||||||
|
borderColor = #400000FF
|
||||||
|
}
|
||||||
|
% =====================
|
||||||
(@tool_button button) {
|
(@tool_button button) {
|
||||||
fontSize = 18
|
fontSize = 18
|
||||||
padding = rect(10, 0, 10, 0)
|
padding = rect(5, 0, 5, 0)
|
||||||
|
margin = rect(5, 0, 0, 0)
|
||||||
|
showBackground = true
|
||||||
|
backgroundColor = #FFAAAAAA
|
||||||
|
useBackgroundGradient = false
|
||||||
|
showBorder = true
|
||||||
|
borderColor = color_black
|
||||||
|
showIcon = true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,9 @@ namespace ogfx
|
||||||
setStylesheetCategoryName("toolbar");
|
setStylesheetCategoryName("toolbar");
|
||||||
setTypeName("ogfx::gui::widgets::ToolBar");
|
setTypeName("ogfx::gui::widgets::ToolBar");
|
||||||
auto& win = cast<Window&>(getWindow());
|
auto& win = cast<Window&>(getWindow());
|
||||||
setSize(win.getWindowWidth(), m_height);
|
w = win.getWindowWidth();
|
||||||
|
h = m_height;
|
||||||
|
disableBorder();
|
||||||
if (win.isMenuBarVisible())
|
if (win.isMenuBarVisible())
|
||||||
setPosition(0, win.getMenuBar().getHeight());
|
setPosition(0, win.getMenuBar().getHeight());
|
||||||
else
|
else
|
||||||
|
|
@ -45,10 +47,27 @@ namespace ogfx
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
widgets::Button& ToolBar::addButton(const String& iconPath, const String& text, EventCallback callback)
|
||||||
|
{
|
||||||
|
static f32 pad = 0;
|
||||||
|
m_buttons.push_back({ getWindow() });
|
||||||
|
auto& btn = m_buttons.back();
|
||||||
|
btn.addThemeID("tool_button");
|
||||||
|
btn.setIcon(iconPath);
|
||||||
|
btn.setIconSize({ m_height, m_height });
|
||||||
|
btn.setText(text);
|
||||||
|
btn.setCallback(ogfx::gui::Widget::eCallback::ActionPerformed, callback);
|
||||||
|
btn.enableIconOnly();
|
||||||
|
addWidget(btn, { m_height * (m_buttons.size() - 1) + pad, 0 });
|
||||||
|
pad += 15;
|
||||||
|
return btn;
|
||||||
|
}
|
||||||
|
|
||||||
void ToolBar::onWindowResized(const Event& event)
|
void ToolBar::onWindowResized(const Event& event)
|
||||||
{
|
{
|
||||||
auto& win = cast<Window&>(getWindow());
|
auto& win = cast<Window&>(getWindow());
|
||||||
setSize(win.getWindowWidth(), m_height);
|
w = win.getWindowWidth();
|
||||||
|
h = m_height;
|
||||||
f32 offset_y = 0;
|
f32 offset_y = 0;
|
||||||
if (win.getMenuBar().isVisible())
|
if (win.getMenuBar().isVisible())
|
||||||
offset_y += win.getMenuBar().geth();
|
offset_y += win.getMenuBar().geth();
|
||||||
|
|
@ -59,7 +78,6 @@ namespace ogfx
|
||||||
{
|
{
|
||||||
auto& win = cast<Window&>(getWindow());
|
auto& win = cast<Window&>(getWindow());
|
||||||
setHeight(theme.get<f32>("toolbar.height", getHeight(), {}, {}));
|
setHeight(theme.get<f32>("toolbar.height", getHeight(), {}, {}));
|
||||||
setItemPadding(theme.get<Rectangle>("toolbar.itemPadding", getItemPadding(), {}, {}));
|
|
||||||
setFontSize(theme.get<i32>("toolbar.fontSize", getFontSize(), {}, {}));
|
setFontSize(theme.get<i32>("toolbar.fontSize", getFontSize(), {}, {}));
|
||||||
setBackgroundColor(theme.get<Color>("toolbar.backgroundColor", getBackgroundColor(), {}, {}));
|
setBackgroundColor(theme.get<Color>("toolbar.backgroundColor", getBackgroundColor(), {}, {}));
|
||||||
setTextColor(theme.get<Color>("toolbar.textColor", getTextColor(), {}, {}));
|
setTextColor(theme.get<Color>("toolbar.textColor", getTextColor(), {}, {}));
|
||||||
|
|
@ -80,6 +98,9 @@ namespace ogfx
|
||||||
void ToolBar::onUpdate(void)
|
void ToolBar::onUpdate(void)
|
||||||
{
|
{
|
||||||
if (!m_visible) return;
|
if (!m_visible) return;
|
||||||
|
bool iconsOnly = !isButtonTextEnabled();
|
||||||
|
for (auto& btn : m_buttons)
|
||||||
|
btn.enableIconOnly(iconsOnly);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ToolBar::onMousePressed(const Event& event)
|
void ToolBar::onMousePressed(const Event& event)
|
||||||
|
|
@ -98,5 +119,13 @@ namespace ogfx
|
||||||
Vec2 mousePos { event.mouse->position_x, event.mouse->position_y };
|
Vec2 mousePos { event.mouse->position_x, event.mouse->position_y };
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ToolBar::refresh_size(void)
|
||||||
|
{
|
||||||
|
for (auto& btn : m_buttons)
|
||||||
|
{
|
||||||
|
btn.setIconSize({ m_height, m_height });
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ogfx/gui/widgets/Widget.hpp>
|
#include <ogfx/gui/widgets/Widget.hpp>
|
||||||
|
#include <ogfx/gui/widgets/Button.hpp>
|
||||||
|
#include <deque>
|
||||||
|
|
||||||
namespace ogfx
|
namespace ogfx
|
||||||
{
|
{
|
||||||
|
|
@ -32,6 +34,7 @@ namespace ogfx
|
||||||
public:
|
public:
|
||||||
ToolBar(Window& window);
|
ToolBar(Window& window);
|
||||||
ToolBar& create(void);
|
ToolBar& create(void);
|
||||||
|
widgets::Button& addButton(const String& iconPath, const String& text = "", EventCallback callback = nullptr);
|
||||||
void onWindowResized(const Event& event) override;
|
void onWindowResized(const Event& event) override;
|
||||||
void applyTheme(const ostd::Stylesheet& theme) override;
|
void applyTheme(const ostd::Stylesheet& theme) override;
|
||||||
void onDraw(BasicRenderer2D& gfx) override;
|
void onDraw(BasicRenderer2D& gfx) override;
|
||||||
|
|
@ -39,22 +42,29 @@ namespace ogfx
|
||||||
void onMousePressed(const Event& event) override;
|
void onMousePressed(const Event& event) override;
|
||||||
void onMouseMoved(const Event& event) override;
|
void onMouseMoved(const Event& event) override;
|
||||||
|
|
||||||
|
inline void setw(f32 ww) override { }
|
||||||
|
inline void seth(f32 hh) override { h = hh; refresh_size(); }
|
||||||
|
|
||||||
inline bool isVisible(void) const { return m_visible; }
|
inline bool isVisible(void) const { return m_visible; }
|
||||||
inline void show(bool v = true) { m_visible = v; }
|
inline void show(bool v = true) { m_visible = v; }
|
||||||
inline void hide(void) { show(false); }
|
inline void hide(void) { show(false); }
|
||||||
|
|
||||||
OSTD_PARAM_GETSET(f32, Height, m_height);
|
OSTD_PARAM_GETSET(f32, Height, m_height);
|
||||||
OSTD_PARAM_GETSET(Rectangle, ItemPadding, m_itemPadding);
|
|
||||||
OSTD_PARAM_GETSET(i32, FontSize, m_fontSize);
|
OSTD_PARAM_GETSET(i32, FontSize, m_fontSize);
|
||||||
OSTD_PARAM_GETSET(Color, BackgroundColor, m_backgroundColor);
|
OSTD_PARAM_GETSET(Color, BackgroundColor, m_backgroundColor);
|
||||||
OSTD_PARAM_GETSET(Color, TextColor, m_textColor);
|
OSTD_PARAM_GETSET(Color, TextColor, m_textColor);
|
||||||
OSTD_PARAM_GETSET(Color, BorderColor, m_borderColor);
|
OSTD_PARAM_GETSET(Color, BorderColor, m_borderColor);
|
||||||
|
OSTD_BOOL_PARAM_GETSET_E_NEG(ButtonText, m_disableButtonText);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void refresh_size(void);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_visible { true };
|
bool m_visible { true };
|
||||||
|
bool m_disableButtonText { true };
|
||||||
|
std::deque<widgets::Button> m_buttons;
|
||||||
|
|
||||||
f32 m_height { 26 };
|
f32 m_height { 26 };
|
||||||
Rectangle m_itemPadding { 0, 0, 0, 0 };
|
|
||||||
i32 m_fontSize { 16 };
|
i32 m_fontSize { 16 };
|
||||||
Color m_backgroundColor { "#6B0A1DFF" };
|
Color m_backgroundColor { "#6B0A1DFF" };
|
||||||
Color m_textColor { "#F16A85FF" };
|
Color m_textColor { "#F16A85FF" };
|
||||||
|
|
|
||||||
|
|
@ -31,29 +31,45 @@ namespace ogfx
|
||||||
{
|
{
|
||||||
Button& Button::create(const String& text)
|
Button& Button::create(const String& text)
|
||||||
{
|
{
|
||||||
setText(text);
|
m_text = text;
|
||||||
setPadding({ 5, 5, 5, 5 });
|
setPadding({ 5, 5, 5, 5 });
|
||||||
setTypeName("ogfx::gui::widgets::Button");
|
setTypeName("ogfx::gui::widgets::Button");
|
||||||
disableChildren();
|
disableChildren();
|
||||||
enableBackground(false);
|
enableBackground(false);
|
||||||
setStylesheetCategoryName("button");
|
setStylesheetCategoryName("button");
|
||||||
validate();
|
validate();
|
||||||
|
invalidate_layout();
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Button::onDraw(ogfx::BasicRenderer2D& gfx)
|
void Button::onDraw(ogfx::BasicRenderer2D& gfx)
|
||||||
{
|
{
|
||||||
if (m_textChanged)
|
if (m_layoutDirty)
|
||||||
__update_size();
|
recompute_layout();
|
||||||
if (isIconEnabled())
|
|
||||||
{
|
|
||||||
|
|
||||||
|
const Vec2 contentTL = getGlobalContentPosition();
|
||||||
|
|
||||||
|
// --- Icon ---
|
||||||
|
if (isIconEnabled() && m_drawIconSize.x > 0 && m_drawIconSize.y > 0)
|
||||||
|
{
|
||||||
|
const Vec2 iconPos = contentTL + m_drawIconOffset;
|
||||||
if (isAnimatedEnabled())
|
if (isAnimatedEnabled())
|
||||||
gfx.drawAnimation(m_anim, getGlobalContentPosition(), m_realIconSize);
|
gfx.drawAnimation(m_anim, iconPos, m_drawIconSize);
|
||||||
else
|
else
|
||||||
gfx.drawImage(m_icon, getGlobalContentPosition(), m_realIconSize);
|
gfx.drawImage(m_icon, iconPos, m_drawIconSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Text ---
|
||||||
|
if (!isIconOnlyEnabled() && m_text.len() > 0)
|
||||||
|
{
|
||||||
|
gfx.drawString(
|
||||||
|
m_text,
|
||||||
|
contentTL + m_drawTextOffset,
|
||||||
|
getTextColor(),
|
||||||
|
getFontSize(),
|
||||||
|
m_contentScale
|
||||||
|
);
|
||||||
}
|
}
|
||||||
gfx.drawString(getText(), getGlobalContentPosition() + Vec2 { m_realIconSize.x + m_iconSpacing, 0 }, getTextColor(), getFontSize());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Button::onUpdate(void)
|
void Button::onUpdate(void)
|
||||||
|
|
@ -73,55 +89,167 @@ namespace ogfx
|
||||||
void Button::setText(const String& text)
|
void Button::setText(const String& text)
|
||||||
{
|
{
|
||||||
m_text = text;
|
m_text = text;
|
||||||
m_textChanged = true;
|
invalidate_layout();
|
||||||
__update_size();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Button::setIcon(const String& filePath)
|
void Button::setIcon(const String& filePath)
|
||||||
{
|
{
|
||||||
disableIcon();
|
disableIcon();
|
||||||
if (filePath == "") return;
|
if (filePath == "") { invalidate_layout(); return; }
|
||||||
if (!ostd::FileSystem::fileExists(filePath))
|
if (!ostd::FileSystem::fileExists(filePath)) { invalidate_layout(); return; }
|
||||||
return;
|
|
||||||
m_icon.destroy();
|
m_icon.destroy();
|
||||||
m_icon.loadFromFile(filePath, getWindow().getGFX());
|
m_icon.loadFromFile(filePath, getWindow().getGFX());
|
||||||
enableIcon();
|
enableIcon();
|
||||||
|
invalidate_layout();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Button::applyTheme(const ostd::Stylesheet& theme)
|
void Button::applyTheme(const ostd::Stylesheet& theme)
|
||||||
{
|
{
|
||||||
enableIcon(getThemeValue<bool>(theme, "showIcon", m_showIcon));
|
enableIcon(getThemeValue<bool>(theme, "showIcon", m_showIcon));
|
||||||
|
enableIconOnly(getThemeValue<bool>(theme, "iconOnly", m_iconOnly));
|
||||||
|
enableAutoSize(getThemeValue<bool>(theme, "autoSize", m_autoSize));
|
||||||
enableAnimated(getThemeValue<bool>(theme, "icon.animated", m_animated));
|
enableAnimated(getThemeValue<bool>(theme, "icon.animated", m_animated));
|
||||||
setAnimationData(getThemeValue<AnimationData>(theme, "icon.animation", m_animData));
|
setAnimationData(getThemeValue<AnimationData>(theme, "icon.animation", m_animData));
|
||||||
String filePath = getThemeValue<String>(theme, "icon.path", m_icon.getFilePath());
|
String filePath = getThemeValue<String>(theme, "icon.path", m_icon.getFilePath());
|
||||||
if (filePath != m_icon.getFilePath())
|
if (filePath != m_icon.getFilePath())
|
||||||
setIcon(filePath);
|
setIcon(filePath);
|
||||||
setIconSize(getThemeValue<Vec2>(theme, "icon.size", getIconSize()));
|
setIconSize(getThemeValue<Vec2>(theme, "icon.size", getIconSize()));
|
||||||
|
setIconSpacing(getThemeValue<f32>(theme, "icon.spacing", m_iconSpacing));
|
||||||
if (isAnimatedEnabled())
|
if (isAnimatedEnabled())
|
||||||
{
|
{
|
||||||
m_anim.create(m_animData);
|
m_anim.create(m_animData);
|
||||||
m_anim.setSpriteSheet(m_icon);
|
m_anim.setSpriteSheet(m_icon);
|
||||||
}
|
}
|
||||||
__update_size();
|
invalidate_layout();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Button::__update_size(void)
|
Vec2 Button::native_icon_size(void) const
|
||||||
{
|
{
|
||||||
m_realIconSize = getIconSize().propy(getGlobalContentBounds().getSize().y);
|
if (!isIconEnabled())
|
||||||
if (!isAutoSizeEnabled())
|
return { 0, 0 };
|
||||||
|
|
||||||
|
// Treat font size as the natural icon edge if the user didn't pick one.
|
||||||
|
const f32 fontEdge = cast<f32>(getFontSize());
|
||||||
|
|
||||||
|
Vec2 hint = m_iconSize;
|
||||||
|
if (hint.x <= 0 && hint.y <= 0)
|
||||||
{
|
{
|
||||||
m_textChanged = false;
|
if (m_icon.isLoaded())
|
||||||
return;
|
{
|
||||||
|
// Fit the icon's natural aspect ratio into a font-sized square.
|
||||||
|
Vec2 nat = m_icon.getSize();
|
||||||
|
if (nat.x > 0 && nat.y > 0)
|
||||||
|
{
|
||||||
|
if (nat.x >= nat.y)
|
||||||
|
return nat.propx(fontEdge);
|
||||||
|
return nat.propy(fontEdge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { fontEdge, fontEdge };
|
||||||
}
|
}
|
||||||
auto size = getWindow().getGFX().getStringDimensions(getText(), getFontSize());
|
|
||||||
size.x += getPadding().left();
|
// One axis specified: derive the other from the icon's aspect (if any),
|
||||||
size.x += getPadding().right();
|
// otherwise make it square.
|
||||||
size.y += getPadding().top();
|
if (hint.x <= 0)
|
||||||
size.y += getPadding().bottom();
|
{
|
||||||
if (isIconEnabled())
|
if (m_icon.isLoaded())
|
||||||
size.x += m_iconSpacing + m_realIconSize.x;
|
{
|
||||||
setSize({ cast<f32>(size.x), cast<f32>(size.y) });
|
Vec2 nat = m_icon.getSize();
|
||||||
m_textChanged = false;
|
if (nat.x > 0 && nat.y > 0)
|
||||||
|
return nat.propy(hint.y);
|
||||||
|
}
|
||||||
|
return { hint.y, hint.y };
|
||||||
|
}
|
||||||
|
if (hint.y <= 0)
|
||||||
|
{
|
||||||
|
if (m_icon.isLoaded())
|
||||||
|
{
|
||||||
|
Vec2 nat = m_icon.getSize();
|
||||||
|
if (nat.x > 0 && nat.y > 0)
|
||||||
|
return nat.propx(hint.x);
|
||||||
|
}
|
||||||
|
return { hint.x, hint.x };
|
||||||
|
}
|
||||||
|
return hint;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Button::recompute_layout(void)
|
||||||
|
{
|
||||||
|
m_layoutDirty = false;
|
||||||
|
|
||||||
|
auto& gfx = getWindow().getGFX();
|
||||||
|
const Rectangle pad = getPadding();
|
||||||
|
const f32 padW = pad.left() + pad.right();
|
||||||
|
const f32 padH = pad.top() + pad.bottom();
|
||||||
|
|
||||||
|
// 1) Native (un-scaled) sizes of the pieces.
|
||||||
|
const Vec2 iconNative = native_icon_size();
|
||||||
|
const bool hasIcon = isIconEnabled() && (iconNative.x > 0 && iconNative.y > 0);
|
||||||
|
const bool hasText = !isIconOnlyEnabled() && m_text.len() > 0;
|
||||||
|
const Vec2 textNative = hasText
|
||||||
|
? gfx.getStringDimensions(m_text, getFontSize())
|
||||||
|
: Vec2 { 0, 0 };
|
||||||
|
|
||||||
|
// 2) Native content extents (icon + spacing + text), no padding yet.
|
||||||
|
f32 nativeW = 0;
|
||||||
|
f32 nativeH = 0;
|
||||||
|
if (hasIcon)
|
||||||
|
{
|
||||||
|
nativeW += iconNative.x;
|
||||||
|
nativeH = std::max(nativeH, iconNative.y);
|
||||||
|
}
|
||||||
|
if (hasText)
|
||||||
|
{
|
||||||
|
if (hasIcon) nativeW += m_iconSpacing;
|
||||||
|
nativeW += textNative.x;
|
||||||
|
nativeH = std::max(nativeH, textNative.y);
|
||||||
|
}
|
||||||
|
if (nativeW <= 0) nativeW = cast<f32>(getFontSize()); // degenerate fallback
|
||||||
|
if (nativeH <= 0) nativeH = cast<f32>(getFontSize());
|
||||||
|
|
||||||
|
// 3) Decide the widget size and the content scale.
|
||||||
|
if (isAutoSizeEnabled())
|
||||||
|
{
|
||||||
|
m_contentScale = 1.0f;
|
||||||
|
setSize({ nativeW + padW, nativeH + padH });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Honor the size the user already set on the widget.
|
||||||
|
const f32 availW = std::max(0.0f, getw() - padW);
|
||||||
|
const f32 availH = std::max(0.0f, geth() - padH);
|
||||||
|
if (availW <= 0 || availH <= 0)
|
||||||
|
m_contentScale = 0.0f;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const f32 sx = availW / nativeW;
|
||||||
|
const f32 sy = availH / nativeH;
|
||||||
|
m_contentScale = std::min(sx, sy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4) Compute scaled draw sizes and centered offsets within the content area.
|
||||||
|
const Vec2 contentSize = getContentBounds().getSize();
|
||||||
|
const Vec2 iconScaled = hasIcon ? iconNative * m_contentScale : Vec2 { 0, 0 };
|
||||||
|
const Vec2 textScaled = hasText ? textNative * m_contentScale : Vec2 { 0, 0 };
|
||||||
|
const f32 spacing = (hasIcon && hasText) ? (m_iconSpacing * m_contentScale) : 0.0f;
|
||||||
|
|
||||||
|
const f32 totalW = iconScaled.x + spacing + textScaled.x;
|
||||||
|
const f32 startX = std::max(0.0f, (contentSize.x - totalW) * 0.5f);
|
||||||
|
|
||||||
|
m_drawIconSize = iconScaled;
|
||||||
|
if (hasIcon)
|
||||||
|
m_drawIconOffset = { startX, std::max(0.0f, (contentSize.y - iconScaled.y) * 0.5f) };
|
||||||
|
else
|
||||||
|
m_drawIconOffset = { 0, 0 };
|
||||||
|
|
||||||
|
if (hasText)
|
||||||
|
m_drawTextOffset = {
|
||||||
|
startX + iconScaled.x + spacing,
|
||||||
|
std::max(0.0f, (contentSize.y - textScaled.y) * 0.5f)
|
||||||
|
};
|
||||||
|
else
|
||||||
|
m_drawTextOffset = { 0, 0 };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,28 +44,60 @@ namespace ogfx
|
||||||
void applyTheme(const ostd::Stylesheet& theme) override;
|
void applyTheme(const ostd::Stylesheet& theme) override;
|
||||||
inline String getText(void) const { return m_text; }
|
inline String getText(void) const { return m_text; }
|
||||||
inline Image& getIcon(void) { return m_icon; }
|
inline Image& getIcon(void) { return m_icon; }
|
||||||
OSTD_BOOL_PARAM_GETSET_E(Animated, m_animated);
|
|
||||||
|
// When auto-size is enabled, the widget resizes itself to fit text+icon+padding
|
||||||
|
// at native font/icon size (text is drawn at scale 1.0).
|
||||||
|
// When auto-size is disabled, the widget keeps its user-defined size, and the
|
||||||
|
// content is scaled uniformly down/up to fit that size (text uses the renderer's
|
||||||
|
// scale parameter; icon is scaled proportionally).
|
||||||
OSTD_BOOL_PARAM_GETSET_E(AutoSize, m_autoSize);
|
OSTD_BOOL_PARAM_GETSET_E(AutoSize, m_autoSize);
|
||||||
|
|
||||||
|
// Icon-only mode: text is ignored when laying out and drawing.
|
||||||
|
// Combined with auto-size, the button becomes a square based on font size.
|
||||||
|
// Combined with manual size, the icon scales to fit the content area.
|
||||||
|
OSTD_BOOL_PARAM_GETSET_E(IconOnly, m_iconOnly);
|
||||||
|
|
||||||
|
OSTD_BOOL_PARAM_GETSET_E(Animated, m_animated);
|
||||||
OSTD_BOOL_PARAM_GETSET_E(Icon, m_showIcon);
|
OSTD_BOOL_PARAM_GETSET_E(Icon, m_showIcon);
|
||||||
OSTD_PARAM_GETSET(ostd::AnimationData, AnimationData, m_animData);
|
OSTD_PARAM_GETSET(ostd::AnimationData, AnimationData, m_animData);
|
||||||
OSTD_PARAM_GETSET(Vec2, IconSize, m_iconSize);
|
OSTD_PARAM_GETSET(Vec2, IconSize, m_iconSize);
|
||||||
|
OSTD_PARAM_GETSET(f32, IconSpacing, m_iconSpacing);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void __update_size(void);
|
// Force a layout recompute on the next draw.
|
||||||
|
inline void invalidate_layout(void) { m_layoutDirty = true; }
|
||||||
|
|
||||||
|
// Recomputes layout. In auto-size mode this also resizes the widget.
|
||||||
|
// In manual-size mode this computes the scale factor that fits content
|
||||||
|
// into the current widget size.
|
||||||
|
void recompute_layout(void);
|
||||||
|
|
||||||
|
// Returns the icon size at native (un-scaled) resolution. If m_iconSize is
|
||||||
|
// (0,0) and an icon is loaded, falls back to the icon's natural size; if no
|
||||||
|
// icon is loaded, falls back to font-size-based square. Aspect ratio of
|
||||||
|
// m_iconSize is preserved.
|
||||||
|
Vec2 native_icon_size(void) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
String m_text { "" };
|
String m_text { "" };
|
||||||
bool m_textChanged { true };
|
|
||||||
Image m_icon;
|
Image m_icon;
|
||||||
Vec2 m_realIconSize { 0, 0 };
|
|
||||||
bool m_autoSize { true };
|
|
||||||
|
|
||||||
bool m_showIcon { false };
|
|
||||||
ostd::AnimationData m_animData;
|
ostd::AnimationData m_animData;
|
||||||
Animation m_anim;
|
Animation m_anim;
|
||||||
bool m_animated { false };
|
|
||||||
f32 m_iconSpacing { 10 };
|
|
||||||
Vec2 m_iconSize { 0, 0 };
|
Vec2 m_iconSize { 0, 0 };
|
||||||
|
f32 m_iconSpacing { 10 };
|
||||||
|
|
||||||
|
// Computed layout state (refreshed by recompute_layout).
|
||||||
|
bool m_layoutDirty { true };
|
||||||
|
f32 m_contentScale { 1.0f }; // applied to text & icon when !m_autoSize
|
||||||
|
Vec2 m_drawIconSize { 0, 0 }; // final on-screen icon size
|
||||||
|
Vec2 m_drawIconOffset { 0, 0 }; // top-left of the icon inside the content area
|
||||||
|
Vec2 m_drawTextOffset { 0, 0 }; // top-left of the text inside the content area
|
||||||
|
|
||||||
|
// Flags
|
||||||
|
bool m_autoSize { true };
|
||||||
|
bool m_iconOnly { false };
|
||||||
|
bool m_showIcon { false };
|
||||||
|
bool m_animated { false };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,8 @@ class Window : public ogfx::gui::Window
|
||||||
m_img.enableAnimated();
|
m_img.enableAnimated();
|
||||||
m_img.setSize(256, 256);
|
m_img.setSize(256, 256);
|
||||||
|
|
||||||
|
getToolBar().setHeight(30);
|
||||||
|
|
||||||
m_label1.setText("Show Panel2");
|
m_label1.setText("Show Panel2");
|
||||||
m_label1.setCallback(ogfx::gui::Widget::eCallback::MousePressed, [&](const ogfx::gui::Event& event) -> void {
|
m_label1.setCallback(ogfx::gui::Widget::eCallback::MousePressed, [&](const ogfx::gui::Event& event) -> void {
|
||||||
m_check1.setChecked(!m_check1.isChecked());
|
m_check1.setChecked(!m_check1.isChecked());
|
||||||
|
|
@ -66,16 +68,6 @@ class Window : public ogfx::gui::Window
|
||||||
m_label1.enableBackground();
|
m_label1.enableBackground();
|
||||||
m_label1.reloadTheme();
|
m_label1.reloadTheme();
|
||||||
|
|
||||||
m_check1.setText("Check this out!");
|
|
||||||
m_check1.setChecked(true);
|
|
||||||
m_check1.setStateChangedCallback([&](CheckBox& sender, bool state) -> void {
|
|
||||||
m_panel1.setVisible(state);
|
|
||||||
});
|
|
||||||
|
|
||||||
m_btn1.setText("BUTTON");
|
|
||||||
m_btn1.setCallback(ogfx::gui::Widget::eCallback::ActionPerformed, [&](const ogfx::gui::Event& event) -> void {
|
|
||||||
std::cout << showOpenFileDialog(filters) << "\n";
|
|
||||||
});
|
|
||||||
m_btn1.addThemeOverride("@.button.showIcon", true);
|
m_btn1.addThemeOverride("@.button.showIcon", true);
|
||||||
m_btn1.setAnimationData(ad);
|
m_btn1.setAnimationData(ad);
|
||||||
m_btn1.setIcon("./img.png");
|
m_btn1.setIcon("./img.png");
|
||||||
|
|
@ -83,6 +75,16 @@ class Window : public ogfx::gui::Window
|
||||||
m_btn1.enableAnimated();
|
m_btn1.enableAnimated();
|
||||||
m_btn1.enableTooltip();
|
m_btn1.enableTooltip();
|
||||||
m_btn1.setTooltipText("Test tooltip");
|
m_btn1.setTooltipText("Test tooltip");
|
||||||
|
m_btn1.setText("TEST BUTTON");
|
||||||
|
m_btn1.setCallback(ogfx::gui::Widget::eCallback::ActionPerformed, [&](const ogfx::gui::Event& event) -> void {
|
||||||
|
std::cout << showOpenFileDialog(filters) << "\n";
|
||||||
|
});
|
||||||
|
|
||||||
|
m_check1.setText("Check this out!");
|
||||||
|
m_check1.setChecked(true);
|
||||||
|
m_check1.setStateChangedCallback([&](CheckBox& sender, bool state) -> void {
|
||||||
|
m_panel1.setVisible(state);
|
||||||
|
});
|
||||||
|
|
||||||
m_label2.setText("Label2");
|
m_label2.setText("Label2");
|
||||||
m_label3.setText("Label3");
|
m_label3.setText("Label3");
|
||||||
|
|
@ -132,6 +134,13 @@ class Window : public ogfx::gui::Window
|
||||||
std::cout << *(selection[0]) << "\n";
|
std::cout << *(selection[0]) << "\n";
|
||||||
});
|
});
|
||||||
|
|
||||||
|
for (i32 i = 0; i < 20; i++)
|
||||||
|
{
|
||||||
|
auto& btn = getToolBar().addButton("./img.png");
|
||||||
|
btn.setAnimationData(ad);
|
||||||
|
btn.enableAnimated();
|
||||||
|
}
|
||||||
|
|
||||||
m_tabs.setSize(900, 700);
|
m_tabs.setSize(900, 700);
|
||||||
auto& t1 = m_tabs.addTab("Tab1");
|
auto& t1 = m_tabs.addTab("Tab1");
|
||||||
auto& t2 = m_tabs.addTab("Tab2 Test");
|
auto& t2 = m_tabs.addTab("Tab2 Test");
|
||||||
|
|
@ -163,12 +172,7 @@ class Window : public ogfx::gui::Window
|
||||||
m_panel2.addWidget(m_label3);
|
m_panel2.addWidget(m_label3);
|
||||||
m_panel2.addWidget(m_panel1, { 400, 50 });
|
m_panel2.addWidget(m_panel1, { 400, 50 });
|
||||||
m_panel2.addWidget(m_label1, { 0, 500 });
|
m_panel2.addWidget(m_label1, { 0, 500 });
|
||||||
// m_panel2.addWidget(m_btn1, { 0, 300 });
|
m_panel2.addWidget(m_btn1, { 0, 300 });
|
||||||
m_btn1.addThemeID("tool_button");
|
|
||||||
m_btn1.seth(26);
|
|
||||||
m_btn1.setText(" ");
|
|
||||||
m_btn1.disableAutoSize();
|
|
||||||
getToolBar().addWidget(m_btn1, { 0, 0 });
|
|
||||||
m_panel2.addWidget(m_img, { 20, 50 });
|
m_panel2.addWidget(m_img, { 20, 50 });
|
||||||
|
|
||||||
addWidget(m_tabs, { 0, 0 });
|
addWidget(m_tabs, { 0, 0 });
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue