Sync push

This commit is contained in:
OmniaX-Dev 2026-04-25 18:11:48 +02:00
parent f00f30004c
commit c47782280b
7 changed files with 284 additions and 61 deletions

View file

@ -1,5 +1,4 @@
CompileFlags: CompileFlags:
CompilationDatabase: bin CompilationDatabase: bin
Add: ["--target=x86_64-w64-mingw32"]
Diagnostics: Diagnostics:
MissingIncludes: None MissingIncludes: None

View file

@ -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
} }

View file

@ -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 });
}
}
} }
} }

View file

@ -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" };

View file

@ -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 };
} }
} }
} }

View file

@ -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 };
}; };
} }
} }

View file

@ -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 });