i_slint_backend_qt/qt_widgets/
stylemetrics.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::LayoutAlignment;
7
8use super::*;
9
10cpp! {{
11namespace {
12struct StyleChangeListener : QWidget {
13    const void *nativeStyleMetrics = nullptr;
14    StyleChangeListener(const void *nativeStyleMetrics) : nativeStyleMetrics(nativeStyleMetrics) {}
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_style_change_event [nativeStyleMetrics: Pin<&NativeStyleMetrics> as "const void*"] {
19                nativeStyleMetrics.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 NativeStyleMetrics {
33    pub layout_spacing: Property<LogicalLength>,
34    pub layout_padding: Property<LogicalLength>,
35    pub text_cursor_width: Property<LogicalLength>,
36    pub window_background: Property<Color>,
37    pub default_text_color: Property<Color>,
38    pub textedit_background: Property<Color>,
39    pub textedit_text_color: Property<Color>,
40    pub textedit_background_disabled: Property<Color>,
41    pub textedit_text_color_disabled: Property<Color>,
42
43    pub placeholder_color: Property<Color>,
44    pub placeholder_color_disabled: Property<Color>,
45
46    pub dark_color_scheme: Property<bool>,
47
48    // Tab Bar metrics:
49    pub tab_bar_alignment: Property<LayoutAlignment>,
50
51    pub style_name: Property<SharedString>,
52
53    pub style_change_listener: core::cell::Cell<*const u8>,
54}
55
56impl const_field_offset::PinnedDrop for NativeStyleMetrics {
57    fn drop(self: Pin<&mut Self>) {
58        slint_native_style_metrics_deinit(self);
59    }
60}
61
62impl NativeStyleMetrics {
63    pub fn new() -> Pin<Rc<Self>> {
64        Rc::pin(NativeStyleMetrics {
65            layout_spacing: Default::default(),
66            layout_padding: Default::default(),
67            text_cursor_width: Default::default(),
68            window_background: Default::default(),
69            default_text_color: Default::default(),
70            textedit_background: Default::default(),
71            textedit_text_color: Default::default(),
72            textedit_background_disabled: Default::default(),
73            textedit_text_color_disabled: Default::default(),
74            placeholder_color: Default::default(),
75            placeholder_color_disabled: Default::default(),
76            dark_color_scheme: Default::default(),
77            tab_bar_alignment: Default::default(),
78            style_name: Default::default(),
79            style_change_listener: core::cell::Cell::new(core::ptr::null()),
80        })
81    }
82
83    pub fn init<T>(self: Pin<Rc<Self>>, _root: &T) {
84        self.as_ref().init_impl();
85    }
86
87    fn init_impl(self: Pin<&Self>) {
88        let wrong_thread = cpp!(unsafe [] -> bool as "bool" {
89            static QMutex mtx;
90            QMutexLocker locker(&mtx);
91            ensure_initialized();
92            return qApp->thread() != QThread::currentThread();
93        });
94        if wrong_thread {
95            return;
96        }
97
98        if self.style_change_listener.get().is_null() {
99            self.style_change_listener.set(cpp!(unsafe [self as "void*"] -> *const u8 as "void*"{
100                return new StyleChangeListener(self);
101            }));
102        }
103
104        let layout_spacing = cpp!(unsafe [] -> f32 as "float" {
105            int spacing = qApp->style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing);
106            if (spacing < 0)
107                spacing = qApp->style()->layoutSpacing(QSizePolicy::DefaultType, QSizePolicy::DefaultType, Qt::Horizontal);
108            return spacing;
109        });
110        self.layout_spacing.set(LogicalLength::new(layout_spacing.max(0.0)));
111        let layout_padding = cpp!(unsafe [] -> f32 as "float" {
112            return qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin);
113        });
114        self.layout_padding.set(LogicalLength::new(layout_padding.max(0.0)));
115        let text_cursor_width = cpp!(unsafe [] -> f32 as "float" {
116            return qApp->style()->pixelMetric(QStyle::PM_TextCursorWidth);
117        });
118        self.text_cursor_width.set(LogicalLength::new(text_cursor_width.max(0.0)));
119        let window_background = cpp!(unsafe[] -> u32 as "QRgb" {
120            return qApp->palette().color(QPalette::Window).rgba();
121        });
122        let window_background = Color::from_argb_encoded(window_background);
123        self.window_background.set(window_background);
124        let default_text_color = cpp!(unsafe[] -> u32 as "QRgb" {
125            return qApp->palette().color(QPalette::WindowText).rgba();
126        });
127        self.default_text_color.set(Color::from_argb_encoded(default_text_color));
128        let textedit_text_color = cpp!(unsafe[] -> u32 as "QRgb" {
129            return qApp->palette().color(QPalette::Text).rgba();
130        });
131        self.textedit_text_color.set(Color::from_argb_encoded(textedit_text_color));
132        let textedit_text_color_disabled = cpp!(unsafe[] -> u32 as "QRgb" {
133            return qApp->palette().color(QPalette::Disabled, QPalette::Text).rgba();
134        });
135        self.textedit_text_color_disabled
136            .set(Color::from_argb_encoded(textedit_text_color_disabled));
137        let textedit_background = cpp!(unsafe[] -> u32 as "QRgb" {
138            return qApp->palette().color(QPalette::Base).rgba();
139        });
140        self.textedit_background.set(Color::from_argb_encoded(textedit_background));
141        let textedit_background_disabled = cpp!(unsafe[] -> u32 as "QRgb" {
142            return qApp->palette().color(QPalette::Disabled, QPalette::Base).rgba();
143        });
144        self.textedit_background_disabled
145            .set(Color::from_argb_encoded(textedit_background_disabled));
146        let placeholder_color = cpp!(unsafe[] -> u32 as "QRgb" {
147            return qApp->palette().color(QPalette::PlaceholderText).rgba();
148        });
149        self.placeholder_color.set(Color::from_argb_encoded(placeholder_color));
150        let placeholder_color_disabled = cpp!(unsafe[] -> u32 as "QRgb" {
151            return qApp->palette().color(QPalette::Disabled, QPalette::PlaceholderText).rgba();
152        });
153        self.placeholder_color_disabled.set(Color::from_argb_encoded(placeholder_color_disabled));
154
155        // This is sub-optimal: It should really be a binding to Palette.color-scheme == ColorScheme.dark, so that
156        // writes to Palette.color-scheme are reflected, but we can't access the other global singleton here and
157        // this is just a backwards-compat property that was never documented to be public.
158        self.dark_color_scheme.set(
159            (window_background.red() as u32
160                + window_background.green() as u32
161                + window_background.blue() as u32)
162                / 3
163                < 128,
164        );
165
166        let tab_bar_alignment = cpp!(unsafe[] -> u32 as "uint32_t" {
167            switch (qApp->style()->styleHint(QStyle::SH_TabBar_Alignment)) {
168                case Qt::AlignLeft: return 1;
169                case Qt::AlignCenter: return 2;
170                case Qt::AlignRight: return 3;
171                default: return 0;
172            }
173        });
174        self.tab_bar_alignment.set(match tab_bar_alignment {
175            1 => LayoutAlignment::Start,
176            2 => LayoutAlignment::Center,
177            3 => LayoutAlignment::End,
178            _ => LayoutAlignment::SpaceBetween, // Should not happen! If it does, it should be noticeable;-)
179        });
180    }
181}
182
183#[cfg(feature = "rtti")]
184impl i_slint_core::rtti::BuiltinGlobal for NativeStyleMetrics {
185    fn new() -> Pin<Rc<Self>> {
186        let r = NativeStyleMetrics::new();
187        r.as_ref().init_impl();
188        r
189    }
190}
191
192#[unsafe(no_mangle)]
193pub extern "C" fn slint_native_style_metrics_init(self_: Pin<&NativeStyleMetrics>) {
194    self_.style_change_listener.set(core::ptr::null()); // because the C++ code don't initialize it
195    self_.init_impl();
196}
197
198#[unsafe(no_mangle)]
199pub extern "C" fn slint_native_style_metrics_deinit(self_: Pin<&mut NativeStyleMetrics>) {
200    let scl = self_.style_change_listener.get();
201    cpp!(unsafe [scl as "StyleChangeListener*"] { delete scl; });
202    self_.style_change_listener.set(core::ptr::null());
203}