From 83a9ce23ddd49d192c3e71d46b389b3d9711055f Mon Sep 17 00:00:00 2001 From: jika Date: Sat, 5 Aug 2023 23:53:58 +0200 Subject: [PATCH] Add support for theming --- remote/src/controller/media.rs | 6 +- remote/src/controller/mod.rs | 1 + remote/src/controller/virtual_keyboard.rs | 16 ++ remote/src/main.rs | 21 +- remote/ui/app-manager.slint | 4 +- remote/ui/app.slint | 10 +- remote/ui/pages/brushes-page.slint | 332 ++++++++++++++++++++++ remote/ui/pages/media-page.slint | 3 - remote/ui/pages/page.slint | 56 +--- remote/ui/pages/pages.slint | 7 +- remote/ui/pages/settings-page.slint | 89 ++++++ remote/ui/themes/onedark.slint | 111 ++++++++ remote/ui/themes/palettes.slint | 43 +++ 13 files changed, 611 insertions(+), 88 deletions(-) create mode 100644 remote/src/controller/virtual_keyboard.rs create mode 100644 remote/ui/pages/brushes-page.slint create mode 100644 remote/ui/pages/settings-page.slint create mode 100644 remote/ui/themes/onedark.slint create mode 100644 remote/ui/themes/palettes.slint diff --git a/remote/src/controller/media.rs b/remote/src/controller/media.rs index ac88b71..497e214 100644 --- a/remote/src/controller/media.rs +++ b/remote/src/controller/media.rs @@ -23,7 +23,7 @@ pub fn init(app: &App, sender_co: MessageToServer, receiver_s: MessageFromServer sender_c.send(ClientMessage::Previous).unwrap(); }); - let ui_handle = app.as_weak(); + let app_handle = app.as_weak(); thread::spawn(move || loop { if receiver_s.is_empty() { thread::sleep(Duration::from_millis(200)); @@ -32,12 +32,12 @@ pub fn init(app: &App, sender_co: MessageToServer, receiver_s: MessageFromServer let msg = receiver_s.recv().unwrap(); info!("Received : {msg:?}"); match msg { - ServerMessage::NowPlaying(title) => ui_handle + ServerMessage::NowPlaying(title) => app_handle .upgrade_in_event_loop(move |handle| { handle.global::().set_now_playing(title.into()) }) .unwrap(), - ServerMessage::Playing(bool) => ui_handle + ServerMessage::Playing(bool) => app_handle .upgrade_in_event_loop(move |handle| { handle.global::().set_playing(bool) }) diff --git a/remote/src/controller/mod.rs b/remote/src/controller/mod.rs index b30398d..1a27c20 100644 --- a/remote/src/controller/mod.rs +++ b/remote/src/controller/mod.rs @@ -1 +1,2 @@ pub mod media; +pub mod virtual_keyboard; diff --git a/remote/src/controller/virtual_keyboard.rs b/remote/src/controller/virtual_keyboard.rs new file mode 100644 index 0000000..7e3954a --- /dev/null +++ b/remote/src/controller/virtual_keyboard.rs @@ -0,0 +1,16 @@ +use slint::*; +use crate::{App,VirtualKeyboardHandler}; + +pub fn init(app: &App) { + let weak = app.as_weak(); + app.global::().on_key_pressed({ + move |key| { + weak.unwrap() + .window() + .dispatch_event(slint::platform::WindowEvent::KeyPressed { text: key.clone() }); + weak.unwrap() + .window() + .dispatch_event(slint::platform::WindowEvent::KeyReleased { text: key }); + } + }); +} diff --git a/remote/src/main.rs b/remote/src/main.rs index 1e464e1..7a76603 100644 --- a/remote/src/main.rs +++ b/remote/src/main.rs @@ -2,8 +2,7 @@ mod controller; mod link; use log::info; - -use crate::{controller::media, link::init_tcp}; +use crate::{controller::{media,virtual_keyboard}, link::init_tcp}; slint::include_modules!(); @@ -24,21 +23,3 @@ fn main() -> Result<(), slint::PlatformError> { app.run() } -mod virtual_keyboard { - use super::*; - use slint::*; - - pub fn init(app: &App) { - let weak = app.as_weak(); - app.global::().on_key_pressed({ - move |key| { - weak.unwrap() - .window() - .dispatch_event(slint::platform::WindowEvent::KeyPressed { text: key.clone() }); - weak.unwrap() - .window() - .dispatch_event(slint::platform::WindowEvent::KeyReleased { text: key }); - } - }); - } -} diff --git a/remote/ui/app-manager.slint b/remote/ui/app-manager.slint index fe9b291..656e2ca 100644 --- a/remote/ui/app-manager.slint +++ b/remote/ui/app-manager.slint @@ -2,6 +2,6 @@ // SPDX-License-Identifier: MIT export global AppManager { - in-out property keyboard-enabled: false; + in-out property keyboard-enabled: true; in-out property widgets-enabled: true; -} \ No newline at end of file +} diff --git a/remote/ui/app.slint b/remote/ui/app.slint index 541afd1..9e4db54 100644 --- a/remote/ui/app.slint +++ b/remote/ui/app.slint @@ -1,18 +1,19 @@ import { CoopWindow, VirtualKeyboard, VirtualKeyboardHandler, GroupListViewItem, Palette, Icons } from "_imports/coop-widgets.slint"; -import { MediaPage } from "pages/pages.slint"; +import { MediaPage , SettingsPage } from "pages/pages.slint"; import { AppManager } from "app-manager.slint"; import { SideBarView } from "side-bar-view.slint"; import { MediaControls, MediaLogic } from "widgets/mediacontrols.slint"; export { VirtualKeyboardHandler } export { AppManager } +export { Palette } export { MediaLogic } export component App inherits CoopWindow { // preferred-width: 600px; // preferred-height: 400px; title: "Deckui"; - + Rectangle { background: Palette.background; } @@ -23,19 +24,22 @@ export component App inherits CoopWindow { col: 1; if (i-side-bar.current-item.parent == 0 && i-side-bar.current-item.item == 0) : MediaPage {} + if (i-side-bar.current-item.parent == 0 && i-side-bar.current-item.item == 1) : SettingsPage {} //if (i-side-bar.current-item.parent == 1 && i-side-bar.current-item.item == 0) : BrushesPage {} } i-side-bar := SideBarView { + // current-item: {parent:0,item:1}; col: 0; parent-width: root.width; title: "Deckui"; responsive: true; property widgets: { - text: "Widgets", + text: "Home", items: [ { leading-icon: Icons.home, text: "Media" }, + { leading-icon: Icons.pin, text: "Settings" }, ] }; diff --git a/remote/ui/pages/brushes-page.slint b/remote/ui/pages/brushes-page.slint new file mode 100644 index 0000000..e96e629 --- /dev/null +++ b/remote/ui/pages/brushes-page.slint @@ -0,0 +1,332 @@ +// SPDX-FileCopyrightText: 2022 Florian Blasius +// SPDX-License-Identifier: MIT + +import { Page } from "page.slint"; +import { RowTitle, ColumnTitle } from "titles.slint"; + +import { MediumLabel, Spacer, HorizontalSeparator, Radius, Palette, Size, Space } from "../_imports/coop-widgets.slint"; + +component BrushRect inherits Rectangle { + width: Size.small; + height: root.width; + border-radius: Radius.medium; + border-width: 1px; + border-color: Palette.border; +} + +export component BrushesPage { + + GridLayout { + padding: Space.large; + spacing: Space.medium; + + // Labels + Row { + RowTitle { + text: "Brushes"; + } + } + + Row { + ColumnTitle { + text: "Brush"; + } + + Spacer {} + + ColumnTitle { + text: "key"; + } + + Spacer {} + + ColumnTitle { + text: "description"; + } + } + + Row { + HorizontalSeparator { + colspan: 9; + } + } + + // background + Row { + BrushRect { + background: Palette.background; + } + + Spacer {} + + MediumLabel { + text: "Palette.background"; + vertical-alignment: center; + } + + Spacer {} + + MediumLabel { + text: "background brush of the window"; + vertical-alignment: center; + } + } + + // background-alt + Row { + BrushRect { + background: Palette.background-alt; + } + + Spacer {} + + MediumLabel { + text: "Palette.background-alt"; + vertical-alignment: center; + } + + Spacer {} + + MediumLabel { + text: "alternative background brush"; + vertical-alignment: center; + } + } + + // foreground + Row { + BrushRect { + background: Palette.foreground; + } + + Spacer {} + + MediumLabel { + text: "Palette.foreground"; + vertical-alignment: center; + } + + Spacer {} + + MediumLabel { + text: "common foreground brush e.g. for text"; + vertical-alignment: center; + } + } + + // foreground-disabled + Row { + BrushRect { + background: Palette.foreground-disabled; + } + + Spacer {} + + MediumLabel { + text: "Palette.foreground-disabled"; + vertical-alignment: center; + } + + Spacer {} + + MediumLabel { + text: "common foreground for disabled elements"; + vertical-alignment: center; + } + } + + // surface + Row { + BrushRect { + background: Palette.surface; + } + + Spacer {} + + MediumLabel { + text: "Palette.surface"; + vertical-alignment: center; + } + + Spacer {} + + MediumLabel { + text: "widget surface brush e.g. used by button"; + vertical-alignment: center; + } + } + + // surface-disabled + Row { + BrushRect { + background: Palette.surface-disabled; + } + + Spacer {} + + MediumLabel { + text: "Palette.surface-disabled"; + vertical-alignment: center; + } + + Spacer {} + + MediumLabel { + text: "widget surface brush of disabled widgets"; + vertical-alignment: center; + } + } + + // on-surface + Row { + BrushRect { + background: Palette.on-surface; + } + + Spacer {} + + MediumLabel { + text: "Palette.on-surface"; + vertical-alignment: center; + } + + Spacer {} + + MediumLabel { + text: "content elements on surface background"; + vertical-alignment: center; + } + } + + // primary + Row { + BrushRect { + background: Palette.primary; + } + + Spacer {} + + MediumLabel { + text: "Palette.primary"; + vertical-alignment: center; + } + + Spacer {} + + MediumLabel { + text: "defines the accent brush"; + vertical-alignment: center; + } + } + + // on-primary + Row { + BrushRect { + background: Palette.on-primary; + } + + Spacer {} + + MediumLabel { + text: "Palette.on-primary"; + vertical-alignment: center; + } + + Spacer {} + + MediumLabel { + text: "content elements on primary background"; + vertical-alignment: center; + } + } + + // border + Row { + BrushRect { + background: Palette.border; + } + + Spacer {} + + MediumLabel { + text: "Palette.border"; + vertical-alignment: center; + } + + Spacer {} + + MediumLabel { + text: "brush of borders"; + vertical-alignment: center; + } + } + + // border-disabled + Row { + BrushRect { + background: Palette.border-disabled; + } + + Spacer {} + + MediumLabel { + text: "Palette.border-disabled"; + vertical-alignment: center; + } + + Spacer {} + + MediumLabel { + text: "brush of disabled borders"; + vertical-alignment: center; + } + } + + // shadow + Row { + BrushRect { + background: Palette.shadow; + } + + Spacer {} + + MediumLabel { + text: "Palette.shadow"; + vertical-alignment: center; + } + + Spacer {} + + MediumLabel { + text: "default drop shadow brush"; + vertical-alignment: center; + } + } + + // shadow + Row { + BrushRect { + background: Palette.error; + } + + Spacer {} + + MediumLabel { + text: "Palette.error"; + vertical-alignment: center; + } + + Spacer {} + + MediumLabel { + text: "brush for error representation elements"; + vertical-alignment: center; + } + } + + // Fills the remaining available space + Row { + Spacer {} + } + } +} diff --git a/remote/ui/pages/media-page.slint b/remote/ui/pages/media-page.slint index aa99cec..fc41e96 100644 --- a/remote/ui/pages/media-page.slint +++ b/remote/ui/pages/media-page.slint @@ -1,6 +1,3 @@ -// SPDX-FileCopyrightText: 2022 Florian Blasius -// SPDX-License-Identifier: MIT - import { Page } from "page.slint"; import { MediaControls } from "../widgets/mediacontrols.slint"; diff --git a/remote/ui/pages/page.slint b/remote/ui/pages/page.slint index 162cd00..a2ae069 100644 --- a/remote/ui/pages/page.slint +++ b/remote/ui/pages/page.slint @@ -1,64 +1,14 @@ -// SPDX-FileCopyrightText: 2022 Florian Blasius -// SPDX-License-Identifier: MIT - import { AppManager } from "../app-manager.slint"; -import { HeaderBar, OutlineButton, CheckBox, Switch, ScrollView, Icons, ComboBox, Palette, ColorTheme, ColorVariant } from "../_imports/coop-widgets.slint"; +import { HeaderBar, ScrollView } from "../_imports/coop-widgets.slint"; + export component Page inherits GridLayout { in-out property title <=> i_header_bar.title; - in-out property keyboard_check_box: true; - in-out property disabled_check_box: true; Row { i_header_bar := HeaderBar { - if (root.keyboard_check_box) : CheckBox { - y: (parent.height - self.height) / 2; - text: "Keyboard enabled"; - checked <=> AppManager.keyboard_enabled; - } - if (root.disabled_check_box) : CheckBox { - y: (parent.height - self.height) / 2; - text: "Widgets enabled"; - checked <=> AppManager.widgets_enabled; - } - - ComboBox { - width: 128px; - placeholder-text: @tr("Select theme:"); - current-index: Palette.current-color-theme == ColorTheme.Coop ? 0 : 1; - - model: [ - { text: "Coop" }, - { text: "Cosmic" }, - ]; - - selected (index) => { - if (index == 0) { - Palette.set-color-theme(ColorTheme.Coop); - return; - } - - if (index == 1) { - Palette.set-color-theme(ColorTheme.Cosmic); - } - } - } - - VerticalLayout { - alignment: center; - - Switch { - on_icon: Icons.mode-night; - off_icon: Icons.light-mode; - checked: !Palette.dark-color-scheme; - - toggled(checked) => { - Palette.set-color-variant(checked ? ColorVariant.Light : ColorVariant.Dark); - } - } - } } } @@ -67,4 +17,4 @@ export component Page inherits GridLayout { @children } } -} \ No newline at end of file +} diff --git a/remote/ui/pages/pages.slint b/remote/ui/pages/pages.slint index 0b8d4d1..f941f05 100644 --- a/remote/ui/pages/pages.slint +++ b/remote/ui/pages/pages.slint @@ -1,6 +1,5 @@ -// SPDX-FileCopyrightText: 2022 Florian Blasius -// SPDX-License-Identifier: MIT - import { MediaPage } from "media-page.slint"; +import { SettingsPage } from "settings-page.slint"; + +export { MediaPage, SettingsPage} -export { MediaPage } diff --git a/remote/ui/pages/settings-page.slint b/remote/ui/pages/settings-page.slint new file mode 100644 index 0000000..49afb78 --- /dev/null +++ b/remote/ui/pages/settings-page.slint @@ -0,0 +1,89 @@ +import { Page } from "page.slint"; +import { AppManager } from "../app-manager.slint"; +import { ComboBox, Switch, Palette, ColorTheme, Icons, ColorVariant, CheckBox } from "../_imports/coop-widgets.slint"; +import { ThemeSwitcher, ColorPalette,Settings } from "../themes/palettes.slint"; +import { Onedark } from "../themes/onedark.slint"; +import { BrushesPage } from "brushes-page.slint"; + +export component SettingsPage inherits Page { + title: "Settings"; + in-out property keyboard_check_box: true; + in-out property disabled_check_box: true; + in-out property color_palette; + in-out property built-in-theme: true; + in-out property color_index; + in-out property debug: false; + + Flickable { + VerticalLayout { + alignment: start; + padding: 30px; + spacing: 30px; + + if (root.keyboard_check_box) : CheckBox { + text: "Keyboard enabled"; + checked <=> AppManager.keyboard_enabled; + } + + if (root.disabled_check_box) : CheckBox { + text: "Widgets enabled"; + checked <=> AppManager.widgets_enabled; + } + + function reload_color() { + root.built-in-theme = true; + if (root.color-index == 0) { + Palette.set-color-theme(ColorTheme.Coop); + return; + } else if (root.color-index == 1) { + Palette.set-color-theme(ColorTheme.Cosmic); + return; + } else if (root.color-index == 2){ + root.color-palette = Onedark.palette; + } + root.built-in-theme = false; + ThemeSwitcher.switch(root.color-palette); + + } + ComboBox { + width: 128px; + height: 40px; + placeholder-text: @tr("Select theme:"); + current-index: root.color-index; + + model: [ + { text: "Coop" }, + { text: "Cosmic" }, + { text: "Onedark" }, + ]; + + selected (index) => { + root.color-index=index; + reload-color(); + } + } + + VerticalLayout { + alignment: center; + + Switch { + height: 48px; + on_icon: Icons.mode-night; + off_icon: Icons.light-mode; + checked: !Palette.dark-color-scheme; + + toggled(checked) => { + Settings.dark-color-scheme = !checked; + if (root.built-in-theme){ + Palette.set-color-variant(checked ? ColorVariant.Light : ColorVariant.Dark); + } else { + // ThemeSwitcher.switch(root.color-palette); + reload-color(); + } + } + } + } + if root.debug : BrushesPage { } + } + } +} diff --git a/remote/ui/themes/onedark.slint b/remote/ui/themes/onedark.slint new file mode 100644 index 0000000..44f0313 --- /dev/null +++ b/remote/ui/themes/onedark.slint @@ -0,0 +1,111 @@ +import { ColorPalette, Settings } from "palettes.slint"; + +struct ColorPalette_ { + black : color, + bg0 : color, + bg1 : color, + bg2 : color, + bg3 : color, + bg_d : color, + bg_blue : color, + bg_yellow : color, + fg : color, + purple : color, + green : color, + orange : color, + blue : color, + yellow : color, + cyan : color, + red : color, + grey : color, + light_grey : color, + dark_cyan : color, + dark_red : color, + dark_yellow : color, + dark_purple : color, + diff_add : color, + diff_delete : color, + diff_change : color, + diff_text : color, + +} + + +export global Onedark { + out property darker : { + black : #0e1013, + bg0 : #1f2329, + bg1 : #282c34, + bg2 : #30363f, + bg3 : #323641, + bg_d : #181b20, + bg_blue : #61afef, + bg_yellow : #e8c88c, + fg : #a0a8b7, + purple : #bf68d9, + green : #8ebd6b, + orange : #cc9057, + blue : #4fa6ed, + yellow : #e2b86b, + cyan : #48b0bd, + red : #e55561, + grey : #535965, + light_grey : #7a818e, + dark_cyan : #266269, + dark_red : #8b3434, + dark_yellow : #835d1a, + dark_purple : #7e3992, + diff_add : #272e23, + diff_delete : #2d2223, + diff_change : #172a3a, + diff_text : #274964, + }; + + out property light: { + black : #101012, + bg0 : #fafafa, + bg1 : #f0f0f0, + bg2 : #e6e6e6, + bg3 : #dcdcdc, + bg_d : #c9c9c9, + bg_blue : #68aee8, + bg_yellow : #e2c792, + fg : #383a42, + purple : #a626a4, + green : #50a14f, + orange : #c18401, + blue : #4078f2, + yellow : #986801, + cyan : #0184bc, + red : #e45649, + grey : #a0a1a7, + light_grey : #818387, + dark_cyan : #2b5d63, + dark_red : #833b3b, + dark_yellow : #7c5c20, + dark_purple : #79428a, + diff_add : #e2fbe4, + diff_delete : #fce2e5, + diff_change : #e2ecfb, + diff_text : #cad3e0, + }; + + out property palette: { + + background: Settings.dark-color-scheme ? darker.bg0: light.bg0, + background-alt: Settings.dark-color-scheme ? darker.bg1: light.bg1, + foreground: Settings.dark-color-scheme ? darker.fg: light.fg, + foreground-disabled: Settings.dark-color-scheme ? darker.bg2: light.bg2, + surface: Settings.dark-color-scheme ? darker.bg3: light.bg3, + surface-disabled: Settings.dark-color-scheme ? darker.bg1: light.bg1, + on-surface: Settings.dark-color-scheme ? darker.bg-d: light.bg-d, + primary: Settings.dark-color-scheme ? darker.dark-purple: light.dark-purple, + on-primary: Settings.dark-color-scheme ? darker.bg-d: light.bg-d, + border: Settings.dark-color-scheme ? darker.bg2: light.bg2, + border-disabled: Settings.dark-color-scheme ? darker.bg3: light.bg3, + shadow: Settings.dark-color-scheme ? #00000052 : #00000014, + error: darker.dark-red, + accent: Settings.dark-color-scheme ? darker.dark-purple: light.dark-purple, + on-accent: Settings.dark-color-scheme ? darker.bg-d: light.bg-d, + }; + } diff --git a/remote/ui/themes/palettes.slint b/remote/ui/themes/palettes.slint new file mode 100644 index 0000000..807e89a --- /dev/null +++ b/remote/ui/themes/palettes.slint @@ -0,0 +1,43 @@ +import { Palette } from "../_imports/coop-widgets.slint"; +import { StyleMetrics } from "std-widgets.slint"; + +export global Settings { + in-out property dark-color-scheme: StyleMetrics.dark-color-scheme; +} +export struct ColorPalette { + background: brush, + background-alt: brush, + foreground: brush, + foreground-disabled: brush, + surface: brush, + surface-disabled: brush, + on-surface: brush, + primary: brush, + on-primary: brush, + border: brush, + border-disabled: brush, + shadow: brush, + error: brush, + accent: brush, + on-accent: brush, +} + +export global ThemeSwitcher { + public function switch(palette: ColorPalette) { + Palette.background = palette.background; + Palette.background-alt = palette.background-alt; + Palette.foreground = palette.foreground; + Palette.foreground-disabled = palette.foreground-disabled; + Palette.surface = palette.surface; + Palette.surface-disabled = palette.surface-disabled; + Palette.on-surface = palette.on-surface; + Palette.primary = palette.primary; + Palette.on-primary = palette.on-primary; + Palette.border = palette.border; + Palette.border-disabled = palette.border-disabled; + Palette.shadow = palette.shadow; + Palette.error = palette.error; + Palette.accent = palette.accent; + Palette.on-accent = palette.on-accent; + } +}