Skip to content

Commit

Permalink
Add an in-out boolean color-scheme property to Palette (#4701)
Browse files Browse the repository at this point in the history
This allows applications to force dark/light mode, as well as determine
which mode is active.
  • Loading branch information
tronical authored Mar 26, 2024
1 parent ccc92e6 commit 6808324
Show file tree
Hide file tree
Showing 48 changed files with 234 additions and 139 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@
# Changelog
All notable changes to this project are documented in this file.

## [1.6.0] - Unreleased

## Slint Language

- Palette: Added `color-scheme` in-out property for accessing the
style's color scheme.

## [1.5.1] - 2024-03-20

- Fix clipping with a border-radius. (#4854)
Expand Down
2 changes: 1 addition & 1 deletion api/cpp/cbindgen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,7 @@ fn gen_corelib(
"slint_windowrc_size",
"slint_windowrc_set_logical_size",
"slint_windowrc_set_physical_size",
"slint_windowrc_dark_color_scheme",
"slint_windowrc_color_scheme",
"slint_windowrc_dispatch_pointer_event",
"slint_windowrc_dispatch_key_event",
"slint_windowrc_dispatch_event",
Expand Down
5 changes: 4 additions & 1 deletion api/cpp/include/slint_window.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,10 @@ class WindowAdapterRc
float scale_factor() const { return slint_windowrc_get_scale_factor(&inner); }
void set_scale_factor(float value) const { slint_windowrc_set_scale_factor(&inner, value); }

bool dark_color_scheme() const { return slint_windowrc_dark_color_scheme(&inner); }
cbindgen_private::ColorScheme color_scheme() const
{
return slint_windowrc_color_scheme(&inner);
}

bool text_input_focused() const { return slint_windowrc_get_text_input_focused(&inner); }
void set_text_input_focused(bool value) const
Expand Down
2 changes: 2 additions & 0 deletions docs/reference/src/language/builtins/globals.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ the selected style e.g. fluent, cupertino, material, or qt.
- **`selection-background`** (_out_ _brush_): Defines the background brush that is used to highlight a selection such as a text selection.
- **`selection-foreground`** (_out_ _brush_): Defines the foreground brush that is used for content that is displayed on `selection-background` brush.
- **`border`** (_out_ _brush_): Defines the brush that is used for borders such as separators and widget borders.
- **`color-scheme`** (_in_ _out_ _enum [`ColorScheme`](enums.md#colorscheme)_): Read this property to determine the color scheme used by the palette.
Set this property to force a dark or light color scheme. All styles except for the Qt style support setting a dark or light color scheme.

### Example

Expand Down
4 changes: 2 additions & 2 deletions examples/ffmpeg/scene.slint
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: MIT

import { VerticalBox, StyleMetrics } from "std-widgets.slint";
import { VerticalBox, StyleMetrics, Palette } from "std-widgets.slint";

export component App inherits Window {
in property <image> video-frame <=> image.source;
Expand Down Expand Up @@ -47,7 +47,7 @@ export component App inherits Window {
y: root.height - self.height - 40px;
controls := Rectangle {
border-radius: 4px;
background: StyleMetrics.dark-color-scheme ? #3737378c : #ffffff82;
background: Palette.color-scheme == ColorScheme.dark ? #3737378c : #ffffff82;

Image {
width: 64px;
Expand Down
15 changes: 11 additions & 4 deletions examples/gallery/ui/pages/page.slint
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: MIT

import { Switch, GridBox, ListView, ScrollView, VerticalBox } from "std-widgets.slint";
import { Switch, GridBox, ListView, ScrollView, HorizontalBox, VerticalBox, Palette } from "std-widgets.slint";

import { GallerySettings } from "../gallery_settings.slint";

Expand All @@ -10,9 +10,7 @@ export component Page inherits VerticalBox {
in property<string> description: "description";
in property<bool> show-enable-switch: true;

HorizontalLayout {
height: 24px;

HorizontalBox {
Text {
font-size: 20px;
text <=> root.title;
Expand All @@ -27,6 +25,15 @@ export component Page inherits VerticalBox {
checked <=> GallerySettings.widgets-enabled;
enabled: true;
}

Switch {
horizontal-stretch: 0;
text: @tr("Dark Mode");
checked: Palette.color-scheme == ColorScheme.dark;
toggled => {
Palette.color-scheme = self.checked ? ColorScheme.dark : ColorScheme.light;
}
}
}

@children
Expand Down
4 changes: 2 additions & 2 deletions examples/gstreamer-player/scene.slint
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: MIT

import { VerticalBox, StyleMetrics } from "std-widgets.slint";
import { VerticalBox, StyleMetrics, Palette } from "std-widgets.slint";

export component App inherits Window {
in property <image> video-frame <=> image.source;
Expand Down Expand Up @@ -47,7 +47,7 @@ export component App inherits Window {
y: root.height - self.height - 40px;
controls := Rectangle {
border-radius: 4px;
background: StyleMetrics.dark-color-scheme ? #3737378c : #ffffff82;
background: Palette.color-scheme == ColorScheme.dark ? #3737378c : #ffffff82;

Image {
width: 64px;
Expand Down
4 changes: 2 additions & 2 deletions examples/iot-dashboard/iot-dashboard.slint
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
****************************************************************************/
*/

import { StyleMetrics } from "std-widgets.slint";
import { Palette } from "std-widgets.slint";

struct Palette {
menuBar : brush,
Expand All @@ -53,7 +53,7 @@ struct Palette {
}

global Skin {
in property <bool> day: !StyleMetrics.dark-color-scheme;
in property <bool> day: Palette.color-scheme != ColorScheme.dark;
out property <Palette> palette : root.day ? {
menuBar : #6D7BFB,
mainContent : #fbfbfb,
Expand Down
10 changes: 5 additions & 5 deletions examples/virtual_keyboard/ui/virtual_keyboard.slint
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: MIT

import { Button, StyleMetrics } from "std-widgets.slint";
import { Button, Palette } from "std-widgets.slint";

import { Icons } from "icons.slint";

Expand All @@ -23,14 +23,14 @@ component VirtualKeyboardButton {

i-container := Rectangle {
border-radius: 4px;
background: StyleMetrics.dark-color-scheme ? #373737 : #ffffff;
background: Palette.color-scheme == ColorScheme.dark ? #373737 : #ffffff;

HorizontalLayout {
padding: 8px;

if (root.key != "") : Text {
text: root.key;
color: StyleMetrics.dark-color-scheme ? #ffffff : #000000;
color: Palette.color-scheme == ColorScheme.dark ? #ffffff : #000000;
font-size: 12px;
vertical-alignment: center;
horizontal-alignment: center;
Expand All @@ -40,7 +40,7 @@ component VirtualKeyboardButton {
y: (parent.height - self.height) / 2;
source: root.icon;
height: 18px;
colorize: StyleMetrics.dark-color-scheme ? #ffffff : #000000;
colorize: Palette.color-scheme == ColorScheme.dark ? #ffffff : #000000;
}
}
}
Expand Down Expand Up @@ -164,7 +164,7 @@ export component VirtualKeyboard {
preferred-width: 100%;

Rectangle {
background: StyleMetrics.dark-color-scheme ? #1c1c1c : #d4d4d4;
background: Palette.color-scheme == ColorScheme.dark ? #1c1c1c : #d4d4d4;
height: 100%;
}

Expand Down
18 changes: 12 additions & 6 deletions internal/backends/android-activity/androidwindowadapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use crate::javahelper::{print_jni_error, JavaHelper};
use android_activity::input::{InputEvent, KeyAction, Keycode, MotionAction, MotionEvent};
use android_activity::{InputStatus, MainEvent, PollEvent};
use i_slint_core::api::{LogicalPosition, PhysicalPosition, PhysicalSize, PlatformError, Window};
use i_slint_core::items::ColorScheme;
use i_slint_core::platform::{
Key, PointerEventButton, WindowAdapter, WindowEvent, WindowProperties,
};
Expand All @@ -29,7 +30,7 @@ pub struct AndroidWindowAdapter {
pub(crate) event_queue: EventQueue,
pub(crate) pending_redraw: Cell<bool>,
pub(super) java_helper: JavaHelper,
pub(crate) dark_color_scheme: core::pin::Pin<Box<Property<bool>>>,
pub(crate) color_scheme: core::pin::Pin<Box<Property<ColorScheme>>>,
pub(crate) fullscreen: Cell<bool>,
/// The offset at which the Slint view is drawn in the native window (account for status bar)
pub offset: Cell<PhysicalPosition>,
Expand Down Expand Up @@ -158,24 +159,29 @@ impl i_slint_core::window::WindowAdapterInternal for AndroidWindowAdapter {
});
}

fn dark_color_scheme(&self) -> bool {
self.dark_color_scheme.as_ref().get()
fn color_scheme(&self) -> ColorScheme {
self.color_scheme.as_ref().get()
}
}

impl AndroidWindowAdapter {
pub fn new(app: AndroidApp) -> Rc<Self> {
let java_helper = JavaHelper::new(&app).unwrap_or_else(|e| print_jni_error(&app, e));
let dark_color_scheme = Box::pin(Property::new(
java_helper.dark_color_scheme().unwrap_or_else(|e| print_jni_error(&app, e)),
let color_scheme = Box::pin(Property::new(
match java_helper.color_scheme().unwrap_or_else(|e| print_jni_error(&app, e)) {
0x10 => ColorScheme::Light, // UI_MODE_NIGHT_NO(0x10)
0x20 => ColorScheme::Dark, // UI_MODE_NIGHT_YES(0x20)
0x0 => ColorScheme::Unknown, // UI_MODE_NIGHT_UNDEFINED
_ => ColorScheme::Unknown,
},
));
Rc::<Self>::new_cyclic(|w| Self {
app,
window: Window::new(w.clone()),
renderer: SkiaRenderer::default(),
event_queue: Default::default(),
pending_redraw: Default::default(),
dark_color_scheme,
color_scheme,
java_helper,
fullscreen: Cell::new(false),
offset: Default::default(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,14 +211,7 @@ public void setText(String text, int cursorPosition, int anchorPosition, int pre
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
int currentNightMode = newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK;
switch (currentNightMode) {
case Configuration.UI_MODE_NIGHT_NO:
SlintAndroidJavaHelper.setDarkMode(false);
break;
case Configuration.UI_MODE_NIGHT_YES:
SlintAndroidJavaHelper.setDarkMode(true);
break;
}
SlintAndroidJavaHelper.setNightMode(currentNightMode);
}

private InputHandle mCursorHandle;
Expand Down Expand Up @@ -423,7 +416,7 @@ public void run() {
static public native void updateText(String text, int cursorPosition, int anchorPosition, int preeditStart,
int preeditOffset);

static public native void setDarkMode(boolean dark);
static public native void setNightMode(int nightMode);

static public native void moveCursorHandle(int id, int pos_x, int pos_y);

Expand Down Expand Up @@ -462,9 +455,9 @@ public void run() {
});
}

public boolean dark_color_scheme() {
public int color_scheme() {
int nightModeFlags = mActivity.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
return nightModeFlags == Configuration.UI_MODE_NIGHT_YES;
return nightModeFlags;
}

// Get the geometry of the view minus the system bars and the keyboard
Expand Down
23 changes: 14 additions & 9 deletions internal/backends/android-activity/javahelper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
use super::*;
use i_slint_core::api::{PhysicalPosition, PhysicalSize};
use i_slint_core::graphics::{euclid, Color};
use i_slint_core::items::InputType;
use i_slint_core::items::{ColorScheme, InputType};
use i_slint_core::platform::WindowAdapter;
use i_slint_core::SharedString;
use jni::objects::{JClass, JObject, JString, JValue};
Expand Down Expand Up @@ -59,9 +59,9 @@ fn load_java_helper(app: &AndroidApp) -> Result<jni::objects::GlobalRef, jni::er
fn_ptr: Java_SlintAndroidJavaHelper_updateText as *mut _,
},
jni::NativeMethod {
name: "setDarkMode".into(),
sig: "(Z)V".into(),
fn_ptr: Java_SlintAndroidJavaHelper_setDarkMode as *mut _,
name: "setNightMode".into(),
sig: "(I)V".into(),
fn_ptr: Java_SlintAndroidJavaHelper_setNightMode as *mut _,
},
jni::NativeMethod {
name: "moveCursorHandle".into(),
Expand Down Expand Up @@ -189,9 +189,9 @@ impl JavaHelper {
})
}

pub fn dark_color_scheme(&self) -> Result<bool, jni::errors::Error> {
pub fn color_scheme(&self) -> Result<i32, jni::errors::Error> {
self.with_jni_env(|env, helper| {
Ok(env.call_method(helper, "dark_color_scheme", "()Z", &[])?.z()?)
Ok(env.call_method(helper, "color_scheme", "()I", &[])?.i()?)
})
}

Expand Down Expand Up @@ -331,14 +331,19 @@ fn convert_utf8_index_to_utf16(in_str: &str, utf8_index: usize) -> usize {
}

#[no_mangle]
extern "system" fn Java_SlintAndroidJavaHelper_setDarkMode(
extern "system" fn Java_SlintAndroidJavaHelper_setNightMode(
_env: JNIEnv,
_class: JClass,
dark: jboolean,
night_mode: jint,
) {
i_slint_core::api::invoke_from_event_loop(move || {
if let Some(w) = CURRENT_WINDOW.with_borrow(|x| x.upgrade()) {
w.dark_color_scheme.as_ref().set(dark == jni::sys::JNI_TRUE);
w.color_scheme.as_ref().set(match night_mode {
0x10 => ColorScheme::Light, // UI_MODE_NIGHT_NO(0x10)
0x20 => ColorScheme::Dark, // UI_MODE_NIGHT_YES(0x20)
0x0 => ColorScheme::Unknown, // UI_MODE_NIGHT_UNDEFINED
_ => ColorScheme::Unknown,
});
}
})
.unwrap()
Expand Down
14 changes: 13 additions & 1 deletion internal/backends/qt/qt_widgets/palette.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

// cSpell: ignore deinit

use i_slint_core::Brush;
use i_slint_core::{items::ColorScheme, Brush};

use super::*;

Expand Down Expand Up @@ -41,6 +41,7 @@ pub struct NativePalette {
pub selection_background: Property<Brush>,
pub selection_foreground: Property<Brush>,
pub border: Property<Brush>,
pub color_scheme: Property<ColorScheme>,
pub style_change_listener: core::cell::Cell<*const u8>,
}

Expand All @@ -64,6 +65,7 @@ impl NativePalette {
border: Default::default(),
selection_background: Default::default(),
selection_foreground: Default::default(),
color_scheme: Default::default(),
style_change_listener: core::cell::Cell::new(core::ptr::null()),
})
}
Expand Down Expand Up @@ -153,6 +155,16 @@ impl NativePalette {
let selection_foreground = Color::from_argb_encoded(selection_foreground);
self.selection_foreground.set(Brush::from(selection_foreground));

self.color_scheme.set(
if (background.red() as u32 + background.green() as u32 + background.blue() as u32) / 3
< 128
{
ColorScheme::Dark
} else {
ColorScheme::Light
},
);

if self.style_change_listener.get().is_null() {
self.style_change_listener.set(cpp!(unsafe [self as "void*"] -> *const u8 as "void*"{
return new PaletteStyleChangeListener(self);
Expand Down
3 changes: 3 additions & 0 deletions internal/backends/qt/qt_widgets/stylemetrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,9 @@ impl NativeStyleMetrics {
});
self.placeholder_color_disabled.set(Color::from_argb_encoded(placeholder_color_disabled));

// This is sub-optimal: It should really be a binding to Palette.color-scheme == ColorScheme.dark, so that
// writes to Palette.color-scheme are reflected, but we can't access the other global singleton here and
// this is just a backwards-compat property that was never documented to be public.
self.dark_color_scheme.set(
(window_background.red() as u32
+ window_background.green() as u32
Expand Down
Loading

0 comments on commit 6808324

Please sign in to comment.