i_slint_backend_qt/qt_widgets/
stylemetrics.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0

// cSpell: ignore deinit

use i_slint_core::items::LayoutAlignment;

use super::*;

cpp! {{
namespace {
struct StyleChangeListener : QWidget {
    const void *nativeStyleMetrics = nullptr;
    StyleChangeListener(const void *nativeStyleMetrics) : nativeStyleMetrics(nativeStyleMetrics) {}
    bool event(QEvent *event) override {
        auto ty = event->type();
        if (ty == QEvent::StyleChange || ty == QEvent::PaletteChange || ty == QEvent::FontChange) {
            rust!(Slint_style_change_event [nativeStyleMetrics: Pin<&NativeStyleMetrics> as "const void*"] {
                nativeStyleMetrics.init_impl();
            });
        }
        return QWidget::event(event);
    }
};
}
}}

#[repr(C)]
#[derive(FieldOffsets, SlintElement)]
#[pin]
#[pin_drop]
pub struct NativeStyleMetrics {
    pub layout_spacing: Property<LogicalLength>,
    pub layout_padding: Property<LogicalLength>,
    pub text_cursor_width: Property<LogicalLength>,
    pub window_background: Property<Color>,
    pub default_text_color: Property<Color>,
    pub default_font_size: Property<LogicalLength>,
    pub textedit_background: Property<Color>,
    pub textedit_text_color: Property<Color>,
    pub textedit_background_disabled: Property<Color>,
    pub textedit_text_color_disabled: Property<Color>,

    pub placeholder_color: Property<Color>,
    pub placeholder_color_disabled: Property<Color>,

    pub dark_color_scheme: Property<bool>,

    // Tab Bar metrics:
    pub tab_bar_alignment: Property<LayoutAlignment>,

    pub style_change_listener: core::cell::Cell<*const u8>,
}

impl const_field_offset::PinnedDrop for NativeStyleMetrics {
    fn drop(self: Pin<&mut Self>) {
        slint_native_style_metrics_deinit(self);
    }
}

impl NativeStyleMetrics {
    pub fn new() -> Pin<Rc<Self>> {
        Rc::pin(NativeStyleMetrics {
            layout_spacing: Default::default(),
            layout_padding: Default::default(),
            text_cursor_width: Default::default(),
            window_background: Default::default(),
            default_text_color: Default::default(),
            default_font_size: Default::default(),
            textedit_background: Default::default(),
            textedit_text_color: Default::default(),
            textedit_background_disabled: Default::default(),
            textedit_text_color_disabled: Default::default(),
            placeholder_color: Default::default(),
            placeholder_color_disabled: Default::default(),
            dark_color_scheme: Default::default(),
            tab_bar_alignment: Default::default(),
            style_change_listener: core::cell::Cell::new(core::ptr::null()),
        })
    }

    pub fn init<T>(self: Pin<Rc<Self>>, _root: &T) {
        self.as_ref().init_impl();
    }

