Replies: 6 comments 3 replies
-
First optionUse inheritance and use a per component Style subclass. Every component will define a style inner class which will form a big Style class with all the styles from all components. Components will just use the main singleton Style instance, which will be set globally (reference counted). class Slider : public Component
{
class Style
{
virtual void paintSlider (Graphics& g, Slider& slider, ...) = 0;
};
};
class CustomStyle : public Slider::Style, ...
{
void paintSlider (Graphics& g, Slider& slider, ...) override
{
// impl
}
};
// Usage
auto customStyle = new CustomStyle();
auto slider = new Slider();
slider->setStyle (customStyle); // OR Style::setDefaultStyle (customStyle) pros:
cons:
|
Beta Was this translation helpful? Give feedback.
-
Second optionUse composition via function pointers. Global styles should compose function pointers coming from components, class Slider : public Component
{
struct
{
void (*onPaintSlider)(Graphics& g, Slider& slider, ...) = nullptr;
};
};
void CustomStyle_paintSlider (Graphics& g, Slider& slider, ...) override
{
// impl
}
// Usage
auto slider = new Slider();
slider->onPaintSlider = &CustomStyle_paintSlider; // OR Style::onPaintSlider = &CustomStyle_paintSlider; pros:
cons:
|
Beta Was this translation helpful? Give feedback.
-
Third OptionUse std::function composition. #include <memory>
#include <functional>
#include <iostream>
class Graphics{};
class Component
{
public:
virtual void paint (Graphics& g) = 0;
};
class Slider : public Component
{
public:
void paint (Graphics& g) override;
struct Style
{
std::function<void (Graphics&, Slider&)> onPaintSlider;
};
void setStyle (Style* s) { style = s; }
private:
Style* style = nullptr;
};
struct GlobalStyle final
{
Slider::Style sliderStyle;
template <class T>
static typename T::Style& getComponentStyle (typename T::Style*);
static void setGlobalStyle (std::shared_ptr<GlobalStyle> s)
{
getGlobalStyleInstance() = s;
}
static std::shared_ptr<const GlobalStyle> getGlobalStyle()
{
return getGlobalStyleInstance();
}
private:
static std::shared_ptr<GlobalStyle>& getGlobalStyleInstance()
{
static std::shared_ptr<GlobalStyle> globalStyle;
return globalStyle;
}
};
template <>
Slider::Style& GlobalStyle::getComponentStyle<Slider> (typename Slider::Style* instanceStyle)
{
return instanceStyle ? *instanceStyle : getGlobalStyleInstance()->sliderStyle;
}
void Slider::paint(Graphics& g)
{
GlobalStyle::getComponentStyle<Slider> (style).onPaintSlider (g, *this);
}
std::shared_ptr<GlobalStyle> createDefaultStyle()
{
auto defaultStyle = std::make_shared<GlobalStyle>();
defaultStyle->sliderStyle =
{
[](Graphics&, Slider&)
{
std::cout << "global style" << '\n';
}
};
return defaultStyle;
}
auto customSliderStyle = Slider::Style
{
[](Graphics&, Slider&)
{
std::cout << "custom style" << '\n';
}
};
int main()
{
GlobalStyle::setGlobalStyle (createDefaultStyle());
auto slider = new Slider();
slider->setStyle(&customSliderStyle);
//slider->setStyle(&GlobalStyle::getGlobalStyle()->sliderStyle);
Graphics g{};
slider->paint(g);
} https://godbolt.org/z/6fPecPKKE Using tuple to ease getting and setting styles: #include <memory>
#include <functional>
#include <iostream>
#include <tuple>
class Graphics{};
class Component
{
public:
virtual void paint (Graphics& g) = 0;
};
// -- An example slider implementation
class Slider : public Component
{
public:
virtual ~Slider() = default;
void paint (Graphics& g) override;
struct Theme
{
std::function<void (Graphics&, Slider&)> onPaintSlider;
};
void setTheme (const Theme* t)
{
theme = t;
}
private:
const Theme* theme = nullptr; // How to make safe ? Refcount ?
};
// -- The global theme class, which controls application wide themes
struct ApplicationTheme final
{
std::tuple<
Slider::Theme
> componentThemes;
template <class T>
void setComponentTheme (T&& instanceTheme)
{
std::get<T>(componentThemes) = std::forward<T>(instanceTheme);
}
template <class T>
static const T& resolveTheme (const T* instanceTheme = nullptr)
{
return instanceTheme != nullptr
? *instanceTheme
: std::get<T>(getGlobalThemeInstance()->componentThemes);
}
static void setGlobalTheme (std::shared_ptr<ApplicationTheme> s)
{
getGlobalThemeInstance() = std::move(s);
}
static std::shared_ptr<const ApplicationTheme> getGlobalTheme()
{
return getGlobalThemeInstance();
}
private:
static std::shared_ptr<ApplicationTheme>& getGlobalThemeInstance()
{
static std::shared_ptr<ApplicationTheme> globalTheme;
return globalTheme;
}
};
// -- Example of a component theme usage
void Slider::paint(Graphics& g)
{
// This is a helper that will resolve the theme
// - If `theme` is present in the current component, use it
// - Otherwise fallback to the global theme
ApplicationTheme::resolveTheme (theme).onPaintSlider (g, *this);
}
// -- Creating themes
std::shared_ptr<ApplicationTheme> createDefaultTheme()
{
auto t = std::make_shared<ApplicationTheme>();
t->setComponentTheme<Slider::Theme>
({
[](Graphics&, Slider&)
{
std::cout << "global theme v1" << '\n';
}
});
return t;
}
std::shared_ptr<ApplicationTheme> createDefaultThemeV2()
{
auto t = createDefaultTheme();
t->setComponentTheme<Slider::Theme>
({
[](Graphics&, Slider&)
{
std::cout << "global theme v2" << '\n';
}
});
return t;
}
int main()
{
ApplicationTheme::setGlobalTheme (createDefaultThemeV2());
auto customSliderTheme = Slider::Theme
{
[](Graphics&, Slider&)
{
std::cout << "custom theme" << '\n';
}
};
auto slider = new Slider();
//slider->setTheme(&customSliderTheme);
//slider->setTheme(&ApplicationTheme::resolveTheme<Slider::Theme>());
Graphics g{};
slider->paint(g);
delete slider;
} https://godbolt.org/z/oEoY568cn pros:
cons:
|
Beta Was this translation helpful? Give feedback.
-
Some ideas on the naming:
i'm leaning towards using the |
Beta Was this translation helpful? Give feedback.
-
@kunitoki I like the std::function approach. Perhaps some benchmarking might be good to see what actual impact it might have on performance. I also think a declarative style could be considered, for example a JSON based stylesheet. The std::function would then be needed only for custom drawing. |
Beta Was this translation helpful? Give feedback.
-
I would like to design a flexible and easily extensible styling infrastructure for the components. Something along the lines of the look and feel of juce, but fixing some of its shortcomings, like the inability to compose different drawing methods from different styles easily.
Beta Was this translation helpful? Give feedback.
All reactions