From 5ccfd07c986fd9c2d415d5e0fd59b0a81e5e599b Mon Sep 17 00:00:00 2001 From: Brisk Date: Fri, 20 Dec 2024 08:41:07 +0100 Subject: [PATCH 1/9] Exception safety revisited --- README.md | 20 +-- docs/docs/about.md | 52 +++--- docs/docs/hello_world.md | 6 +- examples/calc/calc.cpp | 64 +++---- examples/showcase/src/Binding.cpp | 66 +++---- examples/showcase/src/Buttons.cpp | 190 ++++++++++---------- examples/showcase/src/Dialogs.cpp | 50 +++--- examples/showcase/src/Dropdowns.cpp | 143 +++++++-------- examples/showcase/src/Editors.cpp | 113 ++++++------ examples/showcase/src/Layout.cpp | 8 +- examples/showcase/src/Messenger.cpp | 24 +-- examples/showcase/src/ShowcaseComponent.cpp | 40 ++--- examples/showcase/src/Typography.cpp | 48 ++--- examples/showcase/src/Visual.cpp | 147 +++++++-------- include/brisk/gui/GUI.hpp | 15 +- include/brisk/widgets/Color.hpp | 2 +- include/brisk/widgets/ComboBox.hpp | 6 +- include/brisk/widgets/Notifications.hpp | 4 +- include/brisk/widgets/Text.hpp | 8 +- src/gui/GUI.cpp | 13 +- src/gui/Styles_test.cpp | 26 +-- src/widgets/Color.cpp | 26 +-- src/widgets/ComboBox.cpp | 6 +- src/widgets/ContextPopup.cpp | 2 +- src/widgets/DialogComponent.cpp | 40 ++--- src/widgets/ItemList.cpp | 2 +- src/widgets/ListBox.cpp | 2 +- src/widgets/Notifications.cpp | 6 +- src/widgets/Pages.cpp | 2 +- src/widgets/PopupDialog.cpp | 26 +-- src/widgets/Progress.cpp | 2 +- src/widgets/SpinBox.cpp | 4 +- src/widgets/TextEditor.cpp | 102 +++++------ 33 files changed, 632 insertions(+), 633 deletions(-) diff --git a/README.md b/README.md index ca8892a..e7d94ca 100644 --- a/README.md +++ b/README.md @@ -47,21 +47,21 @@ public: // rcnew Widget{...} is equivalent to std::shared_ptr(new Widget{...}) return rcnew Widget{ layout = Layout::Vertical, - new Text{ + rcnew Text{ "Switch (widgets/Switch.hpp)", classes = { "section-header" }, // Widgets can be styled using stylesheets }, - new HLayout{ - new Widget{ - new Switch{ + rcnew HLayout{ + rcnew Widget{ + rcnew Switch{ // Bind the switch value to the m_toggled variable (bidirectional) value = Value{ &m_toggled }, - new Text{ "Switch" }, + rcnew Text{ "Switch" }, }, }, gapColumn = 10_apx, // CSS Flex-like properties - new Text{ + rcnew Text{ text = Value{ &m_label }, // Text may be dynamic visible = Value{ &m_toggled }, // The Switch widget controls the visibility of this text widget @@ -69,8 +69,8 @@ public: }, // Button widget - new Button{ - new Text{ "Click" }, + rcnew Button{ + rcnew Text{ "Click" }, // Using m_lifetime ensures that callbacks will be detached once the Component is deleted onClick = m_lifetime | [this]() { @@ -80,7 +80,7 @@ public: }, // ComboBox widget - new ComboBox{ + rcnew ComboBox{ Value{ &m_textAlignment }, // Bind ComboBox value to an enumeration notManaged(&textAlignList), // Pass the list of name-value pairs to populate the ComboBox }, @@ -88,7 +88,7 @@ public: // The Builder creates widgets dynamically whenever needed Builder([this](Widget* target) { for (int i = 0; i < m_number; ++i) { - target->apply(new Widget{ + target->apply(rcnew Widget{ dimensions = { 40_apx, 40_apx }, }); } diff --git a/docs/docs/about.md b/docs/docs/about.md index c426b17..1272bfa 100644 --- a/docs/docs/about.md +++ b/docs/docs/about.md @@ -25,11 +25,11 @@ Brisk eliminates the need for markup languages in defining user interfaces. Inst For example: ```c++ // Label with text defined at creation -Widget* makeLabel(std::string text) { - return new Widget{ +RC makeLabel(std::string text) { + return rcnew Widget{ padding = 4_apx, classes = { "label" }, - new Text{ std::move(text) }, + rcnew Text{ std::move(text) }, }; } ``` @@ -40,9 +40,9 @@ Here’s an example of creating a `Slider` widget, accompanied by a `Text` widge ```c++ // Slider with a dynamic value display -Widget* makeSlider(float& value) { - return new HLayout{ - new Slider{ +RC makeSlider(float& value) { + return rcnew HLayout{ + rcnew Slider{ // Bind the value of the slider to the provided 'value' variable. value = Value{ &value }, minimum = 0.f, @@ -58,7 +58,7 @@ Widget* makeSlider(float& value) { gapColumn = 10_px, // Create a Text widget to display the value of the slider. - new Text{ + rcnew Text{ // The text is dynamically generated based on the slider value. text = Value{ &value }.transform([](float v) { return fmt::format("Value: {:.1f}", v); @@ -82,8 +82,8 @@ In the example below, the `Text` widget binds its `text` property to a `temperat // Temperature widget with dynamic text and color float temperature = 16.f; -Widget* makeTemperatureWidget() { - return new Text{ +RC makeTemperatureWidget() { + return rcnew Text{ text = Value{ &temperature }.transform([](float t){ return fmt::format("{:.1f}°C", t); }), @@ -109,18 +109,18 @@ In addition to supporting dynamic widget properties, Brisk allows the entire wid // Dynamically created widget tree static int count = 1; -Widget* makeTree() { - return new VLayout{ - new Text{ "Squares:" }, +RC makeTree() { + return rcnew VLayout{ + rcnew Text{ "Squares:" }, Builder{ [](Widget* target){ for (int i = 1; i <= count; ++i) { - target->apply(new Text{ fmt::format("{}^2 = {}", i, i * i) }); + target->apply(rcnew Text{ fmt::format("{}^2 = {}", i, i * i) }); } }}, depends = Value{ &count }, - new Button{ - new Text{ "Next" }, + rcnew Button{ + rcnew Text{ "Next" }, onClick = [](){ bindings->assign(count, count + 1); }, @@ -170,7 +170,7 @@ For example, the `Text` widget is highly configurable with respect to font featu ```c++ // Configuring text with OpenType features -new Text{ +rcnew Text{ text = Value{ &m_text }, fontSize = 40, fontFamily = Lato, @@ -200,21 +200,21 @@ public: // rcnew Widget{...} is equivalent to std::shared_ptr(new Widget{...}) return rcnew Widget{ layout = Layout::Vertical, - new Text{ + rcnew Text{ "Switch (widgets/Switch.hpp)", classes = { "section-header" }, // Widgets can be styled using stylesheets }, - new HLayout{ - new Widget{ - new Switch{ + rcnew HLayout{ + rcnew Widget{ + rcnew Switch{ // Bind the switch value to the m_toggled variable (bidirectional) value = Value{ &m_toggled }, - new Text{ "Switch" }, + rcnew Text{ "Switch" }, }, }, gapColumn = 10_apx, // CSS Flex-like properties - new Text{ + rcnew Text{ text = Value{ &m_label }, // Text may be dynamic visible = Value{ &m_toggled }, // The Switch widget controls the visibility of this text widget @@ -222,8 +222,8 @@ public: }, // Button widget - new Button{ - new Text{ "Click" }, + rcnew Button{ + rcnew Text{ "Click" }, // Using m_lifetime ensures that callbacks will be detached once the Component is deleted onClick = m_lifetime | [this]() { @@ -233,7 +233,7 @@ public: }, // ComboBox widget - new ComboBox{ + rcnew ComboBox{ Value{ &m_textAlignment }, // Bind ComboBox value to an enumeration notManaged(&textAlignList), // Pass the list of name-value pairs to populate the ComboBox }, @@ -241,7 +241,7 @@ public: // The Builder creates widgets dynamically whenever needed Builder([this](Widget* target) { for (int i = 0; i < m_number; ++i) { - target->apply(new Widget{ + target->apply(rcnew Widget{ dimensions = { 40_apx, 40_apx }, }); } diff --git a/docs/docs/hello_world.md b/docs/docs/hello_world.md index 8eb6803..febd8f3 100644 --- a/docs/docs/hello_world.md +++ b/docs/docs/hello_world.md @@ -73,9 +73,9 @@ public: gapRow = 8_px, // Set vertical gap between elements alignItems = AlignItems::Center, // Align child widgets to the center justifyContent = Justify::Center, // Center the layout in the parent - new Text{"Hello, world"}, // Display a text widget with "Hello, world" - new Button{ - new Text{"Quit"}, // Button label + rcnew Text{"Hello, world"}, // Display a text widget with "Hello, world" + rcnew Button{ + rcnew Text{"Quit"}, // Button label onClick = m_lifetime | []() { // Quit the application on button click windowApplication->quit(); }, diff --git a/examples/calc/calc.cpp b/examples/calc/calc.cpp index 8389649..75683d5 100644 --- a/examples/calc/calc.cpp +++ b/examples/calc/calc.cpp @@ -28,7 +28,7 @@ struct CalcBtn : public Button { template CalcBtn(std::string text, const Args&... args) : Button{ - new Text{ + rcnew Text{ std::move(text), Arg::textAlign = TextAlign::Center, }, @@ -107,7 +107,7 @@ class CalcComponent final : public Component { animationSpeed = 0.5, Graphene::buttonColor = 0x555B6E_rgb, alignItems = AlignItems::Stretch, - new Text{ + rcnew Text{ text = calc.valOutput(), textAlign = TextAlign::End, fontFamily = Monospace, @@ -119,8 +119,8 @@ class CalcComponent final : public Component { textAutoSize = TextAutoSize::FitWidth, textAutoSizeRange = { 12.f, 50.f }, }, - new CalcRow{ - new CalcBtn{ + rcnew CalcRow{ + rcnew CalcBtn{ "CE", Graphene::buttonColor = 0x9A202A_rgb, onClick = m_lifetime | @@ -128,21 +128,21 @@ class CalcComponent final : public Component { calc.clear(); }, }, - new CalcBtn{ + rcnew CalcBtn{ "C", onClick = m_lifetime | [this] { calc.clear(); }, }, - new CalcBtn{ + rcnew CalcBtn{ ICON_pi, // "π", onClick = m_lifetime | [this] { calc.constant(Number::parse("3.1415926535897932384626433832795")); }, }, - new CalcBtn{ + rcnew CalcBtn{ "←", onClick = m_lifetime | [this] { @@ -150,29 +150,29 @@ class CalcComponent final : public Component { }, }, }, - new CalcRow{ - new CalcBtn{ + rcnew CalcRow{ + rcnew CalcBtn{ "1/x", onClick = m_lifetime | [this] { calc.operation(UnaryOperator::Reciprocal); }, }, - new CalcBtn{ + rcnew CalcBtn{ "x²", onClick = m_lifetime | [this] { calc.operation(UnaryOperator::Square); }, }, - new CalcBtn{ + rcnew CalcBtn{ ICON_radical, // "√x", onClick = m_lifetime | [this] { calc.operation(UnaryOperator::SquareRoot); }, }, - new CalcBtn{ + rcnew CalcBtn{ ICON_divide, // "÷", Graphene::buttonColor = 0x6B7183_rgb, onClick = m_lifetime | @@ -181,29 +181,29 @@ class CalcComponent final : public Component { }, }, }, - new CalcRow{ - new CalcBtn{ + rcnew CalcRow{ + rcnew CalcBtn{ "7", onClick = m_lifetime | [this] { calc.digit(7); }, }, - new CalcBtn{ + rcnew CalcBtn{ "8", onClick = m_lifetime | [this] { calc.digit(8); }, }, - new CalcBtn{ + rcnew CalcBtn{ "9", onClick = m_lifetime | [this] { calc.digit(9); }, }, - new CalcBtn{ + rcnew CalcBtn{ ICON_x, // "×", Graphene::buttonColor = 0x6B7183_rgb, onClick = m_lifetime | @@ -212,29 +212,29 @@ class CalcComponent final : public Component { }, }, }, - new CalcRow{ - new CalcBtn{ + rcnew CalcRow{ + rcnew CalcBtn{ "4", onClick = m_lifetime | [this] { calc.digit(4); }, }, - new CalcBtn{ + rcnew CalcBtn{ "5", onClick = m_lifetime | [this] { calc.digit(5); }, }, - new CalcBtn{ + rcnew CalcBtn{ "6", onClick = m_lifetime | [this] { calc.digit(6); }, }, - new CalcBtn{ + rcnew CalcBtn{ ICON_minus, // "−", Graphene::buttonColor = 0x6B7183_rgb, onClick = m_lifetime | @@ -243,29 +243,29 @@ class CalcComponent final : public Component { }, }, }, - new CalcRow{ - new CalcBtn{ + rcnew CalcRow{ + rcnew CalcBtn{ "1", onClick = m_lifetime | [this] { calc.digit(1); }, }, - new CalcBtn{ + rcnew CalcBtn{ "2", onClick = m_lifetime | [this] { calc.digit(2); }, }, - new CalcBtn{ + rcnew CalcBtn{ "3", onClick = m_lifetime | [this] { calc.digit(3); }, }, - new CalcBtn{ + rcnew CalcBtn{ ICON_plus, // "+", Graphene::buttonColor = 0x6B7183_rgb, onClick = m_lifetime | @@ -274,29 +274,29 @@ class CalcComponent final : public Component { }, }, }, - new CalcRow{ - new CalcBtn{ + rcnew CalcRow{ + rcnew CalcBtn{ "±", onClick = m_lifetime | [this] { calc.changeSign(); }, }, - new CalcBtn{ + rcnew CalcBtn{ "0", onClick = m_lifetime | [this] { calc.digit(0); }, }, - new CalcBtn{ + rcnew CalcBtn{ ".", onClick = m_lifetime | [this] { calc.decimalSep(); }, }, - new CalcBtn{ + rcnew CalcBtn{ ICON_equal, // "=", Graphene::buttonColor = 0x297227_rgb, onClick = m_lifetime | diff --git a/examples/showcase/src/Binding.cpp b/examples/showcase/src/Binding.cpp index f7c9fd8..b6a1c3d 100644 --- a/examples/showcase/src/Binding.cpp +++ b/examples/showcase/src/Binding.cpp @@ -8,13 +8,13 @@ RC ShowcaseBinding::build(RC notifications) { flexGrow = 1, padding = 16_apx, gapRow = 8_apx, - new HLayout{ - new Widget{ + rcnew HLayout{ + rcnew Widget{ layout = Layout::Vertical, alignItems = AlignItems::FlexStart, - new ToggleButton{ value = Value{ &m_open }, "Open hidden content"_Text, new Text{ ICON_x }, - twoState = true }, - new Widget{ + rcnew ToggleButton{ value = Value{ &m_open }, "Open hidden content"_Text, + rcnew Text{ ICON_x }, twoState = true }, + rcnew Widget{ visible = Value{ &m_open }, padding = 16_apx, margin = 1_apx, @@ -27,40 +27,42 @@ RC ShowcaseBinding::build(RC notifications) { "ToggleButton::value controls Widget::visible"_Text, }, - new HLayout{ - new Widget{ - new Widget{ + rcnew HLayout{ + rcnew Widget{ + rcnew Widget{ gapColumn = 4_apx, - new Knob{ value = Value{ &m_value1 }, minimum = 0.f, maximum = 100.f, - dimensions = 30_apx }, - new Slider{ value = Value{ &m_value1 }, minimum = 0.f, maximum = 100.f, width = 250_apx }, + rcnew Knob{ value = Value{ &m_value1 }, minimum = 0.f, maximum = 100.f, + dimensions = 30_apx }, + rcnew Slider{ value = Value{ &m_value1 }, minimum = 0.f, maximum = 100.f, + width = 250_apx }, }, &m_group, }, gapColumn = 10_apx, "Knob::value bound to Slider::value (<->)"_Text, }, - new HLayout{ - new Widget{ - new Widget{ + rcnew HLayout{ + rcnew Widget{ + rcnew Widget{ gapColumn = 4_apx, - new Knob{ value = Value{ &m_value2 }, minimum = 0.f, maximum = 100.f, - dimensions = 30_apx }, - new Slider{ value = Value{ &m_value2 }.readOnly(), minimum = 0.f, maximum = 100.f, - width = 250_apx }, + rcnew Knob{ value = Value{ &m_value2 }, minimum = 0.f, maximum = 100.f, + dimensions = 30_apx }, + rcnew Slider{ value = Value{ &m_value2 }.readOnly(), minimum = 0.f, maximum = 100.f, + width = 250_apx }, }, &m_group, }, gapColumn = 10_apx, "Knob::value controls Slider::value (->)"_Text, }, - new HLayout{ - new Widget{ - new Widget{ + rcnew HLayout{ + rcnew Widget{ + rcnew Widget{ gapColumn = 4_apx, - new Knob{ value = Value{ &m_value3 }.readOnly(), minimum = 0.f, maximum = 100.f, - dimensions = 30_apx }, - new Slider{ value = Value{ &m_value3 }, minimum = 0.f, maximum = 100.f, width = 250_apx }, + rcnew Knob{ value = Value{ &m_value3 }.readOnly(), minimum = 0.f, maximum = 100.f, + dimensions = 30_apx }, + rcnew Slider{ value = Value{ &m_value3 }, minimum = 0.f, maximum = 100.f, + width = 250_apx }, }, &m_group, }, @@ -68,14 +70,14 @@ RC ShowcaseBinding::build(RC notifications) { "Knob::value is controlled by Slider::value (<-)"_Text, }, - new HLayout{ - new VLayout{ - new CheckBox{ value = Value{ &m_checkBoxes[0] }, "Monday"_Text }, - new CheckBox{ value = Value{ &m_checkBoxes[1] }, "Tuesday"_Text }, - new CheckBox{ value = Value{ &m_checkBoxes[2] }, "Wednesday"_Text }, - new CheckBox{ value = Value{ &m_checkBoxes[3] }, "Thursday"_Text }, - new CheckBox{ value = Value{ &m_checkBoxes[4] }, "Friday"_Text }, - new Text{ + rcnew HLayout{ + rcnew VLayout{ + rcnew CheckBox{ value = Value{ &m_checkBoxes[0] }, "Monday"_Text }, + rcnew CheckBox{ value = Value{ &m_checkBoxes[1] }, "Tuesday"_Text }, + rcnew CheckBox{ value = Value{ &m_checkBoxes[2] }, "Wednesday"_Text }, + rcnew CheckBox{ value = Value{ &m_checkBoxes[3] }, "Thursday"_Text }, + rcnew CheckBox{ value = Value{ &m_checkBoxes[4] }, "Friday"_Text }, + rcnew Text{ "Select two weekdays", visible = transform( [](bool mon, bool tue, bool wed, bool thu, bool fri) { diff --git a/examples/showcase/src/Buttons.cpp b/examples/showcase/src/Buttons.cpp index 2838303..c618bc0 100644 --- a/examples/showcase/src/Buttons.cpp +++ b/examples/showcase/src/Buttons.cpp @@ -16,41 +16,41 @@ RC ShowcaseButtons::build(RC notifications) { flexGrow = 1, padding = 16_apx, gapRow = 8_apx, - new Text{ "Button (widgets/Button.hpp)", classes = { "section-header" } }, - new HLayout{ - new Widget{ - new Button{ - new Text{ "Button 1" }, + rcnew Text{ "Button (widgets/Button.hpp)", classes = { "section-header" } }, + rcnew HLayout{ + rcnew Widget{ + rcnew Button{ + rcnew Text{ "Button 1" }, onClick = m_lifetime | [notifications]() { - notifications->show(new Text{ "Button 1 clicked" }); + notifications->show(rcnew Text{ "Button 1 clicked" }); }, }, - new Button{ - new Text{ "Disabled Button" }, + rcnew Button{ + rcnew Text{ "Disabled Button" }, disabled = true, onClick = m_lifetime | [notifications]() { - notifications->show(new Text{ "Disabled Button clicked" }); + notifications->show(rcnew Text{ "Disabled Button clicked" }); }, }, &m_group, }, }, - new HLayout{ - new Widget{ - new Button{ - new Text{ ICON_settings " Button with icon" }, + rcnew HLayout{ + rcnew Widget{ + rcnew Button{ + rcnew Text{ ICON_settings " Button with icon" }, }, &m_group, }, gapColumn = 10_apx, - new Text{ "Icon from icon font" }, + rcnew Text{ "Icon from icon font" }, }, - new HLayout{ - new Widget{ - new Button{ - new SVGImageView{ + rcnew HLayout{ + rcnew Widget{ + rcnew Button{ + rcnew SVGImageView{ R"SVG( @@ -72,17 +72,17 @@ RC ShowcaseButtons::build(RC notifications) { dimensions = { 18_apx, 18_apx }, }, gapColumn = 5_apx, - new Text{ "Button with icon" }, + rcnew Text{ "Button with icon" }, }, &m_group, }, gapColumn = 10_apx, - new Text{ "SVG icon" }, + rcnew Text{ "SVG icon" }, }, - new HLayout{ - new Widget{ - new Button{ - new Viewport{ + rcnew HLayout{ + rcnew Widget{ + rcnew Button{ + rcnew Viewport{ [](Canvas& canvas, Rectangle rect) { canvas.setFillColor(Palette::Standard::amber); PreparedText text = fonts->prepare(Font{ FontFamily::Default, dp(18) }, @@ -100,29 +100,29 @@ RC ShowcaseButtons::build(RC notifications) { &m_group, }, gapColumn = 10_apx, - new Text{ "Button with Viewport displaying rendered content" }, + rcnew Text{ "Button with Viewport displaying rendered content" }, }, - new VLayout{ + rcnew VLayout{ gapRow = 5_apx, alignItems = AlignItems::FlexStart, - new Button{ - new Text{ "Button with color applied" }, + rcnew Button{ + rcnew Text{ "Button with color applied" }, Graphene::buttonColor = 0xFF4791_rgb, }, - new Button{ - new Text{ "Button with reduced padding" }, + rcnew Button{ + rcnew Text{ "Button with reduced padding" }, padding = 4_px, }, - new Button{ - new Text{ "Button with flat style" }, + rcnew Button{ + rcnew Text{ "Button with flat style" }, classes = { "flat" }, }, &m_group, }, - new HLayout{ - new Widget{ - new Button{ - new Text{ + rcnew HLayout{ + rcnew Widget{ + rcnew Button{ + rcnew Text{ "Hold to repeat action", }, repeatDelay = 0.2, @@ -136,121 +136,121 @@ RC ShowcaseButtons::build(RC notifications) { &m_group, }, gapColumn = 10_apx, - new Text{ + rcnew Text{ text = Value{ &m_clicked }.transform([](int n) { return fmt::format("Clicked {} times", n); }), }, }, - new HLayout{ - new Widget{ - new Button{ new Text{ "First" }, &m_btnGroup, borderRadius = 15_px }, - new Button{ new Text{ "Second" }, &m_btnGroup, borderRadius = 15_px }, - new Button{ new Text{ "Third" }, &m_btnGroup, borderRadius = 15_px }, + rcnew HLayout{ + rcnew Widget{ + rcnew Button{ rcnew Text{ "First" }, &m_btnGroup, borderRadius = 15_px }, + rcnew Button{ rcnew Text{ "Second" }, &m_btnGroup, borderRadius = 15_px }, + rcnew Button{ rcnew Text{ "Third" }, &m_btnGroup, borderRadius = 15_px }, &m_group, }, gapColumn = 10_apx, - new Text{ "Grouped buttons share borders" }, + rcnew Text{ "Grouped buttons share borders" }, }, - new HLayout{ - new Button{ + rcnew HLayout{ + rcnew Button{ gapColumn = 3_apx, - new Text{ "This button contains" }, - new Table{ + rcnew Text{ "This button contains" }, + rcnew Table{ classes = { "table-padding-4" }, - new TableRow{ - new TableCell{ new Text{ "A" } }, - new TableCell{ new Text{ "small" } }, + rcnew TableRow{ + rcnew TableCell{ rcnew Text{ "A" } }, + rcnew TableCell{ rcnew Text{ "small" } }, }, - new TableRow{ - new TableCell{ new Text{ "Table" } }, - new TableCell{ new Text{ "widget" } }, + rcnew TableRow{ + rcnew TableCell{ rcnew Text{ "Table" } }, + rcnew TableCell{ rcnew Text{ "widget" } }, }, }, - new Text{ "inside it" }, + rcnew Text{ "inside it" }, }, }, - new Text{ "ToggleButton (widgets/ToggleButton.hpp)", classes = { "section-header" } }, - new HLayout{ - new Widget{ - new ToggleButton{ + rcnew Text{ "ToggleButton (widgets/ToggleButton.hpp)", classes = { "section-header" } }, + rcnew HLayout{ + rcnew Widget{ + rcnew ToggleButton{ value = Value{ &m_toggled }, - new Text{ "ToggleButton 1" }, + rcnew Text{ "ToggleButton 1" }, }, &m_group, }, }, - new HLayout{ - new Widget{ - new ToggleButton{ + rcnew HLayout{ + rcnew Widget{ + rcnew ToggleButton{ value = Value{ &m_toggled }, - new Text{ "ToggleButton 2" }, + rcnew Text{ "ToggleButton 2" }, }, &m_group, }, gapColumn = 10_apx, - new Text{ "Shares state with ToggleButton 1" }, + rcnew Text{ "Shares state with ToggleButton 1" }, }, - new HLayout{ - new Widget{ - new ToggleButton{ + rcnew HLayout{ + rcnew Widget{ + rcnew ToggleButton{ value = Value{ &m_toggled }, - new Text{ "Off" }, - new Text{ "On" }, + rcnew Text{ "Off" }, + rcnew Text{ "On" }, twoState = true, }, &m_group, }, gapColumn = 10_apx, - new Text{ "Shares state with ToggleButton 1" }, + rcnew Text{ "Shares state with ToggleButton 1" }, }, - new Text{ "CheckBox (widgets/CheckBox.hpp)", classes = { "section-header" } }, - new HLayout{ - new Widget{ - new CheckBox{ + rcnew Text{ "CheckBox (widgets/CheckBox.hpp)", classes = { "section-header" } }, + rcnew HLayout{ + rcnew Widget{ + rcnew CheckBox{ value = Value{ &m_toggled }, - new Text{ "CheckBox" }, + rcnew Text{ "CheckBox" }, }, &m_group, }, gapColumn = 10_apx, - new Text{ "Shares state with ToggleButton 1" }, + rcnew Text{ "Shares state with ToggleButton 1" }, }, - new Text{ "Switch (widgets/Switch.hpp)", classes = { "section-header" } }, - new HLayout{ - new Widget{ - new Switch{ + rcnew Text{ "Switch (widgets/Switch.hpp)", classes = { "section-header" } }, + rcnew HLayout{ + rcnew Widget{ + rcnew Switch{ value = Value{ &m_toggled }, - new Text{ "Switch" }, + rcnew Text{ "Switch" }, }, &m_group, }, gapColumn = 10_apx, - new Text{ "Shares state with ToggleButton 1" }, + rcnew Text{ "Shares state with ToggleButton 1" }, }, - new Text{ "RadioButton (widgets/RadioButton.hpp)", classes = { "section-header" } }, - new HLayout{ - new Widget{ - new RadioButton{ + rcnew Text{ "RadioButton (widgets/RadioButton.hpp)", classes = { "section-header" } }, + rcnew HLayout{ + rcnew Widget{ + rcnew RadioButton{ value = Value{ &m_toggled }, - new Text{ "On" }, + rcnew Text{ "On" }, }, gapColumn = 6_apx, - new RadioButton{ + rcnew RadioButton{ value = Value{ &m_toggled }.transform(std::logical_not<>{}, std::logical_not<>{}), - new Text{ "Off" }, + rcnew Text{ "Off" }, }, &m_group, }, gapColumn = 10_apx, - new Text{ "Shares state with ToggleButton 1" }, + rcnew Text{ "Shares state with ToggleButton 1" }, }, - new Text{ "Hyperlink (widgets/Hyperlink.hpp)", classes = { "section-header" } }, - new HLayout{ - new Widget{ - new Hyperlink{ + rcnew Text{ "Hyperlink (widgets/Hyperlink.hpp)", classes = { "section-header" } }, + rcnew HLayout{ + rcnew Widget{ + rcnew Hyperlink{ "https://brisklib.com", - new Text{ "Click to visit brisklib.com" }, + rcnew Text{ "Click to visit brisklib.com" }, }, &m_group, }, diff --git a/examples/showcase/src/Dialogs.cpp b/examples/showcase/src/Dialogs.cpp index b797146..77f578c 100644 --- a/examples/showcase/src/Dialogs.cpp +++ b/examples/showcase/src/Dialogs.cpp @@ -6,10 +6,10 @@ namespace Brisk { -static Widget* osDialogButton(std::string text, Value> fn) { - return new HLayout{ - new Button{ - new Text{ std::move(text) }, +static RC osDialogButton(std::string text, Value> fn) { + return rcnew HLayout{ + rcnew Button{ + rcnew Text{ std::move(text) }, onClick = std::move(fn), }, }; @@ -20,13 +20,13 @@ class SmallComponent : public Component { RC build() final { return rcnew Widget{ stylesheet = Graphene::stylesheet(), - new Spacer{}, - new Text{ + rcnew Spacer{}, + rcnew Text{ "Separate window based on Brisk::Component", flexGrow = 1, textAlign = TextAlign::Center, }, - new Spacer{}, + rcnew Spacer{}, }; } }; @@ -37,11 +37,11 @@ RC ShowcaseDialogs::build(RC notifications) { padding = 16_apx, gapRow = 8_apx, - new Text{ "Multiple windows (gui/Component.hpp)", classes = { "section-header" } }, + rcnew Text{ "Multiple windows (gui/Component.hpp)", classes = { "section-header" } }, - new HLayout{ - new Button{ - new Text{ "Open window" }, + rcnew HLayout{ + rcnew Button{ + rcnew Text{ "Open window" }, onClick = m_lifetime | [this]() { RC comp = rcnew SmallComponent(); @@ -49,8 +49,8 @@ RC ShowcaseDialogs::build(RC notifications) { }, }, - new Button{ - new Text{ "Open modal window" }, + rcnew Button{ + rcnew Text{ "Open modal window" }, onClick = m_lifetime | [this]() { RC comp = rcnew SmallComponent(); @@ -58,9 +58,9 @@ RC ShowcaseDialogs::build(RC notifications) { }, }, }, - new HLayout{ - new Button{ - new Text{ "TextInputDialog" }, + rcnew HLayout{ + rcnew Button{ + rcnew Text{ "TextInputDialog" }, onClick = m_lifetime | []() { RC dialog = rcnew TextInputDialog{ "Enter name", "World" }; @@ -73,27 +73,27 @@ RC ShowcaseDialogs::build(RC notifications) { }, }, - new Text{ "PopupDialog (widgets/PopupDialog.hpp)", classes = { "section-header" } }, + rcnew Text{ "PopupDialog (widgets/PopupDialog.hpp)", classes = { "section-header" } }, - new HLayout{ - new Button{ - new Text{ "Open Dialog" }, + rcnew HLayout{ + rcnew Button{ + rcnew Text{ "Open Dialog" }, onClick = m_lifetime | [this]() { bindings->assign(m_popupDialog, true); }, }, - new PopupOKDialog{ + rcnew PopupOKDialog{ "Dialog title", Value{ &m_popupDialog }, [notifications]() { - notifications->show(new Text{ "Dialog closed" }); + notifications->show(rcnew Text{ "Dialog closed" }); }, - new Text{ "Dialog" }, + rcnew Text{ "Dialog" }, }, }, - new Text{ "OS dialogs (window/OSDialogs.hpp)", classes = { "section-header" } }, + rcnew Text{ "OS dialogs (window/OSDialogs.hpp)", classes = { "section-header" } }, osDialogButton( "Open URL", m_lifetime | []() { @@ -174,7 +174,7 @@ RC ShowcaseDialogs::build(RC notifications) { m_text += "(nullopt)\n"; bindings->notify(&m_text); }), - new Text{ + rcnew Text{ text = Value{ &m_text }, fontFamily = Monospace, }, diff --git a/examples/showcase/src/Dropdowns.cpp b/examples/showcase/src/Dropdowns.cpp index 5fa2423..86bc91e 100644 --- a/examples/showcase/src/Dropdowns.cpp +++ b/examples/showcase/src/Dropdowns.cpp @@ -15,122 +15,123 @@ RC ShowcaseDropdowns::build(RC notifications) { padding = 16_apx, gapRow = 8_apx, - new Text{ "PopupButton (widgets/PopupButton.hpp)", classes = { "section-header" } }, - new HLayout{ - new Widget{ - new PopupButton{ - new Text{ "Button with menu" }, - new PopupBox{ + rcnew Text{ "PopupButton (widgets/PopupButton.hpp)", classes = { "section-header" } }, + rcnew HLayout{ + rcnew Widget{ + rcnew PopupButton{ + rcnew Text{ "Button with menu" }, + rcnew PopupBox{ classes = { "menubox" }, - new Item{ new Text{ "Item" } }, - new Item{ new Text{ "Item with icon" }, icon = ICON_award }, - new Spacer{ height = 6 }, - new Item{ checked = Value::mutableValue(true), checkable = true, - new Text{ "Item with checkbox" } }, + rcnew Item{ rcnew Text{ "Item" } }, + rcnew Item{ rcnew Text{ "Item with icon" }, icon = ICON_award }, + rcnew Spacer{ height = 6 }, + rcnew Item{ checked = Value::mutableValue(true), checkable = true, + rcnew Text{ "Item with checkbox" } }, }, }, &m_group, }, }, - new HLayout{ - new Widget{ - new PopupButton{ - new Text{ "Button with box" }, - new PopupBox{ + rcnew HLayout{ + rcnew Widget{ + rcnew PopupButton{ + rcnew Text{ "Button with box" }, + rcnew PopupBox{ layout = Layout::Vertical, width = 100_apx, alignItems = AlignItems::Stretch, - new ColorView{ Palette::Standard::index(0) }, - new ColorView{ Palette::Standard::index(1) }, - new ColorView{ Palette::Standard::index(2) }, - new ColorView{ Palette::Standard::index(3) }, - new ColorView{ Palette::Standard::index(4) }, - new ColorView{ Palette::Standard::index(5) }, + rcnew ColorView{ Palette::Standard::index(0) }, + rcnew ColorView{ Palette::Standard::index(1) }, + rcnew ColorView{ Palette::Standard::index(2) }, + rcnew ColorView{ Palette::Standard::index(3) }, + rcnew ColorView{ Palette::Standard::index(4) }, + rcnew ColorView{ Palette::Standard::index(5) }, }, }, &m_group, }, gapColumn = 10_apx, - new Text{ "Click outside the box to hide it" }, + rcnew Text{ "Click outside the box to hide it" }, }, - new Text{ "ComboBox (widgets/ComboBox.hpp)", classes = { "section-header" } }, - new HLayout{ - new Widget{ - new ComboBox{ + rcnew Text{ "ComboBox (widgets/ComboBox.hpp)", classes = { "section-header" } }, + rcnew HLayout{ + rcnew Widget{ + rcnew ComboBox{ value = Value{ &m_month }, - new ItemList{ - new Text{ "January" }, - new Text{ "February" }, - new Text{ "March" }, - new Text{ "April" }, - new Text{ "May" }, - new Text{ "June" }, - new Text{ "July" }, - new Text{ "August" }, - new Text{ "September" }, - new Text{ "October" }, - new Text{ "November" }, - new Text{ "December" }, + rcnew ItemList{ + rcnew Text{ "January" }, + rcnew Text{ "February" }, + rcnew Text{ "March" }, + rcnew Text{ "April" }, + rcnew Text{ "May" }, + rcnew Text{ "June" }, + rcnew Text{ "July" }, + rcnew Text{ "August" }, + rcnew Text{ "September" }, + rcnew Text{ "October" }, + rcnew Text{ "November" }, + rcnew Text{ "December" }, }, }, &m_group, }, gapColumn = 10_apx, - new Text{ "ComboBox with text items" }, + rcnew Text{ "ComboBox with text items" }, }, - new HLayout{ - new Widget{ - new ComboBox{ + rcnew HLayout{ + rcnew Widget{ + rcnew ComboBox{ value = Value{ &m_selectedItem }, - new ItemList{ - IndexedBuilder([](int index) -> Widget* { + rcnew ItemList{ + IndexedBuilder([](int index) -> RC { if (index > 40) return nullptr; - return new Text{ fmt::to_string(index) }; + return rcnew Text{ fmt::to_string(index) }; }), }, }, &m_group, }, gapColumn = 10_apx, - new Text{ "ComboBox with generated content" }, + rcnew Text{ "ComboBox with generated content" }, }, - new HLayout{ - new Widget{ - new ComboBox{ + rcnew HLayout{ + rcnew Widget{ + rcnew ComboBox{ value = Value{ &m_selectedItem2 }, - new ItemList{ - new ColorView{ Palette::Standard::index(0) }, - new ColorView{ Palette::Standard::index(1) }, - new ColorView{ Palette::Standard::index(2) }, - new ColorView{ Palette::Standard::index(3) }, - new ColorView{ Palette::Standard::index(4) }, - new ColorView{ Palette::Standard::index(5) }, + rcnew ItemList{ + rcnew ColorView{ Palette::Standard::index(0) }, + rcnew ColorView{ Palette::Standard::index(1) }, + rcnew ColorView{ Palette::Standard::index(2) }, + rcnew ColorView{ Palette::Standard::index(3) }, + rcnew ColorView{ Palette::Standard::index(4) }, + rcnew ColorView{ Palette::Standard::index(5) }, }, }, &m_group, }, gapColumn = 10_apx, - new Text{ "ComboBox with widgets" }, + rcnew Text{ "ComboBox with widgets" }, }, - new Text{ "ContextPopup (widgets/ContextPopup.hpp)", classes = { "section-header" } }, - new HLayout{ - new Widget{ - new Widget{ + rcnew Text{ "ContextPopup (widgets/ContextPopup.hpp)", classes = { "section-header" } }, + rcnew HLayout{ + rcnew Widget{ + rcnew Widget{ dimensions = { 200_apx, 100_apx }, backgroundColor = 0x777777_rgb, - new Text{ "Right-click for context menu", wordWrap = true, alignSelf = AlignSelf::Center, - color = 0xFFFFFF_rgb, textAlign = TextAlign::Center, fontSize = 200_perc, - mouseInteraction = MouseInteraction::Disable, flexGrow = 1 }, + rcnew Text{ "Right-click for context menu", wordWrap = true, + alignSelf = AlignSelf::Center, color = 0xFFFFFF_rgb, + textAlign = TextAlign::Center, fontSize = 200_perc, + mouseInteraction = MouseInteraction::Disable, flexGrow = 1 }, - new ContextPopup{ + rcnew ContextPopup{ role = "context", tabGroup = true, - new Item{ icon = ICON_pencil, new Text{ "First" } }, - new Item{ icon = ICON_eye, new Text{ "Second" } }, - new Item{ new Text{ "Third" } }, - new Item{ new Text{ "Fourth" } }, + rcnew Item{ icon = ICON_pencil, rcnew Text{ "First" } }, + rcnew Item{ icon = ICON_eye, rcnew Text{ "Second" } }, + rcnew Item{ rcnew Text{ "Third" } }, + rcnew Item{ rcnew Text{ "Fourth" } }, }, }, &m_group, diff --git a/examples/showcase/src/Editors.cpp b/examples/showcase/src/Editors.cpp index ff3fcaa..3492f68 100644 --- a/examples/showcase/src/Editors.cpp +++ b/examples/showcase/src/Editors.cpp @@ -17,139 +17,140 @@ RC ShowcaseEditors::build(RC notifications) { padding = 16_apx, gapRow = 8_apx, - new Text{ "Slider (widgets/Slider.hpp)", classes = { "section-header" } }, + rcnew Text{ "Slider (widgets/Slider.hpp)", classes = { "section-header" } }, - new HLayout{ - new Widget{ - new Slider{ value = Value{ &m_value }, minimum = 0.f, maximum = 100.f, width = 250_apx }, + rcnew HLayout{ + rcnew Widget{ + rcnew Slider{ value = Value{ &m_value }, minimum = 0.f, maximum = 100.f, width = 250_apx }, &m_group, }, gapColumn = 10_apx, - new Text{ + rcnew Text{ text = Value{ &m_value }.transform([](float v) { return fmt::format("Value: {:.1f}", v); }), }, }, - new HLayout{ - new Widget{ - new Slider{ value = Value{ &m_value }, hintFormatter = "x={:.1f}", minimum = 0.f, - maximum = 100.f, width = 250_apx }, + rcnew HLayout{ + rcnew Widget{ + rcnew Slider{ value = Value{ &m_value }, hintFormatter = "x={:.1f}", minimum = 0.f, + maximum = 100.f, width = 250_apx }, &m_group, }, gapColumn = 10_apx, - new Text{ "Value with custom hint" }, + rcnew Text{ "Value with custom hint" }, }, - new HLayout{ - new Widget{ - new Slider{ value = Value{ &m_y }, hintFormatter = "y={:.1f}", minimum = 0.f, maximum = 100.f, - width = 250_apx, dimensions = { 20_apx, 80_apx } }, + rcnew HLayout{ + rcnew Widget{ + rcnew Slider{ value = Value{ &m_y }, hintFormatter = "y={:.1f}", minimum = 0.f, + maximum = 100.f, width = 250_apx, dimensions = { 20_apx, 80_apx } }, &m_group, }, gapColumn = 10_apx, }, - new Text{ "Knob (widgets/Knob.hpp)", classes = { "section-header" } }, + rcnew Text{ "Knob (widgets/Knob.hpp)", classes = { "section-header" } }, - new HLayout{ - new Widget{ - new Knob{ value = Value{ &m_value }, minimum = 0.f, maximum = 100.f, dimensions = 30_apx }, + rcnew HLayout{ + rcnew Widget{ + rcnew Knob{ value = Value{ &m_value }, minimum = 0.f, maximum = 100.f, dimensions = 30_apx }, &m_group, }, gapColumn = 10_apx, - new Text{ + rcnew Text{ text = Value{ &m_value }.transform([](float v) { return fmt::format("Value: {:.1f}", v); }), }, }, - new Text{ "SpinBox (widgets/SpinBox.hpp)", classes = { "section-header" } }, + rcnew Text{ "SpinBox (widgets/SpinBox.hpp)", classes = { "section-header" } }, - new HLayout{ - new Widget{ - new SpinBox{ value = Value{ &m_value }, minimum = 0.f, maximum = 100.f, width = 90_apx }, + rcnew HLayout{ + rcnew Widget{ + rcnew SpinBox{ value = Value{ &m_value }, minimum = 0.f, maximum = 100.f, width = 90_apx }, &m_group, }, gapColumn = 10_apx, - new Text{ + rcnew Text{ text = Value{ &m_value }.transform([](float v) { return fmt::format("Value: {:.1f}", v); }), }, }, - new Text{ "TextEditor (widgets/TextEditor.hpp)", classes = { "section-header" } }, + rcnew Text{ "TextEditor (widgets/TextEditor.hpp)", classes = { "section-header" } }, - new HLayout{ - new Widget{ - new TextEditor(Value{ &m_text }, fontSize = 150_perc, width = 100_perc), + rcnew HLayout{ + rcnew Widget{ + rcnew TextEditor(Value{ &m_text }, fontSize = 150_perc, width = 100_perc), &m_group, }, gapColumn = 10_apx, - new Text{ + rcnew Text{ text = Value{ &m_text }.transform([](std::string s) { return fmt::format("Text: \"{}\"", s); }), }, }, - new Text{ "multiline = true", classes = { "section-header" } }, + rcnew Text{ "multiline = true", classes = { "section-header" } }, - new TextEditor(Value{ &m_multilineText }, fontSize = 150_perc, height = 5_em, multiline = true, - textVerticalAlign = TextAlign::Start, width = auto_), + rcnew TextEditor(Value{ &m_multilineText }, fontSize = 150_perc, height = 5_em, multiline = true, + textVerticalAlign = TextAlign::Start, width = auto_), - new Text{ "PasswordEditor (widgets/TextEditor.hpp)", classes = { "section-header" } }, + rcnew Text{ "PasswordEditor (widgets/TextEditor.hpp)", classes = { "section-header" } }, - new HLayout{ - new Widget{ - new PasswordEditor(Value{ &m_password }, width = 100_perc, fontFamily = Monospace, - passwordChar = Value{ &m_hidePassword }.transform([](bool v) -> char32_t { - return v ? '*' : 0; - })), + rcnew HLayout{ + rcnew Widget{ + rcnew PasswordEditor(Value{ &m_password }, width = 100_perc, fontFamily = Monospace, + passwordChar = + Value{ &m_hidePassword }.transform([](bool v) -> char32_t { + return v ? '*' : 0; + })), &m_group, }, gapColumn = 10_apx, - new CheckBox{ value = Value{ &m_hidePassword }, new Text{ "Hide password" } }, + rcnew CheckBox{ value = Value{ &m_hidePassword }, rcnew Text{ "Hide password" } }, }, - new Text{ "ColorView (widgets/Color.hpp)", classes = { "section-header" } }, + rcnew Text{ "ColorView (widgets/Color.hpp)", classes = { "section-header" } }, - new HLayout{ - new Widget{ - new ColorView{ Palette::Standard::indigo }, + rcnew HLayout{ + rcnew Widget{ + rcnew ColorView{ Palette::Standard::indigo }, &m_group, }, gapColumn = 10_apx, }, - new Text{ "ColorSliders (widgets/Color.hpp)", classes = { "section-header" } }, + rcnew Text{ "ColorSliders (widgets/Color.hpp)", classes = { "section-header" } }, - new HLayout{ - new Widget{ - new ColorSliders{ Value{ &m_color }, false }, + rcnew HLayout{ + rcnew Widget{ + rcnew ColorSliders{ Value{ &m_color }, false }, &m_group, }, gapColumn = 10_apx, }, - new Text{ "ColorPalette (widgets/Color.hpp)", classes = { "section-header" } }, + rcnew Text{ "ColorPalette (widgets/Color.hpp)", classes = { "section-header" } }, - new HLayout{ - new Widget{ - new ColorPalette{ Value{ &m_color } }, + rcnew HLayout{ + rcnew Widget{ + rcnew ColorPalette{ Value{ &m_color } }, &m_group, }, gapColumn = 10_apx, }, - new Text{ "ColorButton (widgets/Color.hpp)", classes = { "section-header" } }, + rcnew Text{ "ColorButton (widgets/Color.hpp)", classes = { "section-header" } }, - new HLayout{ - new Widget{ - new ColorButton{ Value{ &m_color }, false }, + rcnew HLayout{ + rcnew Widget{ + rcnew ColorButton{ Value{ &m_color }, false }, &m_group, }, gapColumn = 10_apx, diff --git a/examples/showcase/src/Layout.cpp b/examples/showcase/src/Layout.cpp index c6812ad..8c1c830 100644 --- a/examples/showcase/src/Layout.cpp +++ b/examples/showcase/src/Layout.cpp @@ -10,8 +10,8 @@ RC ShowcaseLayout::build(RC notifications) { gapRow = 8_apx, overflow = Overflow::ScrollX, - new Text{ "flexWrap = Wrap::Wrap", classes = { "section-header" } }, - new HLayout{ + rcnew Text{ "flexWrap = Wrap::Wrap", classes = { "section-header" } }, + rcnew HLayout{ padding = 16_apx, gapRow = 16_apx, gapColumn = 16_apx, @@ -22,9 +22,9 @@ RC ShowcaseLayout::build(RC notifications) { Builder([](Widget* target) { for (int i = 0; i < 24; ++i) { - target->apply(new Widget{ + target->apply(rcnew Widget{ dimensions = { 80_apx, 80_apx }, - new Text{ + rcnew Text{ fmt::to_string(i + 1), flexGrow = 1, alignSelf = AlignSelf::Stretch, diff --git a/examples/showcase/src/Messenger.cpp b/examples/showcase/src/Messenger.cpp index ec995bc..8f52212 100644 --- a/examples/showcase/src/Messenger.cpp +++ b/examples/showcase/src/Messenger.cpp @@ -47,12 +47,12 @@ void ShowcaseMessenger::messagesBuilder(Widget* target) { }, }, msg.content); - target->apply(new VLayout{ + target->apply(rcnew VLayout{ alignSelf = AlignSelf::FlexEnd, padding = { 8, 6 }, std::move(content), - new Text{ fmt::format("{:%H:%M} {}", msg.date, statusIcon), marginTop = 4_apx, - textAlign = TextAlign::End, opacity = 0.5f }, + rcnew Text{ fmt::format("{:%H:%M} {}", msg.date, statusIcon), marginTop = 4_apx, + textAlign = TextAlign::End, opacity = 0.5f }, width = 360_apx, backgroundColor = 0xe5f7df'F0_rgba, borderWidth = 1_apx, @@ -73,13 +73,13 @@ RC ShowcaseMessenger::build(RC notifications) { painter = Painter(&backgroundPainter), - new VLayout{ + rcnew VLayout{ flexGrow = 1, alignSelf = AlignSelf::Stretch, - new VScrollBox{ + rcnew VScrollBox{ flexGrow = 1, alignSelf = AlignSelf::Stretch, - new VLayout{ + rcnew VLayout{ gapRow = 8, padding = 4, depends = Value{ &m_messagesChanged }, // Rebuild if triggered @@ -91,15 +91,15 @@ RC ShowcaseMessenger::build(RC notifications) { }, }, }, - new HLayout{ + rcnew HLayout{ backgroundColor = Palette::white, borderRadius = -5.f, - new Button{ - new Text{ ICON_paperclip }, + rcnew Button{ + rcnew Text{ ICON_paperclip }, classes = { "flat" }, color = 0x373737_rgb, }, - new TextEditor{ + rcnew TextEditor{ Value{ &m_chatMessage }, flexGrow = 1, padding = 8, @@ -110,8 +110,8 @@ RC ShowcaseMessenger::build(RC notifications) { send(); }, }, - new Button{ - new Text{ ICON_send_horizontal }, + rcnew Button{ + rcnew Text{ ICON_send_horizontal }, classes = { "flat" }, color = 0x373737_rgb, onClick = m_lifetime | diff --git a/examples/showcase/src/ShowcaseComponent.cpp b/examples/showcase/src/ShowcaseComponent.cpp index 0231df4..dead4c3 100644 --- a/examples/showcase/src/ShowcaseComponent.cpp +++ b/examples/showcase/src/ShowcaseComponent.cpp @@ -80,11 +80,11 @@ RC ShowcaseComponent::build() { stylesheet = mainStylesheet, Graphene::darkColors(), - new HLayout{ + rcnew HLayout{ fontSize = 24_dpx, - new Button{ + rcnew Button{ padding = 8_dpx, - new Text{ ICON_zoom_in }, + rcnew Text{ ICON_zoom_in }, borderWidth = 1_dpx, onClick = m_lifetime | []() { @@ -92,9 +92,9 @@ RC ShowcaseComponent::build() { std::exp2(std::round(std::log2(windowApplication->uiScale) * 2 + 1) * 0.5); }, }, - new Button{ + rcnew Button{ padding = 8_dpx, - new Text{ ICON_zoom_out }, + rcnew Text{ ICON_zoom_out }, borderWidth = 1_dpx, onClick = m_lifetime | []() { @@ -102,18 +102,18 @@ RC ShowcaseComponent::build() { std::exp2(std::round(std::log2(windowApplication->uiScale) * 2 - 1) * 0.5); }, }, - new Button{ + rcnew Button{ padding = 8_dpx, - new Text{ ICON_camera }, + rcnew Text{ ICON_camera }, borderWidth = 1_dpx, onClick = m_lifetime | [this]() { captureScreenshot(); }, }, - new Button{ + rcnew Button{ padding = 8_dpx, - new Text{ ICON_sun_moon }, + rcnew Text{ ICON_sun_moon }, borderWidth = 1_dpx, onClick = m_lifetime | [this]() { @@ -126,22 +126,22 @@ RC ShowcaseComponent::build() { }, }, }, - new Pages{ + rcnew Pages{ value = Value{ &m_activePage }, layout = Layout::Horizontal, Pages::tabs = - new Tabs{ + rcnew Tabs{ layout = Layout::Vertical, }, - new Page{ "Buttons", new VScrollBox{ flexGrow = 1, m_buttons->build(notifications) } }, - new Page{ "Dropdowns", new VScrollBox{ flexGrow = 1, m_dropdowns->build(notifications) } }, - new Page{ "Editors", new VScrollBox{ flexGrow = 1, m_editors->build(notifications) } }, - new Page{ "Visual", new VScrollBox{ flexGrow = 1, m_visual->build(notifications) } }, - new Page{ "Layout", new VScrollBox{ flexGrow = 1, m_layout->build(notifications) } }, - new Page{ "Dialogs", new VScrollBox{ flexGrow = 1, m_dialogs->build(notifications) } }, - new Page{ "Typography", new VScrollBox{ flexGrow = 1, m_typography->build(notifications) } }, - new Page{ "Messenger", new VScrollBox{ flexGrow = 1, m_messenger->build(notifications) } }, - new Page{ "Binding", new VScrollBox{ flexGrow = 1, m_binding->build(notifications) } }, + rcnew Page{ "Buttons", rcnew VScrollBox{ flexGrow = 1, m_buttons->build(notifications) } }, + rcnew Page{ "Dropdowns", rcnew VScrollBox{ flexGrow = 1, m_dropdowns->build(notifications) } }, + rcnew Page{ "Editors", rcnew VScrollBox{ flexGrow = 1, m_editors->build(notifications) } }, + rcnew Page{ "Visual", rcnew VScrollBox{ flexGrow = 1, m_visual->build(notifications) } }, + rcnew Page{ "Layout", rcnew VScrollBox{ flexGrow = 1, m_layout->build(notifications) } }, + rcnew Page{ "Dialogs", rcnew VScrollBox{ flexGrow = 1, m_dialogs->build(notifications) } }, + rcnew Page{ "Typography", rcnew VScrollBox{ flexGrow = 1, m_typography->build(notifications) } }, + rcnew Page{ "Messenger", rcnew VScrollBox{ flexGrow = 1, m_messenger->build(notifications) } }, + rcnew Page{ "Binding", rcnew VScrollBox{ flexGrow = 1, m_binding->build(notifications) } }, flexGrow = 1, }, flexGrow = 1, diff --git a/examples/showcase/src/Typography.cpp b/examples/showcase/src/Typography.cpp index c259960b..a25d4ae 100644 --- a/examples/showcase/src/Typography.cpp +++ b/examples/showcase/src/Typography.cpp @@ -10,8 +10,8 @@ static Builder iconsBuilder() { auto iconFontFamily = GoNoto; int iconFontSize = 25; for (int icon = ICON__first; icon < ICON__last; icon += columns) { - HLayout* glyphs = new HLayout{ - new Text{ + RC glyphs = rcnew HLayout{ + rcnew Text{ fmt::format("{:04X}", icon), textVerticalAlign = TextAlign::Center, dimensions = { 60, 50 }, @@ -20,7 +20,7 @@ static Builder iconsBuilder() { for (int c = 0; c < columns; c++) { char32_t ch = icon + c; string u8 = utf32ToUtf8(std::u32string(1, ch)); - glyphs->apply(new Text{ + glyphs->apply(rcnew Text{ u8, classes = { "icon" }, textAlign = TextAlign::Center, @@ -35,7 +35,7 @@ static Builder iconsBuilder() { &staticBinding), }); } - target->apply(glyphs); + target->apply(std::move(glyphs)); } }); } @@ -55,16 +55,16 @@ RC ShowcaseTypography::build(RC notifications) { padding = 16_apx, gapRow = 8_apx, - new Text{ "Fonts", classes = { "section-header" } }, + rcnew Text{ "Fonts", classes = { "section-header" } }, - new HScrollBox{ - new VLayout{ + rcnew HScrollBox{ + rcnew VLayout{ flexGrow = 1, Builder([](Widget* target) { for (int i = 0; i < 7; ++i) { int size = 8 + i * 4; auto row = [target, size](std::string name, FontFamily family, FontWeight weight) { - target->apply(new Text{ + target->apply(rcnew Text{ pangram + fmt::format(" [{}, {}px]", name, size), fontFamily = family, fontWeight = weight, @@ -76,16 +76,16 @@ RC ShowcaseTypography::build(RC notifications) { row("Lato Bold", Lato, FontWeight::Bold); row("GoNoto", GoNoto, FontWeight::Regular); row("Monospace", Monospace, FontWeight::Regular); - target->apply(new Spacer{ height = 12_apx }); + target->apply(rcnew Spacer{ height = 12_apx }); } }), }, }, - new Text{ "Font properties", classes = { "section-header" } }, + rcnew Text{ "Font properties", classes = { "section-header" } }, - new VLayout{ - new Text{ + rcnew VLayout{ + rcnew Text{ "gΥφ fi fl3.14 1/3 LT", fontSize = 40, fontFamily = Lato, @@ -94,33 +94,33 @@ RC ShowcaseTypography::build(RC notifications) { wordSpacing = Value{ &m_wordSpacing }.implicitConversion(), textDecoration = Value{ &m_textDecoration }, }, - new HLayout{ + rcnew HLayout{ Builder{ [this](Widget* target) { for (int i = 0; i < m_fontFeatures.size(); ++i) { - target->apply(new VLayout{ - new Text{ fmt::to_string(m_fontFeatures[i].feature) }, - new Switch{ value = Value{ &m_fontFeatures[i].enabled } }, + target->apply(rcnew VLayout{ + rcnew Text{ fmt::to_string(m_fontFeatures[i].feature) }, + rcnew Switch{ value = Value{ &m_fontFeatures[i].enabled } }, }); } }, }, }, - new Text{ "Text decoration" }, - new ComboBox{ + rcnew Text{ "Text decoration" }, + rcnew ComboBox{ Value{ &m_textDecoration }, notManaged(&textDecorationList), width = 200_apx, }, - new Text{ "Letter spacing" }, - new Slider{ value = Value{ &m_letterSpacing }, minimum = 0.f, maximum = 10.f, width = 200_apx }, - new Text{ "Word spacing" }, - new Slider{ value = Value{ &m_wordSpacing }, minimum = 0.f, maximum = 10.f, width = 200_apx }, + rcnew Text{ "Letter spacing" }, + rcnew Slider{ value = Value{ &m_letterSpacing }, minimum = 0.f, maximum = 10.f, width = 200_apx }, + rcnew Text{ "Word spacing" }, + rcnew Slider{ value = Value{ &m_wordSpacing }, minimum = 0.f, maximum = 10.f, width = 200_apx }, }, - new Text{ "Icons (gui/Icons.hpp)", classes = { "section-header" } }, + rcnew Text{ "Icons (gui/Icons.hpp)", classes = { "section-header" } }, - new VLayout{ + rcnew VLayout{ padding = { 8_apx, 8_apx }, iconsBuilder(), diff --git a/examples/showcase/src/Visual.cpp b/examples/showcase/src/Visual.cpp index 4829b26..7b668db 100644 --- a/examples/showcase/src/Visual.cpp +++ b/examples/showcase/src/Visual.cpp @@ -36,45 +36,45 @@ RC ShowcaseVisual::build(RC notifications) { padding = 16_apx, gapRow = 8_apx, - new Text{ "Text (widgets/Text.hpp)", classes = { "section-header" } }, + rcnew Text{ "Text (widgets/Text.hpp)", classes = { "section-header" } }, - new VLayout{ + rcnew VLayout{ gapRow = 4_apx, - new Text{ "Simple text" }, - new Text{ "Multi-line\ntext" }, - new Text{ "Text with color = Palette::Standard::fuchsia, fontWeight = FontWeight::Bold", - color = Palette::Standard::fuchsia, fontWeight = FontWeight::Bold }, - new Text{ "Text with textAutoSize = TextAutoSize::FitWidth (Resize the window to make the text" - "size fit the width)", - height = 50_apx, textAutoSize = TextAutoSize::FitWidth }, + rcnew Text{ "Simple text" }, + rcnew Text{ "Multi-line\ntext" }, + rcnew Text{ "Text with color = Palette::Standard::fuchsia, fontWeight = FontWeight::Bold", + color = Palette::Standard::fuchsia, fontWeight = FontWeight::Bold }, + rcnew Text{ "Text with textAutoSize = TextAutoSize::FitWidth (Resize the window to make the text" + "size fit the width)", + height = 50_apx, textAutoSize = TextAutoSize::FitWidth }, }, - new Text{ "wordWrap = true", classes = { "section-header" } }, + rcnew Text{ "wordWrap = true", classes = { "section-header" } }, - new VLayout{ - new HLayout{ - new Text{ "Text alignment: " }, - new ComboBox{ + rcnew VLayout{ + rcnew HLayout{ + rcnew Text{ "Text alignment: " }, + rcnew ComboBox{ Value{ &m_textAlign }, notManaged(&textAlignList), width = 110_apx, }, - new Text{ "Font size: " }, + rcnew Text{ "Font size: " }, - new Slider{ value = Value{ &m_fontSize }, minimum = 0.25f, maximum = 4.f, width = 300_apx }, + rcnew Slider{ value = Value{ &m_fontSize }, minimum = 0.25f, maximum = 4.f, width = 300_apx }, }, // Overflow::ScrollX prevents this widget from stretching because of Text overflow = Overflow::ScrollX, - new Text{ loremIpsumShort, wordWrap = true, textAlign = Value{ &m_textAlign }, marginTop = 10_apx, - fontSize = Value{ &m_fontSize }.transform([](float v) { - return v * 100_perc; - }), - fontFamily = Lato }, + rcnew Text{ loremIpsumShort, wordWrap = true, textAlign = Value{ &m_textAlign }, + marginTop = 10_apx, fontSize = Value{ &m_fontSize }.transform([](float v) { + return v * 100_perc; + }), + fontFamily = Lato }, }, - new Text{ "Viewport (widgets/Viewport.hpp)", classes = { "section-header" } }, + rcnew Text{ "Viewport (widgets/Viewport.hpp)", classes = { "section-header" } }, - new Viewport{ + rcnew Viewport{ [](Canvas& canvas, Rectangle rect) { // Static initialization of an image rendered from an SVG representation of "cat" // with a size of 256x256 pixels. @@ -155,58 +155,59 @@ RC ShowcaseVisual::build(RC notifications) { canvas.setFont(Font{ Lato, 48_dp }); canvas.fillText("Brisk", frect.at(0.5f, 0.5f)); }, - nullptr, dimensions = { 256, 256 }, }, - new Text{ "Spinner (widgets/Spinner.hpp)", classes = { "section-header" } }, + rcnew Text{ "Spinner (widgets/Spinner.hpp)", classes = { "section-header" } }, - new HLayout{ - new Spinner{ + rcnew HLayout{ + rcnew Spinner{ dimensions = { 40_apx, 40_apx }, active = Value{ &m_active }, }, gapColumn = 10_apx, - new CheckBox{ value = Value{ &m_active }, new Text{ "Active" } }, + rcnew CheckBox{ value = Value{ &m_active }, rcnew Text{ "Active" } }, }, - new Text{ "Progress (widgets/Progress.hpp)", classes = { "section-header" } }, + rcnew Text{ "Progress (widgets/Progress.hpp)", classes = { "section-header" } }, - new HLayout{ - new Progress{ + rcnew HLayout{ + rcnew Progress{ value = Value{ &m_progress }, minimum = 0, maximum = 100, dimensions = { 400_apx, 20_apx }, }, gapColumn = 10_apx, - new CheckBox{ value = Value{ &m_progressActive }, new Text{ "Active" } }, + rcnew CheckBox{ value = Value{ &m_progressActive }, rcnew Text{ "Active" } }, }, - new Text{ "ImageView (widgets/ImageView.hpp)", classes = { "section-header" } }, + rcnew Text{ "ImageView (widgets/ImageView.hpp)", classes = { "section-header" } }, - new HLayout{ - new ImageView{ hot_air_balloons(), dimensions = { 180_apx, 120_apx } }, + rcnew HLayout{ + rcnew ImageView{ hot_air_balloons(), dimensions = { 180_apx, 120_apx } }, }, - new Text{ "SVGImageView (widgets/ImageView.hpp)", classes = { "section-header" } }, + rcnew Text{ "SVGImageView (widgets/ImageView.hpp)", classes = { "section-header" } }, - new HLayout{ - new SVGImageView{ toStringView(cat()), dimensions = { 120_apx, 120_apx } }, + rcnew HLayout{ + rcnew SVGImageView{ toStringView(cat()), dimensions = { 120_apx, 120_apx } }, }, - new Text{ "Table (widgets/Table.hpp)", classes = { "section-header" } }, + rcnew Text{ "Table (widgets/Table.hpp)", classes = { "section-header" } }, - new VScrollBox{ + rcnew VScrollBox{ height = 400_apx, - new Table{ + rcnew Table{ flexGrow = 1, backgroundColor = 0xFFFFFF'10_rgba, - new TableHeader{ - new TableCell{ headerCell, new Text{ "Country" } }, - new TableCell{ headerCell, new Text{ "Capital" } }, - new TableCell{ headerCell, new Text{ "Population" }, justifyContent = Justify::FlexEnd }, - new TableCell{ headerCell, new Text{ "Area (km²)" }, justifyContent = Justify::FlexEnd }, + rcnew TableHeader{ + rcnew TableCell{ headerCell, rcnew Text{ "Country" } }, + rcnew TableCell{ headerCell, rcnew Text{ "Capital" } }, + rcnew TableCell{ headerCell, rcnew Text{ "Population" }, + justifyContent = Justify::FlexEnd }, + rcnew TableCell{ headerCell, rcnew Text{ "Area (km²)" }, + justifyContent = Justify::FlexEnd }, }, Builder([](Widget* target) { JsonArray countries = Json::fromJson(std::string(toStringView(countries_json()))) @@ -218,48 +219,52 @@ RC ShowcaseVisual::build(RC notifications) { }); for (Json country : countries) { JsonObject& obj = country.access(); - target->apply(new TableRow{ - new TableCell{ cell, new Text{ obj["country"].to().value_or("") } }, - new TableCell{ cell, new Text{ obj["capital"].to().value_or("") } }, - new TableCell{ - cell, new Text{ fmt::to_string(obj["population"].to().value_or(0)) }, + target->apply(rcnew TableRow{ + rcnew TableCell{ cell, + rcnew Text{ obj["country"].to().value_or("") } }, + rcnew TableCell{ cell, + rcnew Text{ obj["capital"].to().value_or("") } }, + rcnew TableCell{ + cell, + rcnew Text{ fmt::to_string(obj["population"].to().value_or(0)) }, + justifyContent = Justify::FlexEnd }, + rcnew TableCell{ + cell, rcnew Text{ fmt::to_string(obj["area"].to().value_or(0)) }, justifyContent = Justify::FlexEnd }, - new TableCell{ cell, - new Text{ fmt::to_string(obj["area"].to().value_or(0)) }, - justifyContent = Justify::FlexEnd }, }); } }), }, }, - new Table{ + rcnew Table{ flexGrow = 1, Builder{ [this](Widget* target) { for (Row& row : m_rows) { - target->apply(new TableRow{ - new TableCell{ cell, new Text{ row.firstName } }, - new TableCell{ cell, new Text{ row.lastName } }, - new TableCell{ cell, new ComboBox{ value = Value{ &row.index }, width = 100_perc, - new ItemList{ - new Text{ "UX/UI Designer" }, - new Text{ "Project Manager" }, - new Text{ "Software Engineer" }, - new Text{ "Software Developer" }, - } } }, - new TableCell{ cell, new CheckBox{ value = Value{ &row.checkBox }, - new Text{ "Full access" } } }, + target->apply(rcnew TableRow{ + rcnew TableCell{ cell, rcnew Text{ row.firstName } }, + rcnew TableCell{ cell, rcnew Text{ row.lastName } }, + rcnew TableCell{ cell, + rcnew ComboBox{ value = Value{ &row.index }, width = 100_perc, + rcnew ItemList{ + rcnew Text{ "UX/UI Designer" }, + rcnew Text{ "Project Manager" }, + rcnew Text{ "Software Engineer" }, + rcnew Text{ "Software Developer" }, + } } }, + rcnew TableCell{ cell, rcnew CheckBox{ value = Value{ &row.checkBox }, + rcnew Text{ "Full access" } } }, }); } }, }, }, - new Text{ "Hint", classes = { "section-header" } }, + rcnew Text{ "Hint", classes = { "section-header" } }, - new HLayout{ - new Text{ + rcnew HLayout{ + rcnew Text{ "Hej, verden", isHintExclusive = true, hint = Value{ &m_hintActive }.transform([](bool v) -> std::string { @@ -267,7 +272,7 @@ RC ShowcaseVisual::build(RC notifications) { }), }, gapColumn = 10_apx, - new CheckBox{ value = Value{ &m_hintActive }, new Text{ "Show hint" } }, + rcnew CheckBox{ value = Value{ &m_hintActive }, rcnew Text{ "Show hint" } }, }, }; diff --git a/include/brisk/gui/GUI.hpp b/include/brisk/gui/GUI.hpp index 58a8a74..0b772e7 100644 --- a/include/brisk/gui/GUI.hpp +++ b/include/brisk/gui/GUI.hpp @@ -143,22 +143,22 @@ constexpr inline Argument depends{}; } struct SingleBuilder : Builder { - using func = function; + using func = function()>; explicit SingleBuilder(func builder); }; struct IndexedBuilder : Builder { - using func = function; + using func = function(size_t index)>; explicit IndexedBuilder(func builder); }; template struct ListBuilder : IndexedBuilder { - explicit ListBuilder(std::vector list, function&)> fn) + explicit ListBuilder(std::vector list, function(const std::type_identity_t&)> fn) : IndexedBuilder([list = std::move(list), fn = std::move(fn)](size_t index) - BRISK_INLINE_LAMBDA -> Widget* { + BRISK_INLINE_LAMBDA -> RC { return index < list.size() ? fn(list[index]) : nullptr; }) {} }; @@ -311,7 +311,6 @@ class LayoutEngine; struct WidgetArgumentAccept { void operator()(std::shared_ptr); - void operator()(Widget*); void operator()(Builder); void operator()(const Attributes&); void operator()(const Rules&); @@ -950,9 +949,7 @@ class WIDGET Widget : public BindingObject { void remove(Widget* widget); void clear(); - void append(Widget* widget); virtual void append(Widget::Ptr widget); - void apply(Widget* widget); void apply(Widget::Ptr widget); virtual void onParentChanged(); @@ -964,6 +961,8 @@ class WIDGET Widget : public BindingObject { /// @returns false if oldWidget wasn't found bool replace(Ptr oldWidget, Ptr newWidget, bool deep); + void apply(std::nullptr_t) = delete; + void apply(WidgetGroup* group); virtual void onChildAdded(Widget* w); @@ -1593,7 +1592,7 @@ struct Argument> { static_assert(std::constructible_from, "WidgetType must be constructible from empty argument list"); if (!get(parent)) { - parent->apply(new WidgetType{ Arg::role = Name.string() }); + parent->apply(rcnew WidgetType{ Arg::role = Name.string() }); } } }; diff --git a/include/brisk/widgets/Color.hpp b/include/brisk/widgets/Color.hpp index 0948a13..666ed8c 100644 --- a/include/brisk/widgets/Color.hpp +++ b/include/brisk/widgets/Color.hpp @@ -105,7 +105,7 @@ class WIDGET ColorPalette final : public Widget { protected: ColorF m_value = Palette::black; - Widget* addColor(const ColorF& swatch, float brightness = 0.f, float chroma = 1.f); + RC addColor(const ColorF& swatch, float brightness = 0.f, float chroma = 1.f); Ptr cloneThis() override; explicit ColorPalette(Construction construction, ColorF color, ArgumentsView args); diff --git a/include/brisk/widgets/ComboBox.hpp b/include/brisk/widgets/ComboBox.hpp index 90ea2f7..9f01ca9 100644 --- a/include/brisk/widgets/ComboBox.hpp +++ b/include/brisk/widgets/ComboBox.hpp @@ -43,11 +43,11 @@ class WIDGET ComboBox : public ValueWidget { std::shared_ptr>> list, const Args&... args) : ComboBox(Construction{ widgetType }, std::tuple{ args... }) { - std::unique_ptr menu(new ItemList()); + RC menu = rcnew ItemList(); for (const KeyValue& item : *list) { - menu->apply(new Item(new Text{ item.first })); + menu->apply(rcnew Item(rcnew Text{ item.first })); } - apply(menu.release()); + apply(std::move(menu)); endConstruction(); value = fromList(std::move(prop), list); } diff --git a/include/brisk/widgets/Notifications.hpp b/include/brisk/widgets/Notifications.hpp index c3d99d2..e3f0747 100644 --- a/include/brisk/widgets/Notifications.hpp +++ b/include/brisk/widgets/Notifications.hpp @@ -37,7 +37,7 @@ class WIDGET NotificationView final : public Widget { std::tuple{ Arg::layout = Layout::Vertical, makeCloseButton(), - new VLayout{ + rcnew VLayout{ Arg::classes = { "notification-body" }, args..., }, @@ -52,7 +52,7 @@ class WIDGET NotificationView final : public Widget { void expireNow(); protected: - Widget* makeCloseButton(); + RC makeCloseButton(); double m_expireTime; void onEvent(Event& event) override; diff --git a/include/brisk/widgets/Text.hpp b/include/brisk/widgets/Text.hpp index 55ff68a..67226f9 100644 --- a/include/brisk/widgets/Text.hpp +++ b/include/brisk/widgets/Text.hpp @@ -120,8 +120,8 @@ void applier(Text* target, ArgVal, T> value) { target->text = value.value; } -inline Text* operator""_Text(const char* text, size_t size) { - return new Text{ std::string_view(text, size) }; +inline RC operator""_Text(const char* text, size_t size) { + return rcnew Text{ std::string_view(text, size) }; } inline namespace Arg { @@ -161,8 +161,8 @@ struct TextBuilder : IndexedBuilder { template explicit TextBuilder(std::vector texts, const Args&... args) : IndexedBuilder( - [saved_texts = std::move(texts), args...](size_t index) BRISK_INLINE_LAMBDA -> Widget* { - return index < saved_texts.size() ? new Text{ saved_texts[index], args... } : nullptr; + [saved_texts = std::move(texts), args...](size_t index) BRISK_INLINE_LAMBDA -> RC { + return index < saved_texts.size() ? rcnew Text{ saved_texts[index], args... } : nullptr; }) {} }; diff --git a/src/gui/GUI.cpp b/src/gui/GUI.cpp index 25c5393..ad7716b 100644 --- a/src/gui/GUI.cpp +++ b/src/gui/GUI.cpp @@ -605,10 +605,10 @@ IndexedBuilder::IndexedBuilder(IndexedBuilder::func builder) : Builder([builder = std::move(builder)](Widget* target) BRISK_INLINE_LAMBDA -> void { size_t index = 0; for (;;) { - Widget* w = builder(index); + RC w = builder(index); if (!w) return; - target->apply(w); + target->apply(std::move(w)); ++index; } }) {} @@ -1682,20 +1682,11 @@ void Widget::remove(Widget* widget) { removeChild(it); } -void Widget::append(Widget* widget) { - append(Widget::Ptr(widget)); -} - void Widget::apply(Widget::Ptr widget) { if (widget) append(std::move(widget)); } -void Widget::apply(Widget* widget) { - if (widget) - append(Widget::Ptr(widget)); -} - const Widget::WidgetPtrs& Widget::widgets() const { return m_widgets; } diff --git a/src/gui/Styles_test.cpp b/src/gui/Styles_test.cpp index 7720b09..9059776 100644 --- a/src/gui/Styles_test.cpp +++ b/src/gui/Styles_test.cpp @@ -67,7 +67,7 @@ TEST_CASE("Rules") { CHECK(Rules{ shadowSize = 2 }.merge(Rules{ tabSize = 1 }) == Rules{ shadowSize = 2, tabSize = 1 }); CHECK(Rules{}.merge(Rules{ shadowSize = 2, tabSize = 1 }) == Rules{ shadowSize = 2, tabSize = 1 }); - Widget::Ptr w(new Widget{}); + Widget::Ptr w = rcnew Widget{}; Rules{ shadowSize = 2, tabSize = 1 }.applyTo(w.get()); CHECK(w->tabSize.get() == 1); CHECK(w->shadowSize.get() == 2_px); @@ -104,14 +104,14 @@ WidgetProtected* unprotect(std::shared_ptr w) TEST_CASE("Selectors") { using namespace Selectors; - Widget::Ptr w(new Widget{ + Widget::Ptr w = rcnew Widget{ id = "primary", classes = { "success", "large" }, - new Widget{ + rcnew Widget{ classes = { "text" }, }, - }); + }; unprotect(w)->setType("button"); auto child = w->widgets().front(); @@ -151,7 +151,7 @@ TEST_CASE("Selectors") { TEST_CASE("Styles") { using namespace Selectors; using enum WidgetState; - RC ss(new Stylesheet{ + RC ss = rcnew Stylesheet{ Style{ Type{ "button" }, { padding = Edges{ 20 } }, @@ -185,20 +185,20 @@ TEST_CASE("Styles") { Id{ "secondary" }, { shadowSize = 3 }, }, - }); + }; - Widget::Ptr w1(new Widget{ + Widget::Ptr w1 = rcnew Widget{ id = "primary", - }); + }; CHECK(w1->id.get() == "primary"); CHECK(w1->shadowSize.get() == Length(0)); - Widget::Ptr w2(new Widget{ + Widget::Ptr w2 = rcnew Widget{ stylesheet = ss, id = "first", id = "primary", - }); + }; unprotect(w2)->restyleIfRequested(); CHECK(w2->id.get() == "primary"); @@ -299,13 +299,13 @@ TEST_CASE("resolving") { TEST_CASE("inherit") { RC w1 = rcnew Widget{ fontSize = 20_px, - new Widget{ + rcnew Widget{ fontSize = 200_perc, - new Widget{ + rcnew Widget{ // fontSize = inherit }, }, - new Widget{ + rcnew Widget{ // fontSize = inherit }, }; diff --git a/src/widgets/Color.cpp b/src/widgets/Color.cpp index d4132c7..cb7c1dc 100644 --- a/src/widgets/Color.cpp +++ b/src/widgets/Color.cpp @@ -60,7 +60,7 @@ ColorSliders::ColorSliders(Construction construction, ColorF color, bool alpha, args.apply(this); m_layout = Layout::Vertical; - apply(new Slider{ + apply(rcnew Slider{ Arg::value = colorSubvalue(Value{ &value }), Arg::minDimensions = { 100, 16 }, Arg::borderColor = 0xd80000_rgb, @@ -69,7 +69,7 @@ ColorSliders::ColorSliders(Construction construction, ColorF color, bool alpha, Arg::minimum = 0, Arg::maximum = 1, }); - apply(new Slider{ + apply(rcnew Slider{ Arg::value = colorSubvalue(Value{ &value }), Arg::minDimensions = { 100, 16 }, Arg::borderColor = 0x00b600_rgb, @@ -78,7 +78,7 @@ ColorSliders::ColorSliders(Construction construction, ColorF color, bool alpha, Arg::minimum = 0, Arg::maximum = 1, }); - apply(new Slider{ + apply(rcnew Slider{ Arg::value = colorSubvalue(Value{ &value }), Arg::minDimensions = { 100, 16 }, Arg::borderColor = 0x0000e8_rgb, @@ -88,7 +88,7 @@ ColorSliders::ColorSliders(Construction construction, ColorF color, bool alpha, Arg::maximum = 1, }); if (alpha) { - apply(new Slider{ + apply(rcnew Slider{ Arg::value = colorSubvalue(Value{ &value }), Arg::minDimensions = { 100, 16 }, Arg::borderColor = 0xF3F3F3_rgb, @@ -173,7 +173,7 @@ ColorPalette::ColorPalette(Construction construction, ColorF color, ArgumentsVie }) { const float v = p.first; const float c = p.second; - apply(new HLayout{ + apply(rcnew HLayout{ addColor(ColorOf(i++ / 6.f)), // addColor(Palette::Standard::red, v, c), addColor(Palette::Standard::orange, v, c), addColor(Palette::Standard::amber, v, c), addColor(Palette::Standard::yellow, v, c), @@ -200,11 +200,11 @@ static ColorF adjustSwatch(ColorF color, float lightnessOffset, float chromaMult return ColorF(lab, color.alpha); } -Widget* ColorPalette::addColor(const ColorF& swatch, float brightness, float chroma) { +RC ColorPalette::addColor(const ColorF& swatch, float brightness, float chroma) { ColorF c = adjustSwatch(swatch, brightness, chroma); - return new Button{ - new Widget{ - new ColorView{ + return rcnew Button{ + rcnew Widget{ + rcnew ColorView{ c, }, }, @@ -246,10 +246,10 @@ ColorButton::ColorButton(Construction construction, Value prop, bool alp args.apply(this); m_layout = Layout::Vertical; - apply(new ColorView{ prop }); - apply(new PopupBox{ new HLayout{ new ColorView{ prop, Arg::classes = { "large" } }, - new ColorSliders{ prop, alpha, Arg::flexGrow = 1 } }, - new ColorPalette(prop) }); + apply(rcnew ColorView{ prop }); + apply(rcnew PopupBox{ rcnew HLayout{ rcnew ColorView{ prop, Arg::classes = { "large" } }, + rcnew ColorSliders{ prop, alpha, Arg::flexGrow = 1 } }, + rcnew ColorPalette(prop) }); } void GradientItem::paint(Canvas& canvas_) const { diff --git a/src/widgets/ComboBox.cpp b/src/widgets/ComboBox.cpp index a7fe528..684c5d6 100644 --- a/src/widgets/ComboBox.cpp +++ b/src/widgets/ComboBox.cpp @@ -61,9 +61,9 @@ void ComboBox::onConstructed() { selecteditem.create(this); if (!unroll.get(this)) { - apply(new ToggleButton{ Arg::value = Value{ &itemlist.get(this)->visible }, - new Text{ ICON_chevron_down }, new Text{ ICON_chevron_up }, - Arg::role = unroll.role(), Arg::twoState = true }); + apply(rcnew ToggleButton{ Arg::value = Value{ &itemlist.get(this)->visible }, + rcnew Text{ ICON_chevron_down }, rcnew Text{ ICON_chevron_up }, + Arg::role = unroll.role(), Arg::twoState = true }); } Base::onConstructed(); } diff --git a/src/widgets/ContextPopup.cpp b/src/widgets/ContextPopup.cpp index 679fc98..9b619b9 100644 --- a/src/widgets/ContextPopup.cpp +++ b/src/widgets/ContextPopup.cpp @@ -38,7 +38,7 @@ void ContextPopup::append(Widget::Ptr widget) { it->dynamicFocus = true; Base::append(std::move(widget)); } else { - Base::append(new Item{ std::move(widget), dynamicFocus = true }); + Base::append(rcnew Item{ std::move(widget), dynamicFocus = true }); } } } // namespace Brisk diff --git a/src/widgets/DialogComponent.cpp b/src/widgets/DialogComponent.cpp index 41c72fb..e442ccd 100644 --- a/src/widgets/DialogComponent.cpp +++ b/src/widgets/DialogComponent.cpp @@ -54,26 +54,26 @@ void DialogComponent::close(bool result) { RC DialogComponent::dialogButtons(DialogButtons buttons, std::string okBtn, std::string cancelBtn) { return rcnew HLayout{ margin = { 15, 10 }, // - new Spacer{}, + rcnew Spacer{}, (buttons && DialogButtons::OK) - ? new Button{ new Text{ std::move(okBtn) }, id = "dialog-ok", - onClick = listener( - [this] { - accept(); - }, - this), - margin = { 4, 0 } } + ? rcnew Button{ rcnew Text{ std::move(okBtn) }, id = "dialog-ok", + onClick = listener( + [this] { + accept(); + }, + this), + margin = { 4, 0 } } : nullptr, (buttons && DialogButtons::Cancel) - ? new Button{ new Text{ std::move(cancelBtn) }, id = "dialog-cancel", - onClick = listener( - [this] { - reject(); - }, - this), - margin = { 4, 0 } } + ? rcnew Button{ rcnew Text{ std::move(cancelBtn) }, id = "dialog-cancel", + onClick = listener( + [this] { + reject(); + }, + this), + margin = { 4, 0 } } : nullptr, - new Spacer{} }; + rcnew Spacer{} }; } void DialogComponent::rejected() {} @@ -104,8 +104,8 @@ RC TextInputDialog::build() { return rcnew VLayout{ stylesheet = Graphene::stylesheet(), Graphene::darkColors(), - new Text{ this->prompt, margin = { 15, 10 } }, - new TextEditor{ Value{ &this->value }, autofocus = true, margin = { 15, 10 } }, + rcnew Text{ this->prompt, margin = { 15, 10 } }, + rcnew TextEditor{ Value{ &this->value }, autofocus = true, margin = { 15, 10 } }, dialogButtons(DialogButtons::OK | DialogButtons::Cancel), }; } @@ -115,7 +115,7 @@ MessageDialog::MessageDialog(std::string text, std::string icon) RC MessageDialog::build() { return rcnew VLayout{ stylesheet = Graphene::stylesheet(), Graphene::darkColors(), - new Text{ this->text, margin = { 15, 10 } }, + rcnew Text{ this->text, margin = { 15, 10 } }, dialogButtons(DialogButtons::OK | DialogButtons::Cancel) }; } @@ -124,7 +124,7 @@ ConfirmDialog::ConfirmDialog(std::string text, std::string icon) RC ConfirmDialog::build() { return rcnew VLayout{ stylesheet = Graphene::stylesheet(), Graphene::darkColors(), - new Text{ this->text, margin = { 15, 10 } }, + rcnew Text{ this->text, margin = { 15, 10 } }, dialogButtons(DialogButtons::OK | DialogButtons::Cancel) }; } } // namespace Brisk diff --git a/src/widgets/ItemList.cpp b/src/widgets/ItemList.cpp index 935ffe4..3e08ee7 100644 --- a/src/widgets/ItemList.cpp +++ b/src/widgets/ItemList.cpp @@ -51,7 +51,7 @@ void ItemList::append(Widget::Ptr widget) { it->dynamicFocus = true; Base::append(std::move(widget)); } else { - Base::append(new Item{ std::move(widget), dynamicFocus = true }); + Base::append(rcnew Item{ std::move(widget), dynamicFocus = true }); } } diff --git a/src/widgets/ListBox.cpp b/src/widgets/ListBox.cpp index 256e467..da5d782 100644 --- a/src/widgets/ListBox.cpp +++ b/src/widgets/ListBox.cpp @@ -46,7 +46,7 @@ void ListBox::append(Widget::Ptr widget) { if (dynamic_cast(widget.get())) Base::append(std::move(widget)); else - Base::append(new Item{ std::move(widget) }); + Base::append(rcnew Item{ std::move(widget) }); } Widget::Ptr ListBox::cloneThis() { diff --git a/src/widgets/Notifications.cpp b/src/widgets/Notifications.cpp index a0a2a42..e13e8a4 100644 --- a/src/widgets/Notifications.cpp +++ b/src/widgets/Notifications.cpp @@ -31,9 +31,9 @@ void NotificationView::onEvent(Event& event) { } } -Widget* NotificationView::makeCloseButton() { - return new Button{ - new Text{ ICON_x }, +RC NotificationView::makeCloseButton() { + return rcnew Button{ + rcnew Text{ ICON_x }, Arg::classes = { "flat", "slim" }, Arg::placement = Placement::Absolute, Arg::zorder = ZOrder::TopMost, diff --git a/src/widgets/Pages.cpp b/src/widgets/Pages.cpp index 6a24542..7830c30 100644 --- a/src/widgets/Pages.cpp +++ b/src/widgets/Pages.cpp @@ -27,7 +27,7 @@ void Tabs::clearTabs() { } void Tabs::createTab(Value visible, Page* page) { - apply(new TabButton(Arg::value = std::move(visible), new Text{ Arg::text = page->title })); + apply(rcnew TabButton(Arg::value = std::move(visible), rcnew Text{ Arg::text = page->title })); } void Pages::updateTabs() { diff --git a/src/widgets/PopupDialog.cpp b/src/widgets/PopupDialog.cpp index 082c1dd..0431825 100644 --- a/src/widgets/PopupDialog.cpp +++ b/src/widgets/PopupDialog.cpp @@ -41,14 +41,14 @@ PopupDialog::PopupDialog(Construction construction, Value visibleProp, Arg Arg::absolutePosition = { 0, 0 }, Arg::anchor = { 0, 0 }, Arg::zorder = ZOrder::TopMost, - new Spacer{}, - new Widget{ + rcnew Spacer{}, + rcnew Widget{ Arg::classes = { "dialog" }, Arg::layout = Layout::Vertical, Arg::alignSelf = AlignSelf::Center, asAttributes(args), }, - new Spacer{}, + rcnew Spacer{}, }, } {} @@ -62,21 +62,21 @@ PopupOKDialog::PopupOKDialog(Construction construction, std::string title, Value construction, visibleProp, std::tuple{ - new Text{ + rcnew Text{ std::move(title), Arg::classes = { "dialog-title" }, }, - new VLayout{ + rcnew VLayout{ Arg::classes = { "dialog-body" }, asAttributes(args), - new Button{ new Text{ "OK" }, Arg::classes = { "dialog-button" }, - Arg::alignSelf = AlignSelf::Center, - Arg::onClick = listener( - [accepted = std::move(accepted), visibleProp]() { - visibleProp.set(false); - accepted(); - }, - this) }, + rcnew Button{ rcnew Text{ "OK" }, Arg::classes = { "dialog-button" }, + Arg::alignSelf = AlignSelf::Center, + Arg::onClick = listener( + [accepted = std::move(accepted), visibleProp]() { + visibleProp.set(false); + accepted(); + }, + this) }, }, }, } {} diff --git a/src/widgets/Progress.cpp b/src/widgets/Progress.cpp index c6e92d8..d7d4069 100644 --- a/src/widgets/Progress.cpp +++ b/src/widgets/Progress.cpp @@ -65,7 +65,7 @@ Progress::Progress(Construction construction, ArgumentsView args) : Ba m_maximum = 1; args.apply(this); if (!find(MatchAny{})) { - apply(new ProgressBar{}); + apply(rcnew ProgressBar{}); } } } // namespace Brisk diff --git a/src/widgets/SpinBox.cpp b/src/widgets/SpinBox.cpp index 9aecf98..8984aaf 100644 --- a/src/widgets/SpinBox.cpp +++ b/src/widgets/SpinBox.cpp @@ -107,8 +107,8 @@ void UpDownButtons::onChildAdded(Widget* w) { void UpDownButtons::onConstructed() { if (!down.get(this) && !up.get(this)) { - apply(new Button{ new Text{ ICON_chevron_up } }); - apply(new Button{ new Text{ ICON_chevron_down } }); + apply(rcnew Button{ rcnew Text{ ICON_chevron_up } }); + apply(rcnew Button{ rcnew Text{ ICON_chevron_down } }); } Base::onConstructed(); } diff --git a/src/widgets/TextEditor.cpp b/src/widgets/TextEditor.cpp index 4167879..2295aae 100644 --- a/src/widgets/TextEditor.cpp +++ b/src/widgets/TextEditor.cpp @@ -45,60 +45,60 @@ TextEditor::TextEditor(Construction construction, ArgumentsView args } void TextEditor::createContextMenu() { - apply(new ContextPopup{ + apply(rcnew ContextPopup{ Arg::role = "context", Arg::fontFamily = FontFamily::Default, Arg::fontSize = FontSize::Normal, - new Item{ Arg::icon = ICON_scissors, new Text{ "Cut||Menu"_tr }, new Spacer{}, - new Text{ - hotKeyToString(KeyCode::X, KeyModifiers::ControlOrCommand), - Arg::classes = { "hotkeyhint" }, - }, - Arg::onClick = listener( - [this] { - cutToClipboard(); - }, - this) }, - new Item{ Arg::icon = ICON_copy, new Text{ "Copy||Menu"_tr }, new Spacer{}, - new Text{ - hotKeyToString(KeyCode::C, KeyModifiers::ControlOrCommand), - Arg::classes = { "hotkeyhint" }, - }, - Arg::onClick = listener( - [this] { - copyToClipboard(); - }, - this) }, - new Item{ Arg::icon = ICON_clipboard, new Text{ "Paste||Menu"_tr }, new Spacer{}, - new Text{ - hotKeyToString(KeyCode::V, KeyModifiers::ControlOrCommand), - Arg::classes = { "hotkeyhint" }, - }, - Arg::onClick = listener( - [this] { - pasteFromClipboard(); - }, - this) }, - new Item{ Arg::icon = ICON_x, new Text{ "Delete||Menu"_tr }, new Spacer{}, - new Text{ - hotKeyToString(KeyCode::Del, KeyModifiers::None), - Arg::classes = { "hotkeyhint" }, - }, - Arg::onClick = listener( - [this] { - deleteSelection(); - }, - this) }, - new Item{ new Text{ "Select All||Menu"_tr }, new Spacer{}, - new Text{ - hotKeyToString(KeyCode::A, KeyModifiers::ControlOrCommand), - Arg::classes = { "hotkeyhint" }, - }, - Arg::onClick = listener( - [this] { - selectAll(); - }, - this) }, + rcnew Item{ Arg::icon = ICON_scissors, rcnew Text{ "Cut||Menu"_tr }, rcnew Spacer{}, + rcnew Text{ + hotKeyToString(KeyCode::X, KeyModifiers::ControlOrCommand), + Arg::classes = { "hotkeyhint" }, + }, + Arg::onClick = listener( + [this] { + cutToClipboard(); + }, + this) }, + rcnew Item{ Arg::icon = ICON_copy, rcnew Text{ "Copy||Menu"_tr }, rcnew Spacer{}, + rcnew Text{ + hotKeyToString(KeyCode::C, KeyModifiers::ControlOrCommand), + Arg::classes = { "hotkeyhint" }, + }, + Arg::onClick = listener( + [this] { + copyToClipboard(); + }, + this) }, + rcnew Item{ Arg::icon = ICON_clipboard, rcnew Text{ "Paste||Menu"_tr }, rcnew Spacer{}, + rcnew Text{ + hotKeyToString(KeyCode::V, KeyModifiers::ControlOrCommand), + Arg::classes = { "hotkeyhint" }, + }, + Arg::onClick = listener( + [this] { + pasteFromClipboard(); + }, + this) }, + rcnew Item{ Arg::icon = ICON_x, rcnew Text{ "Delete||Menu"_tr }, rcnew Spacer{}, + rcnew Text{ + hotKeyToString(KeyCode::Del, KeyModifiers::None), + Arg::classes = { "hotkeyhint" }, + }, + Arg::onClick = listener( + [this] { + deleteSelection(); + }, + this) }, + rcnew Item{ rcnew Text{ "Select All||Menu"_tr }, rcnew Spacer{}, + rcnew Text{ + hotKeyToString(KeyCode::A, KeyModifiers::ControlOrCommand), + Arg::classes = { "hotkeyhint" }, + }, + Arg::onClick = listener( + [this] { + selectAll(); + }, + this) }, }); } From 56987c72fc84e2b9f64d86e047f6793941b8872b Mon Sep 17 00:00:00 2001 From: Brisk Date: Fri, 20 Dec 2024 08:42:12 +0100 Subject: [PATCH 2/9] Value refactoring --- examples/showcase/src/Typography.cpp | 4 +- include/brisk/core/Binding.hpp | 133 +++++++++++++++++---------- src/core/Binding_test.cpp | 12 +-- src/widgets/Pages.cpp | 2 +- 4 files changed, 91 insertions(+), 60 deletions(-) diff --git a/examples/showcase/src/Typography.cpp b/examples/showcase/src/Typography.cpp index a25d4ae..b869cb1 100644 --- a/examples/showcase/src/Typography.cpp +++ b/examples/showcase/src/Typography.cpp @@ -90,8 +90,8 @@ RC ShowcaseTypography::build(RC notifications) { fontSize = 40, fontFamily = Lato, fontFeatures = Value{ &m_fontFeatures }, - letterSpacing = Value{ &m_letterSpacing }.implicitConversion(), - wordSpacing = Value{ &m_wordSpacing }.implicitConversion(), + letterSpacing = Value{ &m_letterSpacing }, + wordSpacing = Value{ &m_wordSpacing }, textDecoration = Value{ &m_textDecoration }, }, rcnew HLayout{ diff --git a/include/brisk/core/Binding.hpp b/include/brisk/core/Binding.hpp index b394de8..8afd3f9 100644 --- a/include/brisk/core/Binding.hpp +++ b/include/brisk/core/Binding.hpp @@ -218,6 +218,9 @@ constexpr inline bool isValue> = true; template concept AtomicCompatible = std::is_trivially_copyable_v && std::is_copy_assignable_v; +template Fn> +Value> transform(Fn&& fn, const Value&... values); + /** * @brief Value class that manages a value with getter and setter functionality. * @@ -280,23 +283,6 @@ struct Value { (self->*notify)(); })) {} - template - Value implicitConversion() && { - if constexpr (std::is_convertible_v) { - return std::move(*this).transform( - [](T value) -> U { - return static_cast(value); - }, - [](U value) -> T { - return static_cast(value); - }); - } else { - return std::move(*this).transform([](T value) -> U { - return static_cast(value); - }); - } - } - template Value explicitConversion() && { if constexpr (requires(T t, U u) { @@ -502,31 +488,59 @@ struct Value { return Value{ *this }.transform(std::forward(forward)); } - friend inline Value operator==(Value value, std::type_identity_t compare) { + template Fn> + friend Value> binary(Value left, Value right, Fn&& fn) { + return Value{ + [fn = std::move(fn), leftGet = std::move(left.m_get), rightGet = std::move(right.m_get)]() -> T { + return fn(leftGet(), rightGet()); + }, + nullptr, + mergeSmallVectors(std::move(left.m_srcAddresses), std::move(right.m_srcAddresses)), + std::move(left.m_destAddress), + }; + } + + template Fn> + friend Value> binary(Value left, std::type_identity_t right, Fn&& fn) { + return Value{ + [fn = std::move(fn), leftGet = std::move(left.m_get), right = std::move(right)]() -> T { + return fn(leftGet(), right); + }, + nullptr, + std::move(left.m_srcAddresses), + std::move(left.m_destAddress), + }; + } + + template Fn> + friend Value> binary(std::type_identity_t left, Value right, Fn&& fn) { + return Value{ + [fn = std::move(fn), left = std::move(left), rightGet = std::move(right.m_get)]() -> T { + return fn(left, rightGet()); + }, + nullptr, + std::move(right.m_srcAddresses), + std::move(right.m_destAddress), + }; + } + + Value equal(std::type_identity_t compare) && { return Value{ - [get = std::move(value.m_get), compare]() -> bool { + [get = std::move(m_get), compare]() -> bool { return get() == compare; }, - [set = std::move(value.m_set), compare](bool newValue) { + [set = std::move(m_set), compare](bool newValue) { if (newValue) if (set) set(compare); }, - std::move(value.m_srcAddresses), - std::move(value.m_destAddress), + std::move(m_srcAddresses), + std::move(m_destAddress), }; } - template Fn> - friend Value> binary(Value left, Value right, Fn&& fn) { - return Value{ - [fn = std::move(fn), leftGet = std::move(left.m_get), rightGet = std::move(right.m_get)]() { - return fn(leftGet, rightGet); - }, - nullptr, - mergeSmallVectors(std::move(left.m_srcAddresses), std::move(right.m_srcAddresses)), - nullptr, - }; + Value equal(std::type_identity_t compare) const& { + return Value{ *this }.equal(std::move(compare)); } #define BRISK_BINDING_OP(oper, op) \ @@ -620,23 +634,28 @@ struct Value { }); } - friend inline Value operator!=(Value value, std::type_identity_t compare) { - return Value{ - [get = std::move(value.m_get), compare]() -> bool { - return get() != compare; - }, - nullptr, - std::move(value.m_srcAddresses), - nullptr, - }; - } + struct Operand { + const Value& value; - friend inline Value operator==(std::type_identity_t compare, Value value) { - return operator==(std::move(value), std::move(compare)); - } + friend Value operator==(Operand op1, Operand op2) { + return Brisk::transform( + [](T x, T y) -> bool { + return x == y; + }, + op1.value, op2.value); + } + + friend Value operator!=(Operand op1, Operand op2) { + return Brisk::transform( + [](T x, T y) -> bool { + return x != y; + }, + op1.value, op2.value); + } + }; - friend inline Value operator!=(std::type_identity_t compare, Value value) { - return operator!=(std::move(value), std::move(compare)); + Operand operator*() const { + return *this; } const GetFn& getter() const& noexcept { @@ -674,6 +693,22 @@ struct Value { : m_get(std::move(get)), m_set(std::move(set)), m_srcAddresses{ address }, m_destAddress(address) {} private: + template + Value implicitConversion() && { + if constexpr (std::is_convertible_v) { + return std::move(*this).transform( + [](T value) -> U { + return static_cast(value); + }, + [](U value) -> T { + return static_cast(value); + }); + } else { + return std::move(*this).transform([](T value) -> U { + return static_cast(value); + }); + } + } friend class Bindings; template @@ -703,10 +738,6 @@ Value(U*) -> Value; template Value(const U*) -> Value; -namespace Internal { -inline void mergeBindingAddresses(BindingAddresses& target, BindingAddresses a) {} -} // namespace Internal - template Fn> Value> transform(Fn&& fn, const Value&... values) { BindingAddresses addresses; diff --git a/src/core/Binding_test.cpp b/src/core/Binding_test.cpp index c0131fb..bcdfcf3 100644 --- a/src/core/Binding_test.cpp +++ b/src/core/Binding_test.cpp @@ -293,11 +293,11 @@ TEST_CASE("Binding equal Immediate") { BindingRegistration lt{ &data, nullptr }; - bindings->connectBidir(Value{ &data.v0 }, Value{ &data.index } == 0, BindType::Immediate, true, "v0", + bindings->connectBidir(Value{ &data.v0 }, Value{ &data.index }.equal(0), BindType::Immediate, true, "v0", "index==0"); - bindings->connectBidir(Value{ &data.v1 }, Value{ &data.index } == 1, BindType::Immediate, true, "v1", + bindings->connectBidir(Value{ &data.v1 }, Value{ &data.index }.equal(1), BindType::Immediate, true, "v1", "index==1"); - bindings->connectBidir(Value{ &data.v2 }, Value{ &data.index } == 2, BindType::Immediate, true, "v2", + bindings->connectBidir(Value{ &data.v2 }, Value{ &data.index }.equal(2), BindType::Immediate, true, "v2", "index==2"); CHECK(data.v0 == true); CHECK(data.v1 == false); @@ -326,11 +326,11 @@ TEST_CASE("Binding equal Deferred") { BindingRegistration lt{ &data, mainScheduler }; - bindings->connectBidir(Value{ &data.v0 }, Value{ &data.index } == 0, BindType::Deferred, true, "v0", + bindings->connectBidir(Value{ &data.v0 }, Value{ &data.index }.equal(0), BindType::Deferred, true, "v0", "index==0"); - bindings->connectBidir(Value{ &data.v1 }, Value{ &data.index } == 1, BindType::Deferred, true, "v1", + bindings->connectBidir(Value{ &data.v1 }, Value{ &data.index }.equal(1), BindType::Deferred, true, "v1", "index==1"); - bindings->connectBidir(Value{ &data.v2 }, Value{ &data.index } == 2, BindType::Deferred, true, "v2", + bindings->connectBidir(Value{ &data.v2 }, Value{ &data.index }.equal(2), BindType::Deferred, true, "v2", "index==2"); mainScheduler->process(); CHECK(data.v0 == true); diff --git a/src/widgets/Pages.cpp b/src/widgets/Pages.cpp index 7830c30..b6ee565 100644 --- a/src/widgets/Pages.cpp +++ b/src/widgets/Pages.cpp @@ -38,7 +38,7 @@ void Pages::updateTabs() { int index = 0; for (Widget::Ptr w : *this) { if (Page* p = dynamic_cast(w.get())) { - auto prop = Value{ &this->value } == index; + auto prop = Value{ &this->value }.equal(index); tabs->createTab(prop, p); ++index; } From 03be72c225066129d48f0d24ed092c56c42c6d6b Mon Sep 17 00:00:00 2001 From: Brisk Date: Fri, 20 Dec 2024 08:52:04 +0100 Subject: [PATCH 3/9] Fix Widget::clone exception safety --- include/brisk/graphics/Canvas.hpp | 2 +- include/brisk/graphics/Fonts.hpp | 2 +- include/brisk/gui/GUI.hpp | 15 ++++++++------ include/brisk/gui/Groups.hpp | 12 +++++------ include/brisk/widgets/AutoScrollable.hpp | 2 +- include/brisk/widgets/Button.hpp | 2 +- include/brisk/widgets/CheckBox.hpp | 2 +- include/brisk/widgets/Color.hpp | 10 +++++----- include/brisk/widgets/ComboBox.hpp | 2 +- include/brisk/widgets/ContextPopup.hpp | 2 +- include/brisk/widgets/Dot.hpp | 2 +- include/brisk/widgets/Hint.hpp | 2 +- include/brisk/widgets/Hyperlink.hpp | 2 +- include/brisk/widgets/ImageView.hpp | 4 ++-- include/brisk/widgets/Item.hpp | 2 +- include/brisk/widgets/ItemList.hpp | 2 +- include/brisk/widgets/Knob.hpp | 2 +- include/brisk/widgets/Line.hpp | 8 ++++---- include/brisk/widgets/ListBox.hpp | 2 +- include/brisk/widgets/Notifications.hpp | 4 ++-- include/brisk/widgets/Pages.hpp | 8 ++++---- include/brisk/widgets/PopupBox.hpp | 2 +- include/brisk/widgets/PopupButton.hpp | 2 +- include/brisk/widgets/PopupDialog.hpp | 4 ++-- include/brisk/widgets/RadioButton.hpp | 2 +- include/brisk/widgets/ScrollBar.hpp | 2 +- include/brisk/widgets/ScrollBox.hpp | 6 +++--- include/brisk/widgets/Slider.hpp | 2 +- include/brisk/widgets/Spacer.hpp | 2 +- include/brisk/widgets/SpinBox.hpp | 4 ++-- include/brisk/widgets/Switch.hpp | 2 +- include/brisk/widgets/Table.hpp | 12 +++++------ include/brisk/widgets/Text.hpp | 6 +++--- include/brisk/widgets/TextEditor.hpp | 2 +- include/brisk/widgets/ToggleButton.hpp | 2 +- include/brisk/widgets/ValueWidget.hpp | 2 +- src/graphics/Renderer_test.cpp | 3 +-- .../WindowRenderTarget_Linux.cpp | 10 +++++----- src/gui/GUI.cpp | 6 +++--- src/widgets/AutoScrollable.cpp | 4 ++-- src/widgets/Button.cpp | 4 ++-- src/widgets/CheckBox.cpp | 4 +--- src/widgets/Color.cpp | 20 ++++++------------- src/widgets/ComboBox.cpp | 7 +++---- src/widgets/ContextPopup.cpp | 4 +--- src/widgets/Dot.cpp | 4 ++-- src/widgets/Hint.cpp | 4 ++-- src/widgets/Hyperlink.cpp | 4 +--- src/widgets/ImageView.cpp | 8 ++------ src/widgets/Item.cpp | 4 +--- src/widgets/ItemList.cpp | 4 ++-- src/widgets/Knob.cpp | 4 ++-- src/widgets/Line.cpp | 16 +++++---------- src/widgets/ListBox.cpp | 7 +++---- src/widgets/Notifications.cpp | 8 ++++---- src/widgets/Pages.cpp | 16 ++++----------- src/widgets/PopupBox.cpp | 4 ++-- src/widgets/PopupButton.cpp | 4 ++-- src/widgets/PopupDialog.cpp | 8 ++------ src/widgets/RadioButton.cpp | 4 +--- src/widgets/ScrollBar.cpp | 4 ++-- src/widgets/ScrollBox.cpp | 12 ++++------- src/widgets/Slider.cpp | 4 +--- src/widgets/Spacer.cpp | 7 +++---- src/widgets/SpinBox.cpp | 8 +++----- src/widgets/Switch.cpp | 7 +++---- src/widgets/Table.cpp | 16 ++++----------- src/widgets/Text.cpp | 12 +++++------ src/widgets/TextEditor.cpp | 4 ++-- src/widgets/ToggleButton.cpp | 4 +--- src/widgets/ValueWidget.cpp | 4 ++-- 71 files changed, 161 insertions(+), 223 deletions(-) diff --git a/include/brisk/graphics/Canvas.hpp b/include/brisk/graphics/Canvas.hpp index 45b61a9..3a5f4bd 100644 --- a/include/brisk/graphics/Canvas.hpp +++ b/include/brisk/graphics/Canvas.hpp @@ -344,7 +344,7 @@ class Canvas : protected RawCanvas { * @param text The PreparedText struct to render. */ void fillText(PointF position, const PreparedText& text); - + void fillText(PointF position, PointF alignment, const PreparedText& text); /** diff --git a/include/brisk/graphics/Fonts.hpp b/include/brisk/graphics/Fonts.hpp index 74da5ef..f97ee6b 100644 --- a/include/brisk/graphics/Fonts.hpp +++ b/include/brisk/graphics/Fonts.hpp @@ -789,7 +789,7 @@ struct PreparedText { * @return uint32_t The index of the nearest grapheme boundary. */ uint32_t caretToGrapheme(PointF pt) const; - + uint32_t caretToGrapheme(uint32_t line, float x) const; /** diff --git a/include/brisk/gui/GUI.hpp b/include/brisk/gui/GUI.hpp index 0b772e7..b7b3ad1 100644 --- a/include/brisk/gui/GUI.hpp +++ b/include/brisk/gui/GUI.hpp @@ -519,17 +519,20 @@ using Fn0Type = Type (*)(); template using Fn1Type = Type (*)(Widget*); -template U> -U* fixClone(U* ptr) noexcept { +template U> +void fixClone(U* ptr) noexcept { if constexpr (requires { typename U::Base; }) { fixClone(static_cast(ptr)); } ptr->propInit = ptr; - return ptr; } #define BRISK_CLONE_IMPLEMENTATION \ - return Ptr(Internal::fixClone(new std::remove_cvref_t(*this))); + { \ + auto result = rcnew std::remove_cvref_t(*this); \ + Internal::fixClone(result.get()); \ + return result; \ + } } // namespace Internal @@ -622,7 +625,7 @@ class WIDGET Widget : public BindingObject { Widget& operator=(const Widget&) = delete; Widget& operator=(Widget&&) = delete; - Ptr clone(); + Ptr clone() const; constexpr static std::string_view widgetType = "widget"; @@ -1147,7 +1150,7 @@ class WIDGET Widget : public BindingObject { void enableCustomMeasure() noexcept; - virtual Ptr cloneThis(); + virtual Ptr cloneThis() const; void requestAnimationFrame(); void animationFrame(); diff --git a/include/brisk/gui/Groups.hpp b/include/brisk/gui/Groups.hpp index d7f8809..4355ce3 100644 --- a/include/brisk/gui/Groups.hpp +++ b/include/brisk/gui/Groups.hpp @@ -70,16 +70,14 @@ class WIDGET WidgetWithGroup final : public Widget { endConstruction(); } - void apply(Widget* w) { - Widget::apply(w); - if (w) - w->apply(&group); + void append(Widget::Ptr widget) override { + if (widget) + widget->apply(&group); + Widget::append(std::move(widget)); } protected: - Ptr cloneThis() { - BRISK_CLONE_IMPLEMENTATION; - } + Ptr cloneThis() const { BRISK_CLONE_IMPLEMENTATION } WidgetGroup group; }; diff --git a/include/brisk/widgets/AutoScrollable.hpp b/include/brisk/widgets/AutoScrollable.hpp index 1bbfbd9..c858d51 100644 --- a/include/brisk/widgets/AutoScrollable.hpp +++ b/include/brisk/widgets/AutoScrollable.hpp @@ -55,7 +55,7 @@ class WIDGET AutoScrollable : public Widget { void onAnimationFrame() override; Rectangle chevronRect(LogicalDirection direction) const; void onEvent(Event& event) override; - Ptr cloneThis() override; + Ptr cloneThis() const override; explicit AutoScrollable(Construction construction, Orientation orientation, ArgumentsView args); }; diff --git a/include/brisk/widgets/Button.hpp b/include/brisk/widgets/Button.hpp index 4521f50..cf98aa1 100644 --- a/include/brisk/widgets/Button.hpp +++ b/include/brisk/widgets/Button.hpp @@ -63,7 +63,7 @@ class WIDGET Button : public Widget { void onRefresh() override; virtual void onClicked(); void doClick(); - Ptr cloneThis() override; + Ptr cloneThis() const override; explicit Button(Construction construction, ArgumentsView