diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1ef1f14..0109d20 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -117,6 +117,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: ${{matrix.triplet}} + retention-days: 90 path: | Brisk-*.tar.xz @@ -151,6 +152,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: ${{matrix.triplet}} + retention-days: 90 path: | Brisk-*.tar.xz @@ -198,6 +200,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: ${{matrix.triplet}} + retention-days: 90 path: | Brisk-*.tar.xz @@ -231,6 +234,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: test-artifacts-${{github.job}} + retention-days: 10 path: | build/*.png build/*.log @@ -264,6 +268,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: test-artifacts-${{github.job}} + retention-days: 10 path: | build/*.png build/*.log @@ -300,6 +305,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: test-artifacts-${{github.job}} + retention-days: 10 path: | build/*.png build/*.log @@ -331,6 +337,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: test-artifacts-${{github.job}} + retention-days: 10 path: | build/*.png build/*.log @@ -362,6 +369,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: test-artifacts-${{github.job}} + retention-days: 10 path: | build/*.png build/*.log @@ -399,6 +407,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: test-artifacts-${{github.job}} + retention-days: 10 path: | build/*.png build/*.log @@ -432,6 +441,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: test-artifacts-${{github.job}} + retention-days: 10 path: | build/*.png build/*.log @@ -482,6 +492,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: showcase-${{matrix.triplet}} + retention-days: 30 path: | build/showcase @@ -526,6 +537,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: showcase-${{matrix.triplet}} + retention-days: 30 path: | build/Release/showcase.app @@ -571,6 +583,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: showcase-${{matrix.triplet}} + retention-days: 30 path: | build/Release/showcase.exe diff --git a/CMakeLists.txt b/CMakeLists.txt index 770b24e..ed250ac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,6 +82,11 @@ if (WIN32 AND BRISK_GENERATE_MAP) add_link_options(/Map) endif () +if (BRISK_SAN) + add_compile_options(-fsanitize=${BRISK_SAN}) + add_link_options(-fsanitize=${BRISK_SAN}) +endif () + set(DEPS_DIR "${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}" CACHE STRING "DEPS_DIR") 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..b869cb1 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,51 +76,51 @@ 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, 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 }, }, - 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/core/BasicTypes.hpp b/include/brisk/core/BasicTypes.hpp index a76faa1..e8ead20 100644 --- a/include/brisk/core/BasicTypes.hpp +++ b/include/brisk/core/BasicTypes.hpp @@ -868,6 +868,16 @@ struct Overload : Ts... { template Overload(Ts&&...) -> Overload; +template +bool isAligned(T* pointer) { + constexpr size_t Alignment = sizeof(T) * Factor; + if constexpr (std::has_single_bit(Alignment)) { + return (reinterpret_cast(pointer) & (Alignment - 1)) == 0; + } else { + return false; + } +} + } // namespace Brisk namespace fmt { 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/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/graphics/Matrix.hpp b/include/brisk/graphics/Matrix.hpp index 11b129f..d5e71b4 100644 --- a/include/brisk/graphics/Matrix.hpp +++ b/include/brisk/graphics/Matrix.hpp @@ -505,13 +505,20 @@ struct MatrixOf { constexpr size_t N = 8; constexpr size_t N2 = N * 2; size_t i = 0; - SIMD ad = repeat(SIMD{ a, d }); - SIMD cb = repeat(SIMD{ c, b }); - SIMD ef = repeat(SIMD{ e, f }); - for (; i + N - 1 < points.size(); i += N) { - SIMD xy = *reinterpret_cast*>(points.data() + i); - xy = xy * ad + swapAdjacent(xy) * cb + ef; - *reinterpret_cast*>(points.data() + i) = xy; + + if (isAligned(points.data()) && points.size() >= N) { + SIMD ad = repeat(SIMD{ a, d }); + SIMD cb = repeat(SIMD{ c, b }); + SIMD ef = repeat(SIMD{ e, f }); + + for (; i < points.size() && !isAligned(points.data() + i); ++i) { + points[i] = transform(points[i]); + } + for (; i + N - 1 < points.size(); i += N) { + SIMD xy = *reinterpret_cast*>(points.data() + i); + xy = xy * ad + swapAdjacent(xy) * cb + ef; + *reinterpret_cast*>(points.data() + i) = xy; + } } for (; i < points.size(); ++i) { points[i] = transform(points[i]); diff --git a/include/brisk/gui/GUI.hpp b/include/brisk/gui/GUI.hpp index 58a8a74..b7b3ad1 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&); @@ -520,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 @@ -623,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"; @@ -950,9 +952,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 +964,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); @@ -1148,7 +1150,7 @@ class WIDGET Widget : public BindingObject { void enableCustomMeasure() noexcept; - virtual Ptr cloneThis(); + virtual Ptr cloneThis() const; void requestAnimationFrame(); void animationFrame(); @@ -1593,7 +1595,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/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