i_slint_backend_qt/qt_widgets/
palette.rs

1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4// cSpell: ignore deinit
5
6use i_slint_core::{items::ColorScheme, Brush};
7
8use super::*;
9
10cpp! {{
11namespace {
12struct PaletteStyleChangeListener : QWidget {
13    const void *qtStylePalette = nullptr;
14    PaletteStyleChangeListener(const void *qtStylePalette) : qtStylePalette(qtStylePalette) {}
15    bool event(QEvent *event) override {
16        auto ty = event->type();
17        if (ty == QEvent::StyleChange || ty == QEvent::PaletteChange || ty == QEvent::FontChange) {
18            rust!(Slint_qt_style_change_event [qtStylePalette: Pin<&NativePalette> as "const void*"] {
19                qtStylePalette.init_impl();
20            });
21        }
22        return QWidget::event(event);
23    }
24};
25}
26}}
27
28#[repr(C)]
29#[derive(FieldOffsets, SlintElement)]
30#[pin]
31#[pin_drop]
32pub struct NativePalette {
33    pub background: Property<Brush>,
34    pub foreground: Property<Brush>,
35    pub alternate_background: Property<Brush>,
36    pub alternate_foreground: Property<Brush>,
37    pub accent_background: Property<Brush>,
38    pub accent_foreground: Property<Brush>,
39    pub control_background: Property<Brush>,
40    pub control_foreground: Property<Brush>,
41    pub selection_background: Property<Brush>,
42    pub selection_foreground: Property<Brush>,
43    pub border: Property<Brush>,
44    pub color_scheme: Property<ColorScheme>,
45    pub style_change_listener: core::cell::Cell<*const u8>,
46}
47
48impl const_field_offset::PinnedDrop for NativePalette {
49    fn drop(self: Pin<&mut Self>) {
50        slint_native_palette_deinit(self);
51    }
52}
53
54impl NativePalette {
55    pub fn new() -> Pin<Rc<Self>> {
56        Rc::pin(NativePalette {
57            background: Default::default(),
58            alternate_background: Default::default(),
59            alternate_foreground: Default::default(),
60            foreground: Default::default(),
61            accent_background: Default::default(),
62            accent_foreground: Default::default(),
63            control_background: Default::default(),
64            control_foreground: Default::default(),
65            border: Default::default(),
66            selection_background: Default::default(),
67            selection_foreground: Default::default(),
68            color_scheme: Default::default(),
69            style_change_listener: core::cell::Cell::new(core::ptr::null()),
70        })
71    }
72
73    pub fn init<T>(self: Pin<Rc<Self>>, _root: &T) {
74        self.as_ref().init_impl();
75    }
76
77    fn init_impl(self: Pin<&Self>) {
78        let wrong_thread = cpp!(unsafe [] -> bool as "bool" {
79            static QMutex mtx;
80            QMutexLocker locker(&mtx);
81            ensure_initialized();
82            return qApp->thread() != QThread::currentThread();
83        });
84        if wrong_thread {
85            return;
86        }
87
88        let background = cpp!(unsafe[] -> u32 as "QRgb" {
89            return qApp->palette().color(QPalette::Window).rgba();
90        });
91        let background = Color::from_argb_encoded(background);
92        self.background.set(Brush::from(background));
93
94        let alternate_background = cpp!(unsafe[] -> u32 as "QRgb" {
95            return qApp->palette().color(QPalette::Base).rgba();
96        });
97        let alternate_background = Color::from_argb_encoded(alternate_background);
98        self.alternate_background.set(Brush::from(alternate_background));
99
100        let alternate_foreground = cpp!(unsafe[] -> u32 as "QRgb" {
101            return qApp->palette().color(QPalette::Text).rgba();
102        });
103        let alternate_foreground = Color::from_argb_encoded(alternate_foreground);
104        self.alternate_foreground.set(Brush::from(alternate_foreground));
105
106        let foreground = cpp!(unsafe[] -> u32 as "QRgb" {
107            return qApp->palette().color(QPalette::WindowText).rgba();
108        });
109        let foreground = Color::from_argb_encoded(foreground);
110        self.foreground.set(Brush::from(foreground));
111
112        let accent_background = cpp!(unsafe[] -> u32 as "QRgb" {
113            return qApp->palette().color(QPalette::Link).rgba();
114        });
115        let accent_background = Color::from_argb_encoded(accent_background);
116        self.accent_background.set(Brush::from(accent_background));
117
118        let accent_foreground = cpp!(unsafe[] -> u32 as "QRgb" {
119            #if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
120                return qApp->palette().color(QPalette::Accent).rgba();
121            #else
122                return qApp->palette().color(QPalette::Highlight).rgba();
123            #endif
124        });
125        let accent_foreground = Color::from_argb_encoded(accent_foreground);
126        self.accent_foreground.set(Brush::from(accent_foreground));
127
128        let control_background = cpp!(unsafe[] -> u32 as "QRgb" {
129            return qApp->palette().color(QPalette::Button).rgba();
130        });
131        let control_background = Color::from_argb_encoded(control_background);
132        self.control_background.set(Brush::from(control_background));
133
134        let control_foreground = cpp!(unsafe[] -> u32 as "QRgb" {
135            return qApp->palette().color(QPalette::ButtonText).rgba();
136        });
137        let control_foreground = Color::from_argb_encoded(control_foreground);
138        self.control_foreground.set(Brush::from(control_foreground));
139
140        let border = cpp!(unsafe[] -> u32 as "QRgb" {
141            return qApp->palette().color(QPalette::Midlight).rgba();
142        });
143        let border = Color::from_argb_encoded(border);
144        self.border.set(Brush::from(border));
145
146        let selection_background = cpp!(unsafe[] -> u32 as "QRgb" {
147            return qApp->palette().color(QPalette::Highlight).rgba();
148        });
149        let selection_background = Color::from_argb_encoded(selection_background);
150        self.selection_background.set(Brush::from(selection_background));
151
152        let selection_foreground = cpp!(unsafe[] -> u32 as "QRgb" {
153            return qApp->palette().color(QPalette::HighlightedText).rgba();
154        });
155        let selection_foreground = Color::from_argb_encoded(selection_foreground);
156        self.selection_foreground.set(Brush::from(selection_foreground));
157
158        self.color_scheme.set(
159            if (background.red() as u32 + background.green() as u32 + background.blue() as u32) / 3
160                < 128
161            {
162                ColorScheme::Dark
163            } else {
164                ColorScheme::Light
165            },
166        );
167
168        if self.style_change_listener.get().is_null() {
169            self.style_change_listener.set(cpp!(unsafe [self as "void*"] -> *const u8 as "void*"{
170                return new PaletteStyleChangeListener(self);
171            }));
172        }
173    }
174}
175
176#[cfg(feature = "rtti")]
177impl i_slint_core::rtti::BuiltinGlobal for NativePalette {
178    fn new() -> Pin<Rc<Self>> {
179        let r = NativePalette::new();
180        r.as_ref().init_impl();
181        r
182    }
183}
184
185#[unsafe(no_mangle)]
186pub extern "C" fn slint_native_palette_init(self_: Pin<&NativePalette>) {
187    self_.style_change_listener.set(core::ptr::null()); // because the C++ code don't initialize it
188    self_.init_impl();
189}
190
191#[unsafe(no_mangle)]
192pub extern "C" fn slint_native_palette_deinit(self_: Pin<&mut NativePalette>) {
193    let scl = self_.style_change_listener.get();
194    cpp!(unsafe [scl as "PaletteStyleChangeListener*"] { delete scl; });
195    self_.style_change_listener.set(core::ptr::null());
196}