    fn init_impl(self: Pin<&Self>) {
        let wrong_thread = cpp!(unsafe [] -> bool as "bool" {
            static QMutex mtx;
            QMutexLocker locker(&mtx);
            ensure_initialized();
            return qApp->thread() != QThread::currentThread();
        });
        if wrong_thread {
            return;
        }

        if self.style_change_listener.get().is_null() {
            self.style_change_listener.set(cpp!(unsafe [self as "void*"] -> *const u8 as "void*"{
                return new StyleChangeListener(self);
            }));
        }

        let layout_spacing = cpp!(unsafe [] -> f32 as "float" {
            int spacing = qApp->style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing);
            if (spacing < 0)
                spacing = qApp->style()->layoutSpacing(QSizePolicy::DefaultType, QSizePolicy::DefaultType, Qt::Horizontal);
            return spacing;
        });
        self.layout_spacing.set(LogicalLength::new(layout_spacing.max(0.0)));
        let layout_padding = cpp!(unsafe [] -> f32 as "float" {
            return qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin);
        });
        self.layout_padding.set(LogicalLength::new(layout_padding.max(0.0)));
        let text_cursor_width = cpp!(unsafe [] -> f32 as "float" {
            return qApp->style()->pixelMetric(QStyle::PM_TextCursorWidth);
        });
        self.text_cursor_width.set(LogicalLength::new(text_cursor_width.max(0.0)));
        let window_background = cpp!(unsafe[] -> u32 as "QRgb" {
            return qApp->palette().color(QPalette::Window).rgba();
        });
        let window_background = Color::from_argb_encoded(window_background);
        self.window_background.set(window_background);
        let default_text_color = cpp!(unsafe[] -> u32 as "QRgb" {
            return qApp->palette().color(QPalette::WindowText).rgba();
        });
        self.default_text_color.set(Color::from_argb_encoded(default_text_color));
        let default_font_size = cpp!(unsafe[] -> i32 as "int" {
            return QFontInfo(qApp->font()).pixelSize();
        });
        self.default_font_size.set(LogicalLength::new(default_font_size as f32));
        let textedit_text_color = cpp!(unsafe[] -> u32 as "QRgb" {
            return qApp->palette().color(QPalette::Text).rgba();
        });
        self.textedit_text_color.set(Color::from_argb_encoded(textedit_text_color));
        let textedit_text_color_disabled = cpp!(unsafe[] -> u32 as "QRgb" {
            return qApp->palette().color(QPalette::Disabled, QPalette::Text).rgba();
        });
        self.textedit_text_color_disabled
            .set(Color::from_argb_encoded(textedit_text_color_disabled));
        let textedit_background = cpp!(unsafe[] -> u32 as "QRgb" {
            return qApp->palette().color(QPalette::Base).rgba();
        });
        self.textedit_background.set(Color::from_argb_encoded(textedit_background));
        let textedit_background_disabled = cpp!(unsafe[] -> u32 as "QRgb" {
            return qApp->palette().color(QPalette::Disabled, QPalette::Base).rgba();
        });
        self.textedit_background_disabled
            .set(Color::from_argb_encoded(textedit_background_disabled));
        let placeholder_color = cpp!(unsafe[] -> u32 as "QRgb" {
            return qApp->palette().color(QPalette::PlaceholderText).rgba();
        });
        self.placeholder_color.set(Color::from_argb_encoded(placeholder_color));
        let placeholder_color_disabled = cpp!(unsafe[] -> u32 as "QRgb" {
            return qApp->palette().color(QPalette::Disabled, QPalette::PlaceholderText).rgba();
        });
        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
                + window_background.blue() as u32)
                / 3
                < 128,
        );

        let tab_bar_alignment = cpp!(unsafe[] -> u32 as "uint32_t" {
            switch (qApp->style()->styleHint(QStyle::SH_TabBar_Alignment)) {
                case Qt::AlignLeft: return 1;
                case Qt::AlignCenter: return 2;
                case Qt::AlignRight: return 3;
                default: return 0;
            }
        });
        self.tab_bar_alignment.set(match tab_bar_alignment {
            1 => LayoutAlignment::Start,
            2 => LayoutAlignment::Center,
            3 => LayoutAlignment::End,
            _ => LayoutAlignment::SpaceBetween, // Should not happen! If it does, it should be noticeable;-)
        });
    }
}

#[cfg(feature = "rtti")]
impl i_slint_core::rtti::BuiltinGlobal for NativeStyleMetrics {
    fn new() -> Pin<Rc<Self>> {
        let r = NativeStyleMetrics::new();
        r.as_ref().init_impl();
        r
    }
}

#[no_mangle]
pub extern "C" fn slint_native_style_metrics_init(self_: Pin<&NativeStyleMetrics>) {
    self_.style_change_listener.set(core::ptr::null()); // because the C++ code don't initialize it
    self_.init_impl();
}

#[no_mangle]
pub extern "C" fn slint_native_style_metrics_deinit(self_: Pin<&mut NativeStyleMetrics>) {
    let scl = self_.style_change_listener.get();
    cpp!(unsafe [scl as "StyleChangeListener*"] { delete scl; });
    self_.style_change_listener.set(core::ptr::null());
}