Loading...
Searching...
No Matches
TabbedPanel.h
Go to the documentation of this file.
1//
2// TabbedPanel.h
3//
4
5#ifndef MALENA_TABBEDPANEL_H
6#define MALENA_TABBEDPANEL_H
7
8#pragma once
9
12#include <Malena/Core/Core.h>
22#include <functional>
23#include <memory>
24#include <string>
25#include <vector>
26#include <type_traits>
29
30namespace ml
31{
33 {
34 public:
35 enum class Flag {};
36 enum class State {};
37 };
38
78 class MALENA_API TabbedPanel : public ComponentWith<TabbedPanelManifest>,
80 public TabbedPanelTheme,
81 public Themeable
82 {
83 public:
87
88 private:
89 struct Tab
90 {
91 std::string label;
92 std::unique_ptr<ml::Core> content;
93 std::function<void(sf::Vector2f)> resizeFn;
94 const sf::Texture* icon = nullptr;
95 bool closeable = false;
96 float x = 0.f;
97 float width = 0.f;
98 };
99
100 std::vector<Tab> _tabs;
101 int _activeIdx = -1;
102 int _hoveredIdx = -1;
103
104 sf::Vector2f _position = {0.f, 0.f};
105 sf::Vector2f _size = {400.f, 300.f};
106
107 // Content whose unique_ptr was transferred out during event dispatch is
108 // held here until the next onUpdate tick, preventing use-after-free when
109 // the EventManager's CLICK loop continues past a just-closed tab.
110 std::vector<std::unique_ptr<ml::Core>> _pendingDelete;
111
112 std::function<void(std::size_t, const std::string&)> _onTabChanged;
113 std::function<void(std::size_t, const std::string&)> _onTabClosed;
114
115 // ── Internal ──────────────────────────────────────────────────────────
116 void computeTabLayout();
117 int hitTestStrip(const sf::Vector2f& wp) const;
118 bool hitTestClose(int tabIdx, const sf::Vector2f& wp) const;
119
120 // Geometry helpers
121 sf::FloatRect stripRect() const;
122 sf::FloatRect contentRect() const;
123 sf::Vector2f stripAxis() const;
124
125 void drawStrip(sf::RenderTarget& target, const sf::RenderStates& states) const;
126 void drawTab(sf::RenderTarget& target, const sf::RenderStates& states,
127 int idx, bool active, bool hovered) const;
128
129 protected:
130 void draw(sf::RenderTarget& target, sf::RenderStates states) const override;
131 void onThemeApplied(const Theme& theme) override;
132
133 public:
135
136 TabbedPanel(const TabbedPanel&) = delete;
138
139 // ── Apply ─────────────────────────────────────────────────────────────
140
141 template<typename S>
142 void applySettings(const S& s)
143 {
144 static_assert(std::is_base_of_v<TabbedPanelSettings, S>,
145 "applySettings() requires TabbedPanelSettings");
146 static_cast<TabbedPanelSettings&>(*this) = s;
147 computeTabLayout();
148 }
149
150 template<typename T>
151 void applyTheme(const T& t)
152 {
153 static_assert(std::is_base_of_v<TabbedPanelTheme, T>,
154 "applyTheme() requires TabbedPanelTheme");
155 static_cast<TabbedPanelTheme&>(*this) = t;
156 }
157
158 template<typename St>
159 void applyStyle(const St& s)
160 {
161 static_assert(std::is_base_of_v<TabbedPanelSettings, St> &&
162 std::is_base_of_v<TabbedPanelTheme, St>,
163 "applyStyle() requires TabbedPanelSettings and TabbedPanelTheme");
164 static_cast<TabbedPanelSettings&>(*this) = s;
165 static_cast<TabbedPanelTheme&>(*this) = s;
166 computeTabLayout();
167 }
168
169 // ── Tab management ────────────────────────────────────────────────────
170
186 template<typename T>
187 T& addTab(const std::string& label,
188 std::unique_ptr<T> content,
189 const sf::Texture* icon = nullptr,
190 bool closeable = false)
191 {
192 static_assert(std::is_base_of_v<ml::Core, T>,
193 "addTab() content must derive from ml::Core");
194
195 T* ptr = content.get();
196
197 Tab tab;
198 tab.label = label;
199 tab.content = std::move(content);
200 tab.icon = icon;
201 tab.closeable = closeable;
202
203 if constexpr (detail::has_setSize<T>::value)
204 tab.resizeFn = [ptr](sf::Vector2f sz){ ptr->setSize(sz); };
205
206 _tabs.push_back(std::move(tab));
207 computeTabLayout();
208
209 if (_activeIdx < 0)
210 selectTab(0);
211
212 return *ptr;
213 }
214
222 void removeTab(std::size_t index);
223
225 void selectTab(std::size_t index);
226
228 [[nodiscard]] int activeTab() const { return _activeIdx; }
229
231 [[nodiscard]] std::size_t tabCount() const { return _tabs.size(); }
232
245 [[nodiscard]] sf::Vector2f contentSize() const;
246
247 // ── Callbacks ─────────────────────────────────────────────────────────
248
250 void onTabChanged(std::function<void(std::size_t, const std::string&)> cb);
251
253 void onTabClosed(std::function<void(std::size_t, const std::string&)> cb);
254
255 // ── Size / position ───────────────────────────────────────────────────
256
257 void setSize(const sf::Vector2f& size);
258 [[nodiscard]] sf::Vector2f getSize() const { return _size; }
259
260 void setPosition(const sf::Vector2f& position) override;
261 sf::Vector2f getPosition() const override;
263 };
264
265 template<typename MANIFEST>
266 class TabbedPanelWith : public TabbedPanel, public Customizable<MANIFEST>
267 { public: using TabbedPanel::TabbedPanel; };
268
269} // namespace ml
270#endif // MALENA_TABBEDPANEL_H
static const sf::Font & getDefault()
Return the built-in Arial font.
Base class for all Malena manifests.
Definition Manifest.h:51
T & addTab(const std::string &label, std::unique_ptr< T > content, const sf::Texture *icon=nullptr, bool closeable=false)
Add a tab with owned content.
TabbedPanelSettings::TabPosition TabPosition
Definition TabbedPanel.h:86
sf::FloatRect getGlobalBounds() const override
TabbedPanel(const TabbedPanel &)=delete
void applySettings(const S &s)
TabbedPanel & operator=(const TabbedPanel &)=delete
sf::Vector2f contentSize() const
Return the content area size.
void draw(sf::RenderTarget &target, sf::RenderStates states) const override
TabbedPanel(const sf::Font &font=FontManager<>::getDefault())
TabbedPanelManifest::State State
Definition TabbedPanel.h:85
void onTabClosed(std::function< void(std::size_t, const std::string &)> cb)
Fired when a tab is closed via the × button.
void selectTab(std::size_t index)
Programmatically select a tab by index.
void applyStyle(const St &s)
TabbedPanelManifest::Flag Flag
Definition TabbedPanel.h:84
sf::Vector2f getPosition() const override
void onThemeApplied(const Theme &theme) override
Called by ThemeManager when the active theme changes.
int activeTab() const
Return the currently active tab index, or -1 if no tabs.
std::size_t tabCount() const
Return the number of tabs.
sf::Vector2f getSize() const
void setPosition(const sf::Vector2f &position) override
void setSize(const sf::Vector2f &size)
void onTabChanged(std::function< void(std::size_t, const std::string &)> cb)
Fired when the active tab changes.
void removeTab(std::size_t index)
Remove a tab by index.
void applyTheme(const T &t)
TabbedPanel(const sf::Font &font=FontManager<>::getDefault())
Component< M, Traits... > ComponentWith
Alias for Component<M, Traits...>.
Definition Component.h:299
#define MALENA_API
Definition Component.h:22
Rect< float > FloatRect
Vector2< float > Vector2f
const sf::Font * font
bool closeable
show × on all tabs by default
Universal design token set applied across all Themeable components.
Definition Theme.h:70