From 87cb949ad387919e1d2aa0737350dd10e1835041 Mon Sep 17 00:00:00 2001 From: Florian Blasius Date: Wed, 22 Feb 2023 11:44:37 +0000 Subject: [PATCH] energy mng demo: settings menu (#2263) * energy-mng: settings menu * SettingsOverview * About * Settings page * IconButton * ScrollView * ListView * Item / ItemGroup * CheckBox * RadioButton * Switch * emng: code review * Update examples/energy_mng/ui/pages/menu_page/settings.slint Co-authored-by: Olivier Goffart --------- Co-authored-by: Olivier Goffart --- examples/energy_mng/ui/assets/arrow-left.png | Bin 184 -> 0 bytes .../ui/assets/arrow-left.png.license | 2 - examples/energy_mng/ui/assets/arrow-left.svg | 6 + examples/energy_mng/ui/assets/arrow-right.png | Bin 179 -> 0 bytes .../ui/assets/arrow-right.png.license | 2 - examples/energy_mng/ui/assets/arrow-right.svg | 6 + examples/energy_mng/ui/assets/check.svg | 6 + examples/energy_mng/ui/assets/information.svg | 6 + examples/energy_mng/ui/assets/settings.svg | 6 + .../ui/components/state_layer.slint | 25 ++- examples/energy_mng/ui/main_window.slint | 14 +- .../energy_mng/ui/pages/menu_page/about.slint | 73 +++++++ .../ui/pages/menu_page/menu_overview.slint | 56 +++++ .../ui/pages/menu_page/menu_page.slint | 65 ++++++ .../ui/pages/menu_page/settings.slint | 200 ++++++++++++++++++ examples/energy_mng/ui/pages/page.slint | 6 +- examples/energy_mng/ui/pages/pages.slint | 6 +- examples/energy_mng/ui/theme.slint | 4 +- .../energy_mng/ui/widgets/check_box.slint | 42 ++++ .../energy_mng/ui/widgets/icon_button.slint | 37 ++++ examples/energy_mng/ui/widgets/item.slint | 74 +++++++ .../energy_mng/ui/widgets/list_view.slint | 90 ++++++++ examples/energy_mng/ui/widgets/menu.slint | 15 ++ .../energy_mng/ui/widgets/navigation.slint | 8 +- .../energy_mng/ui/widgets/radio_button.slint | 38 ++++ .../energy_mng/ui/widgets/scroll_view.slint | 117 ++++++++++ examples/energy_mng/ui/widgets/switch.slint | 47 ++++ .../energy_mng/ui/widgets/tab_widget.slint | 4 +- examples/energy_mng/ui/widgets/widgets.slint | 16 +- 29 files changed, 944 insertions(+), 27 deletions(-) delete mode 100644 examples/energy_mng/ui/assets/arrow-left.png delete mode 100644 examples/energy_mng/ui/assets/arrow-left.png.license create mode 100644 examples/energy_mng/ui/assets/arrow-left.svg delete mode 100644 examples/energy_mng/ui/assets/arrow-right.png delete mode 100644 examples/energy_mng/ui/assets/arrow-right.png.license create mode 100644 examples/energy_mng/ui/assets/arrow-right.svg create mode 100644 examples/energy_mng/ui/assets/check.svg create mode 100644 examples/energy_mng/ui/assets/information.svg create mode 100644 examples/energy_mng/ui/assets/settings.svg create mode 100644 examples/energy_mng/ui/pages/menu_page/about.slint create mode 100644 examples/energy_mng/ui/pages/menu_page/menu_overview.slint create mode 100644 examples/energy_mng/ui/pages/menu_page/menu_page.slint create mode 100644 examples/energy_mng/ui/pages/menu_page/settings.slint create mode 100644 examples/energy_mng/ui/widgets/check_box.slint create mode 100644 examples/energy_mng/ui/widgets/icon_button.slint create mode 100644 examples/energy_mng/ui/widgets/item.slint create mode 100644 examples/energy_mng/ui/widgets/list_view.slint create mode 100644 examples/energy_mng/ui/widgets/radio_button.slint create mode 100644 examples/energy_mng/ui/widgets/scroll_view.slint create mode 100644 examples/energy_mng/ui/widgets/switch.slint diff --git a/examples/energy_mng/ui/assets/arrow-left.png b/examples/energy_mng/ui/assets/arrow-left.png deleted file mode 100644 index 5a6ce17d7b9fc1d8378d22023512ff15d39c3025..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 184 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP>``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eBzOSc?V@L(#+sOwx85B5N_g{3#4VitVBR7SW zHCAWSYW^mN`@1=15}w(}nfO!+);;<1Xx5qoU)df`m(z+iacXv5C-`y39utK{f)W3< a-kJ)D{$Y6eINB0uFoUP7pUXO@geCxRp*SP} diff --git a/examples/energy_mng/ui/assets/arrow-left.png.license b/examples/energy_mng/ui/assets/arrow-left.png.license deleted file mode 100644 index 0cb20e9df5c..00000000000 --- a/examples/energy_mng/ui/assets/arrow-left.png.license +++ /dev/null @@ -1,2 +0,0 @@ -SPDX-FileCopyrightText: Copyright © SixtyFPS GmbH -SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial diff --git a/examples/energy_mng/ui/assets/arrow-left.svg b/examples/energy_mng/ui/assets/arrow-left.svg new file mode 100644 index 00000000000..edefc140df3 --- /dev/null +++ b/examples/energy_mng/ui/assets/arrow-left.svg @@ -0,0 +1,6 @@ + + + + + diff --git a/examples/energy_mng/ui/assets/arrow-right.png b/examples/energy_mng/ui/assets/arrow-right.png deleted file mode 100644 index fb2e3785cffcc93f9e0540c575e74dad78d3b03c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 179 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP>``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eBzK5raV@L(#+y0Hb30m5_#ep-y@ -SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial diff --git a/examples/energy_mng/ui/assets/arrow-right.svg b/examples/energy_mng/ui/assets/arrow-right.svg new file mode 100644 index 00000000000..83153f16c5f --- /dev/null +++ b/examples/energy_mng/ui/assets/arrow-right.svg @@ -0,0 +1,6 @@ + + + + + diff --git a/examples/energy_mng/ui/assets/check.svg b/examples/energy_mng/ui/assets/check.svg new file mode 100644 index 00000000000..45c29dc0e65 --- /dev/null +++ b/examples/energy_mng/ui/assets/check.svg @@ -0,0 +1,6 @@ + + + + + diff --git a/examples/energy_mng/ui/assets/information.svg b/examples/energy_mng/ui/assets/information.svg new file mode 100644 index 00000000000..74da2c438fa --- /dev/null +++ b/examples/energy_mng/ui/assets/information.svg @@ -0,0 +1,6 @@ + + + + + diff --git a/examples/energy_mng/ui/assets/settings.svg b/examples/energy_mng/ui/assets/settings.svg new file mode 100644 index 00000000000..02712d98119 --- /dev/null +++ b/examples/energy_mng/ui/assets/settings.svg @@ -0,0 +1,6 @@ + + + + + diff --git a/examples/energy_mng/ui/components/state_layer.slint b/examples/energy_mng/ui/components/state_layer.slint index 36b0abcfa34..0908309e365 100644 --- a/examples/energy_mng/ui/components/state_layer.slint +++ b/examples/energy_mng/ui/components/state_layer.slint @@ -3,19 +3,24 @@ import { Theme } from "../theme.slint"; -export component StateLayer inherits Rectangle { - callback clicked <=> i-touch-area.clicked; +export component StateLayer inherits TouchArea { + in property border-radius <=> i-container.border-radius; + + width: 100%; + height: 100%; - background: Theme.palette.pure-black; - opacity: 0.0; - - i-touch-area := TouchArea {} - - animate background { duration: Theme.durations.medium; } + i-container := Rectangle { + width: 100%; + height: 100%; + background: Theme.palette.pure-black; + opacity: 0.0; + + animate background { duration: Theme.durations.medium; } + } states [ - pressed when i-touch-area.pressed : { - opacity: 0.12; + pressed when root.pressed : { + i-container.opacity: 0.12; } ] } \ No newline at end of file diff --git a/examples/energy_mng/ui/main_window.slint b/examples/energy_mng/ui/main_window.slint index 182842a3cab..76df86cc893 100644 --- a/examples/energy_mng/ui/main_window.slint +++ b/examples/energy_mng/ui/main_window.slint @@ -3,8 +3,9 @@ import { Theme } from "theme.slint"; import { Navigation, MenuButton, Menu, Value } from "widgets/widgets.slint"; -import { Balance, BalanceAdapter, Overview, OverviewAdapter, Usage, UsageAdapter, Weather, WeatherAdapter } from "pages/pages.slint"; -export { OverviewAdapter, UsageAdapter, Value, WeatherAdapter } +import { Balance, BalanceAdapter, Overview, OverviewAdapter, Usage, UsageAdapter, Weather, WeatherAdapter, + MenuPage, MenuPageAdapter, MenuOverviewAdapter, SettingsAdapter } from "pages/pages.slint"; +export { OverviewAdapter, UsageAdapter, Value, WeatherAdapter, MenuPageAdapter, MenuOverviewAdapter, SettingsAdapter } export component MainWindow inherits Window { title: "EnergyMNG Demo"; @@ -13,8 +14,8 @@ export component MainWindow inherits Window { background: Theme.palette.pure-black; i-navigation := Navigation { + current-index <=> MenuOverviewAdapter.current-page; page-count: 4; - current-index: 2; Overview { index: 0; @@ -35,6 +36,7 @@ export component MainWindow inherits Window { pagination-clicked => { i-menu.show-button(); + i-navigation.hide(); } clicked => { @@ -49,5 +51,11 @@ export component MainWindow inherits Window { end-y: 9px; width: parent.width - 8px; height: parent.height - 14px; + + MenuPage { + page-changed => { + i-menu.hide(); + } + } } } \ No newline at end of file diff --git a/examples/energy_mng/ui/pages/menu_page/about.slint b/examples/energy_mng/ui/pages/menu_page/about.slint new file mode 100644 index 00000000000..cf91b66d609 --- /dev/null +++ b/examples/energy_mng/ui/pages/menu_page/about.slint @@ -0,0 +1,73 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial + +import { Theme } from "../../theme.slint"; +import { Page } from "../page.slint"; +import { IconButton } from "../../widgets/widgets.slint"; + +export component About inherits Page { + callback back <=> i-back-button.clicked; + + padding-left: 0; + padding-right: 0; + + VerticalLayout { + padding-left: Theme.spaces.medium; + padding-right: Theme.spaces.medium; + padding-top: Theme.spaces.medium; + padding-bottom: Theme.spaces.large; + spacing: Theme.spaces.medium; + + HorizontalLayout { + vertical-stretch: 0; + alignment: start; + + i-back-button := IconButton { + horizontal-stretch: 0; + icon: @image-url("../../assets/arrow-left.svg"); + } + } + + Text { + vertical-stretch: 0; + horizontal-alignment: center; + text: "Developed by"; + color: Theme.palette.slint-blue-100; + font-size: Theme.typo.desciption.size; + font-weight: Theme.typo.desciption.weight; + } + + Image { + preferred-height: 45px; + source: @image-url("../../../../../logo/slint-logo-simple-white.svg"); + } + + // spacer + Rectangle { + vertical-stretch: 1; + } + + Text { + vertical-stretch: 0; + horizontal-alignment: center; + text: "Designed by"; + color: Theme.palette.slint-blue-100; + font-size: Theme.typo.desciption.size; + font-weight: Theme.typo.desciption.weight; + } + + Text { + vertical-stretch: 0; + horizontal-alignment: center; + text: "Spyrosoft"; + color: Theme.palette.white; + font-size: Theme.typo.header-item.size; + font-weight: Theme.typo.header-item.weight; + } + + // Image { + // preferred-height: 32px; + // source: @image-url("../assets/logo-spyrosoft.svg"); + // } + } +} \ No newline at end of file diff --git a/examples/energy_mng/ui/pages/menu_page/menu_overview.slint b/examples/energy_mng/ui/pages/menu_page/menu_overview.slint new file mode 100644 index 00000000000..8e9d0c97071 --- /dev/null +++ b/examples/energy_mng/ui/pages/menu_page/menu_overview.slint @@ -0,0 +1,56 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial + +import { Theme } from "../../theme.slint"; +import { About } from "about.slint"; +import { Page } from "../page.slint"; +import { IconButton, ListView } from "../../widgets/widgets.slint"; + +export global MenuOverviewAdapter { + in property <[StandardListViewItem]> model: [ + { text: "Production & Self-consumption"}, + { text: "Usage"}, + { text: "Balance"}, + { text: "Weather"}, + ]; + in-out property current-page; +} + +export component MenuOverview inherits Page { + callback page-changed <=> i-page-list.selection-changed; + callback show-settings <=> i-settings-button.clicked; + callback show-about <=> i-about-button.clicked; + + in property <[StandardListViewItem]> model <=> MenuOverviewAdapter.model; + in-out property current-page <=> MenuOverviewAdapter.current-page; + + padding-left: 0; + padding-right: 0; + + VerticalLayout { + padding: Theme.spaces.medium; + spacing: Theme.spaces.small; + + HorizontalLayout { + vertical-stretch: 0; + + i-settings-button := IconButton { + icon: @image-url("../../assets/settings.svg"); + } + + // spacer + Rectangle { + horizontal-stretch: 1; + } + + i-about-button := IconButton { + icon: @image-url("../../assets/information.svg"); + } + } + + i-page-list := ListView { + model: root.model; + selected-index <=> root.current-page; + } + } +} \ No newline at end of file diff --git a/examples/energy_mng/ui/pages/menu_page/menu_page.slint b/examples/energy_mng/ui/pages/menu_page/menu_page.slint new file mode 100644 index 00000000000..669ae6dd6df --- /dev/null +++ b/examples/energy_mng/ui/pages/menu_page/menu_page.slint @@ -0,0 +1,65 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial + +import { About } from "about.slint"; +import { MenuOverview, MenuOverviewAdapter } from "menu_overview.slint"; +import { Settings, SettingsAdapter } from "settings.slint"; +import { Theme } from "../../theme.slint"; + +export { MenuOverviewAdapter, SettingsAdapter } + +export global MenuPageAdapter { + in property <[StandardListViewItem]> model: [ + { text: "Production & Self-consumption"}, + { text: "Usage"}, + { text: "Balance"}, + { text: "Weather"}, + ]; + in-out property selected-index; +} + +export component MenuPage { + callback page-changed <=> i-overview.page-changed; + + private property current-index; + private property show-settings; + + i-overview := MenuOverview { + index: 0; + current-index: root.current-index; + + show-settings => { + show-settings = true; + current-index = 1; + + } + show-about => { + show-settings = false; + current-index = 1; + } + } + + About { + visible: !show-settings; + index: 1; + current-index: root.current-index; + + back => { + back(); + } + } + + Settings { + visible: show-settings; + index: 1; + current-index: root.current-index; + + back => { + back(); + } + } + + function back() { + current-index = 0; + } + } \ No newline at end of file diff --git a/examples/energy_mng/ui/pages/menu_page/settings.slint b/examples/energy_mng/ui/pages/menu_page/settings.slint new file mode 100644 index 00000000000..d1257983a85 --- /dev/null +++ b/examples/energy_mng/ui/pages/menu_page/settings.slint @@ -0,0 +1,200 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial + +import { Theme } from "../../theme.slint"; +import { About } from "about.slint"; +import { Page } from "../page.slint"; +import { IconButton, CheckBox, RadioButton, Switch, ScrollView, Item, ItemGroupBox } from "../../widgets/widgets.slint"; + +export global SettingsAdapter { + callback check-radio-option(int /* index */); + // fuctions + in-out property fuction-one-checked: true; + in-out property fuction-two-checked; + in-out property fuction-three-checked; + + // radio options + in-out property radio-option-one-checked: true; + in-out property radio-option-two-checked: false; + + // check options + in-out property check-option-one-checked: true; + in-out property check-option-two-checked; + in-out property check-option-three-checked; + + check-radio-option(index) => { + if(index == 0) { + radio-option-one-checked = true; + radio-option-two-checked = false; + return; + } + + radio-option-one-checked = false; + radio-option-two-checked = true; + } +} + +export component Settings inherits Page { + callback back <=> i-back-button.clicked; + callback check-radio-option <=> SettingsAdapter.check-radio-option; + + // fuctions + in-out property fuction-one-checked <=> SettingsAdapter.fuction-one-checked; + in-out property fuction-two-checked <=> SettingsAdapter.fuction-two-checked; + in-out property fuction-three-checked <=> SettingsAdapter.fuction-three-checked; + + // radio options + in-out property radio-option-one-checked <=> SettingsAdapter.radio-option-one-checked; + in-out property radio-option-two-checked <=> SettingsAdapter.radio-option-two-checked; + + // check options + in-out property check-option-one-checked <=> SettingsAdapter.check-option-one-checked; + in-out property check-option-two-checked <=> SettingsAdapter.check-option-two-checked; + in-out property check-option-three-checked <=> SettingsAdapter.check-option-three-checked; + + padding-left: 0; + padding-right: 0; + + VerticalLayout { + padding-left: Theme.spaces.medium; + padding-right: Theme.spaces.medium; + padding-top: Theme.spaces.medium; + padding-bottom: Theme.spaces.large; + spacing: Theme.spaces.medium; + + HorizontalLayout { + vertical-stretch: 0; + alignment: start; + + i-back-button := IconButton { + horizontal-stretch: 0; + icon: @image-url("../../assets/arrow-left.svg"); + } + } + + // spacer + ScrollView { + vertical-stretch: 1; + + VerticalLayout { + alignment: start; + spacing: Theme.spaces.medium; + + Item { + text: "Function One"; + + i-function-one-check-box := CheckBox { + checked <=> root.fuction-one-checked; + } + + clicked => { + i-function-one-check-box.clicked(); + } + } + + ItemGroupBox { + title: "Subtitle"; + + Item { + text: "Option One"; + has-separator: true; + + i-radio-option-one := RadioButton { + checked: root.radio-option-one-checked; + + clicked => { + root.check-radio-option(0); + } + } + + clicked => { + i-radio-option-one.clicked(); + } + } + + Item { + text: "Option Two"; + + i-radio-option-two := RadioButton { + checked: root.radio-option-two-checked; + + clicked => { + root.check-radio-option(1); + } + } + + clicked => { + i-radio-option-two.clicked(); + } + } + } + + ItemGroupBox { + title: "Subtitle"; + + Item { + text: "Option One"; + has-separator: true; + + i-check-option-one := CheckBox { + checked <=> root.check-option-one-checked; + } + + clicked => { + i-check-option-one.clicked(); + } + } + + Item { + text: "Option Two"; + has-separator: true; + + i-check-option-two := CheckBox { + checked <=> root.check-option-two-checked; + } + + clicked => { + i-check-option-two.clicked(); + } + } + + Item { + text: "Option Three"; + + i-check-option-three := CheckBox { + checked <=> root.check-option-three-checked; + } + + clicked => { + i-check-option-three.clicked(); + } + } + } + + Item { + text: "Function Two"; + + i-fuction-two := Switch { + checked <=> root.fuction-two-checked; + } + + clicked => { + i-fuction-two.clicked(); + } + } + + Item { + text: "Function Three"; + + i-fuction-three := Switch { + checked <=> root.fuction-three-checked; + } + + clicked => { + i-fuction-three.clicked(); + } + } + } + } + } +} \ No newline at end of file diff --git a/examples/energy_mng/ui/pages/page.slint b/examples/energy_mng/ui/pages/page.slint index 7c029986a69..5d833e28599 100644 --- a/examples/energy_mng/ui/pages/page.slint +++ b/examples/energy_mng/ui/pages/page.slint @@ -8,6 +8,8 @@ export component Page inherits Rectangle { in property current-index; out property active: index == current-index; + padding-left: 14px; + padding-right: 14px; x: (index - current-index) * self.width; width: 100%; height: 100%; @@ -15,8 +17,8 @@ export component Page inherits Rectangle { animate x { duration: Theme.durations.fast; } GridLayout { - padding-left: 14px; - padding-right: 14px; + padding-left: root.padding-left; + padding-right: root.padding-right; @children } diff --git a/examples/energy_mng/ui/pages/pages.slint b/examples/energy_mng/ui/pages/pages.slint index ff22e0d4995..d3f0a446f71 100644 --- a/examples/energy_mng/ui/pages/pages.slint +++ b/examples/energy_mng/ui/pages/pages.slint @@ -7,10 +7,12 @@ import { Usage, UsageAdapter } from "usage.slint"; import { Weather, WeatherAdapter } from "weather.slint"; import { Page } from "page.slint"; import { Dashboard } from "dashboard.slint"; +import { MenuPage, MenuPageAdapter, MenuOverviewAdapter, SettingsAdapter } from "menu_page/menu_page.slint"; export { Balance, BalanceAdapter } export { Overview, OverviewAdapter } -export { Usage, UsageAdapter } +export { Usage, UsageAdapter } export { Weather, WeatherAdapter } export { Page } -export { Dashboard } \ No newline at end of file +export { Dashboard } +export { MenuPage, MenuPageAdapter, MenuOverviewAdapter, SettingsAdapter } diff --git a/examples/energy_mng/ui/theme.slint b/examples/energy_mng/ui/theme.slint index 109c9039163..bd5f16c7404 100644 --- a/examples/energy_mng/ui/theme.slint +++ b/examples/energy_mng/ui/theme.slint @@ -8,6 +8,7 @@ export struct Palette { dark-deep-blue: brush, shark-gray: brush, limon-green: brush, + limon-green-op10: brush, white: brush, // slint blue @@ -89,6 +90,7 @@ export global Theme { dark-deep-blue: #040708, shark-gray: #2C2F36, limon-green: #DEFB3A, + limon-green-op10: #defb3a1a, white: #FFFFFF, // slint blue @@ -113,7 +115,7 @@ export global Theme { lime-green-600: #CBE600, lime-green-700: #BBCF00, lime-green-800: #ACB700, - lime-green-900: #948E00, + lime-green-900: #D9D9D9, // gradients limon-green-gradient: @linear-gradient(135deg, #defb3a75 0%, #defb3a00 100%), diff --git a/examples/energy_mng/ui/widgets/check_box.slint b/examples/energy_mng/ui/widgets/check_box.slint new file mode 100644 index 00000000000..ad67736c4ca --- /dev/null +++ b/examples/energy_mng/ui/widgets/check_box.slint @@ -0,0 +1,42 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial + +import { Theme } from "../theme.slint"; + +export component CheckBox { + callback clicked <=> i-touch-area.clicked; + in-out property checked; + + min-height: 18px; + width: self.height; + + i-container := Rectangle { + border-color: Theme.palette.slint-blue-300; + border-width: 2px; + border-radius: 2px; + + i-check-icon := Image { + opacity: 0.0; + colorize: Theme.palette.pure-black; + source: @image-url("../assets/check.svg"); + + animate opacity { duration: Theme.durations.fast; } + } + + animate background { duration: Theme.durations.fast; } + } + + i-touch-area := TouchArea { + clicked => { + checked = !checked; + } + } + + states [ + checked when checked : { + i-container.border-width: 0; + i-container.background: Theme.palette.limon-green; + i-check-icon.opacity: 1.0; + } + ] +} \ No newline at end of file diff --git a/examples/energy_mng/ui/widgets/icon_button.slint b/examples/energy_mng/ui/widgets/icon_button.slint new file mode 100644 index 00000000000..6772d06bc1f --- /dev/null +++ b/examples/energy_mng/ui/widgets/icon_button.slint @@ -0,0 +1,37 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial + +import { Theme } from "../theme.slint"; + +export component IconButton { + callback clicked <=> i-touch-area.clicked; + + in property icon <=> i-icon.source; + in property enabled <=> i-touch-area.enabled; + + vertical-stretch: 0; + horizontal-stretch: 0; + min-width: 24px; + min-height: 24px; + + GridLayout { + padding: 4px; + + i-icon := Image { + colorize: Theme.palette.slint-blue-300; + + animate colorize { duration: Theme.durations.medium; } + } + } + + i-touch-area := TouchArea {} + + states [ + disabled when !root.enabled : { + opacity: 0.25; + } + pressed when i-touch-area.pressed : { + i-icon.colorize: Theme.palette.limon-green; + } + ] +} \ No newline at end of file diff --git a/examples/energy_mng/ui/widgets/item.slint b/examples/energy_mng/ui/widgets/item.slint new file mode 100644 index 00000000000..19652ae8493 --- /dev/null +++ b/examples/energy_mng/ui/widgets/item.slint @@ -0,0 +1,74 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial + +import { StateLayer } from "../components/state_layer.slint"; +import { ScrollView } from "scroll_view.slint"; +import { Theme } from "../theme.slint"; +import { StateLayer } from "../components/state_layer.slint"; + +export component Item { + callback clicked <=> i-touch-area.clicked; + + in property text <=> i-text.text; + in property has-separator; + + min-height: 40px; + + i-container := Rectangle { + background: Theme.palette.dark-deep-blue; + border-radius: 4px; + } + + i-touch-area := TouchArea {} + + HorizontalLayout { + padding-left: Theme.spaces.medium; + padding-top: Theme.spaces.medium; + padding-bottom: Theme.spaces.medium; + padding-right: Theme.spaces.medium; + spacing: Theme.spaces.medium; + + i-text := Text { + horizontal-stretch: 1; + color: Theme.palette.white; + font-size: Theme.typo.desciption.size; + font-weight: Theme.typo.desciption.weight; + vertical-alignment: center; + } + + @children + } + + if(has-separator) : Rectangle { + width: parent.width - 2 * Theme.spaces.medium; + height: 1px; + x: Theme.spaces.medium; + y: parent.height - self.height; + background: Theme.palette.slint-blue-300; + opacity: 0.25; + } +} +export component ItemGroupBox { + in property title <=> i-title.text; + + VerticalLayout { + HorizontalLayout { + padding: Theme.spaces.medium; + + i-title := Text { + color: Theme.palette.white; + font-size: Theme.typo.header.size; + font-weight: Theme.typo.header.weight; + } + } + + Rectangle { + background: Theme.palette.dark-deep-blue; + border-radius: 4px; + + VerticalLayout { + @children + } + } + } +} \ No newline at end of file diff --git a/examples/energy_mng/ui/widgets/list_view.slint b/examples/energy_mng/ui/widgets/list_view.slint new file mode 100644 index 00000000000..a3bf0e06e77 --- /dev/null +++ b/examples/energy_mng/ui/widgets/list_view.slint @@ -0,0 +1,90 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial + +import { StateLayer } from "../components/state_layer.slint"; +import { ScrollView } from "scroll_view.slint"; +import { Theme } from "../theme.slint"; +import { StateLayer } from "../components/state_layer.slint"; + +component ListViewItem { + callback clicked <=> i-state-layer.clicked; + in property text <=> i-text.text; + in property selected; + + min-height: 40px; + + i-container := Rectangle { + background: Theme.palette.dark-deep-blue; + border-radius: 4px; + border-width: 1px; + border-color: Theme.palette.slint-blue-400; + } + + HorizontalLayout { + padding-left: Theme.spaces.medium; + padding-top: Theme.spaces.medium; + padding-bottom: Theme.spaces.medium; + padding-right: Theme.spaces.medium; + spacing: Theme.spaces.medium; + + i-text := Text { + horizontal-stretch: 1; + color: Theme.palette.white; + font-size: Theme.typo.desciption.size; + font-weight: Theme.typo.desciption.weight; + vertical-alignment: center; + } + + i-icon := Image { + horizontal-stretch: 0; + visible: false; + source: @image-url("../assets/check.svg"); + colorize: Theme.palette.limon-green; + } + } + + i-state-layer := StateLayer { + width: i-container.width; + height: i-container.height; + border-radius: i-container.border-radius; + } + + states [ + selected when selected : { + i-container.border-color: Theme.palette.limon-green; + i-icon.visible: true; + } + ] +} + +export component ListView { + callback selection-changed(int /* index */); + + in property <[StandardListViewItem]> model; + in-out property selected-index; + + i-scroll-view := ScrollView { + i-blub := VerticalLayout { + alignment: start; + spacing: Theme.spaces.medium; + + for item[index] in model : ListViewItem { + private property offset: i-scroll-view.viewport-y + index * (self.height + parent.spacing); + + text: item.text; + selected: index == selected-index; + + animate opacity { duration: Theme.durations.fast; } + + clicked => { + select(index); + } + } + } + } + + function select(index: int) { + selected-index = index; + selection-changed(index); + } +} \ No newline at end of file diff --git a/examples/energy_mng/ui/widgets/menu.slint b/examples/energy_mng/ui/widgets/menu.slint index 9a06a22b0d8..7bff0014deb 100644 --- a/examples/energy_mng/ui/widgets/menu.slint +++ b/examples/energy_mng/ui/widgets/menu.slint @@ -5,17 +5,23 @@ import { Theme } from "../theme.slint"; import { MenuButton } from "menu_button.slint"; export component Menu { + callback opend(); + private property menu-button-visible; private property container-visibility; private property open; + in property start-y; in property end-y; y: start-y; + i-container := Rectangle { visible: container-visibility == 1.0; border-radius: 4px; background: Theme.palette.ebony-radial-gradient; + + @children } HorizontalLayout { @@ -46,6 +52,15 @@ export component Menu { function toggle-open() { menu-button-visible = false; open = !open; + + if(open) { + opend(); + } + } + + public function hide() { + menu-button-visible = false; + open = false; } animate y { duration: Theme.durations.medium; } diff --git a/examples/energy_mng/ui/widgets/navigation.slint b/examples/energy_mng/ui/widgets/navigation.slint index f99198c0a78..a4a7f0e8f59 100644 --- a/examples/energy_mng/ui/widgets/navigation.slint +++ b/examples/energy_mng/ui/widgets/navigation.slint @@ -51,7 +51,7 @@ export component Navigation { alignment: start; FloatButton { - icon: @image-url("../assets/arrow-left.png"); + icon: @image-url("../assets/arrow-left.svg"); clicked => { root.increment(); } @@ -78,7 +78,7 @@ export component Navigation { alignment: end; FloatButton { - icon: @image-url("../assets/arrow-right.png"); + icon: @image-url("../assets/arrow-right.svg"); clicked => { root.decrement(); @@ -102,4 +102,8 @@ export component Navigation { function decrement() { current-index = min(current-index + 1, page-count - 1); } + + public function hide() { + show-navigation = false; + } } \ No newline at end of file diff --git a/examples/energy_mng/ui/widgets/radio_button.slint b/examples/energy_mng/ui/widgets/radio_button.slint new file mode 100644 index 00000000000..c192f228064 --- /dev/null +++ b/examples/energy_mng/ui/widgets/radio_button.slint @@ -0,0 +1,38 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial + +import { Theme } from "../theme.slint"; + +export component RadioButton { + callback clicked <=> i-touch-area.clicked; + + in property checked; + + min-height: 18px; + width: self.height; + + i-container := Rectangle { + border-color: Theme.palette.slint-blue-300; + border-width: 2px; + border-radius: self.height / 2; + + i-indicator := Rectangle { + height: parent.height - 4 * parent.border-width; + width: self.height; + border-radius: self.height / 2; + + animate background { duration: Theme.durations.fast; } + } + + animate border-color { duration: Theme.durations.fast; } + } + + i-touch-area := TouchArea {} + + states [ + checked when checked : { + i-container.border-color: Theme.palette.limon-green; + i-indicator.background: Theme.palette.limon-green; + } + ] +} \ No newline at end of file diff --git a/examples/energy_mng/ui/widgets/scroll_view.slint b/examples/energy_mng/ui/widgets/scroll_view.slint new file mode 100644 index 00000000000..c16a2575a42 --- /dev/null +++ b/examples/energy_mng/ui/widgets/scroll_view.slint @@ -0,0 +1,117 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial + +import { Theme } from "../theme.slint"; +import { StateLayer } from "../components/state_layer.slint"; + +component ScrollBar inherits Rectangle { + in-out property horizontal; + in-out property maximum; + in-out property page-size; + // this is always negative and bigger than -maximum + in-out property value; + + background: Theme.palette.pure-black; + border-width: 1px; + + i-handle := Rectangle { + width: !root.horizontal ? parent.width : root.maximum <= 0phx ? 0phx : parent.width * (root.page-size / (root.maximum + root.page-size)); + height: root.horizontal ? parent.height : root.maximum <= 0phx ? 0phx : parent.height * (root.page-size / (root.maximum + root.page-size)); + + border-radius: 3px; + background: Theme.palette.slint-blue-400; + x: !root.horizontal ? 0phx : (root.width - i-handle.width) * (-root.value / root.maximum); + y: root.horizontal ? 0phx : (root.height - i-handle.height) * (-root.value / root.maximum); + } + + i-touch-area := StateLayer { + private property pressed-value; + + width: parent.width; + height: parent.height; + + pointer-event(event) => { + if (event.button == PointerEventButton.left && event.kind == PointerEventKind.down) { + self.pressed-value = -root.value; + } + } + moved => { + if (self.enabled && self.pressed) { + root.value = -max(0px, min(root.maximum, self.pressed-value + ( + root.horizontal ? (i-touch-area.mouse-x - i-touch-area.pressed-x) * (root.maximum / (root.width - i-handle.width)) + : (i-touch-area.mouse-y - i-touch-area.pressed-y) * (root.maximum / (root.height - i-handle.height)) + ))); + } + } + } +} + +export component ScrollView { + in-out property viewport-width <=> i-flickable.viewport-width; + in-out property viewport-height <=> i-flickable.viewport-height; + in-out property viewport-x <=> i-flickable.viewport-x; + in-out property viewport-y <=> i-flickable.viewport-y; + out property visible-width <=> i-flickable.width; + out property visible-height <=> i-flickable.height; + in property enabled: true; + // FIXME: remove. This property is currently set by the ListView and is used by the native style to draw the scrollbar differently when it has focus + in-out property has-focus; + + min-height: 50px; + min-width: 50px; + horizontal-stretch: 1; + vertical-stretch: 1; + + i-flickable := Flickable { + x: 2px; + y: 2px; + viewport-y <=> i-ver-bar.value; + viewport-x <=> i-hor-bar.value; + width: parent.width - i-ver-bar.width - Theme.spaces.medium; + height: parent.height - i-hor-bar.height - Theme.spaces.medium; + + @children + } + + i-ver-bar := ScrollBar { + visible: viewport-height > visible-height; + width: 5px; + x: i-flickable.width + i-flickable.x + Theme.spaces.medium; + y: i-flickable.y; + height: i-flickable.height; + horizontal: false; + maximum: i-flickable.viewport-height - i-flickable.height; + page-size: i-flickable.height; + } + + i-hor-bar := ScrollBar { + visible: viewport-width > visible-width; + height: 5px; + y: i-flickable.height + i-flickable.y; + x: i-flickable.x; + width: i-flickable.width; + horizontal: true; + maximum: i-flickable.viewport-width - i-flickable.width; + page-size: i-flickable.width; + } + + Rectangle { + visible: viewport-y < 0; + x: 2px; + y: 2px; + width: i-flickable.width; + height: 38px; + background: @linear-gradient(180deg, #D9D9D9 0%, #D9D9D900 100%); + opacity: 0.1; + } + + Rectangle { + visible: viewport-height > visible-height && viewport-y > visible-height - viewport-height; + x: 2px; + y: i-flickable.y + i-flickable.height - self.height; + width: i-flickable.width; + height: 38px; + background: @linear-gradient(180deg, #D9D9D900 0%, #D9D9D9 100%); + opacity: 0.1; + } +} \ No newline at end of file diff --git a/examples/energy_mng/ui/widgets/switch.slint b/examples/energy_mng/ui/widgets/switch.slint new file mode 100644 index 00000000000..3ef86231c3c --- /dev/null +++ b/examples/energy_mng/ui/widgets/switch.slint @@ -0,0 +1,47 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial + +import { Theme } from "../theme.slint"; + +export component Switch { + callback clicked <=> i-touch-area.clicked; + in-out property checked; + + min-height: 18px; + width: 2 * self.height; + + i-container := Rectangle { + border-color: Theme.palette.slint-blue-300; + border-width: 2px; + border-radius: self.height / 2; + + i-indicator := Rectangle { + x: 2 * parent.border-width; + height: parent.height - 4 * parent.border-width; + width: self.height; + border-radius: self.height / 2; + background: Theme.palette.slint-blue-300; + + animate background { duration: Theme.durations.fast; } + animate x { duration: Theme.durations.fast; } + } + + animate border-color { duration: Theme.durations.fast; } + animate background { duration: Theme.durations.fast; } + } + + i-touch-area := TouchArea { + clicked => { + checked = !checked; + } + } + + states [ + checked when checked : { + i-container.background: Theme.palette.limon-green-op10; + i-container.border-color: Theme.palette.limon-green; + i-indicator.background: Theme.palette.limon-green; + i-indicator.x: i-container.width - i-indicator.width - 2 * i-container.border-width; + } + ] +} \ No newline at end of file diff --git a/examples/energy_mng/ui/widgets/tab_widget.slint b/examples/energy_mng/ui/widgets/tab_widget.slint index 27ff2d02573..b74b6f7a02b 100644 --- a/examples/energy_mng/ui/widgets/tab_widget.slint +++ b/examples/energy_mng/ui/widgets/tab_widget.slint @@ -35,8 +35,8 @@ component Tab { x: 0; background: @linear-gradient(angle, rgba(222, 251, 58, 0) , rgba(222, 251, 58, 0.2)); } - Rectangle { - + + Rectangle { y: 0; width: 50%; height: 50%; diff --git a/examples/energy_mng/ui/widgets/widgets.slint b/examples/energy_mng/ui/widgets/widgets.slint index 5af05a6f585..1e68ee8f717 100644 --- a/examples/energy_mng/ui/widgets/widgets.slint +++ b/examples/energy_mng/ui/widgets/widgets.slint @@ -13,6 +13,13 @@ import { ValueDisplay, Value } from "value_display.slint"; import { Tile } from "tile.slint"; import { BarTiles, BarTileModel } from "bar_tiles.slint"; import { TabWidget } from "tab_widget.slint"; +import { IconButton } from "icon_button.slint"; +import { ScrollView } from "scroll_view.slint"; +import { ListView } from "list_view.slint"; +import { Item, ItemGroupBox } from "item.slint"; +import { CheckBox } from "check_box.slint"; +import { RadioButton } from "radio_button.slint"; +import { Switch } from "switch.slint"; export { BalanceChart } export { BarChart } @@ -25,4 +32,11 @@ export { Menu } export { ValueDisplay, Value } export { Tile } export { BarTiles, BarTileModel } -export { TabWidget } \ No newline at end of file +export { TabWidget } +export { IconButton } +export { ScrollView } +export { ListView } +export { Item, ItemGroupBox } +export { CheckBox } +export { RadioButton } +export { Switch } \ No newline at end of file