i_slint_backend_qt/qt_widgets/
button.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 qstyle unshade
5
6use super::*;
7use i_slint_core::graphics::euclid;
8
9#[allow(nonstandard_style)]
10#[allow(unused)]
11mod standard_button {
12    // Generated with
13    // bindgen /usr/include/qt/QtWidgets/qstyle.h  --whitelist-type QStyle -- -I /usr/include/qt -xc++ | grep _StandardPixmap_ -A1
14    pub const QStyle_StandardPixmap_SP_TitleBarMenuButton: QStyle_StandardPixmap = 0;
15    pub const QStyle_StandardPixmap_SP_TitleBarMinButton: QStyle_StandardPixmap = 1;
16    pub const QStyle_StandardPixmap_SP_TitleBarMaxButton: QStyle_StandardPixmap = 2;
17    pub const QStyle_StandardPixmap_SP_TitleBarCloseButton: QStyle_StandardPixmap = 3;
18    pub const QStyle_StandardPixmap_SP_TitleBarNormalButton: QStyle_StandardPixmap = 4;
19    pub const QStyle_StandardPixmap_SP_TitleBarShadeButton: QStyle_StandardPixmap = 5;
20    pub const QStyle_StandardPixmap_SP_TitleBarUnshadeButton: QStyle_StandardPixmap = 6;
21    pub const QStyle_StandardPixmap_SP_TitleBarContextHelpButton: QStyle_StandardPixmap = 7;
22    pub const QStyle_StandardPixmap_SP_DockWidgetCloseButton: QStyle_StandardPixmap = 8;
23    pub const QStyle_StandardPixmap_SP_MessageBoxInformation: QStyle_StandardPixmap = 9;
24    pub const QStyle_StandardPixmap_SP_MessageBoxWarning: QStyle_StandardPixmap = 10;
25    pub const QStyle_StandardPixmap_SP_MessageBoxCritical: QStyle_StandardPixmap = 11;
26    pub const QStyle_StandardPixmap_SP_MessageBoxQuestion: QStyle_StandardPixmap = 12;
27    pub const QStyle_StandardPixmap_SP_DesktopIcon: QStyle_StandardPixmap = 13;
28    pub const QStyle_StandardPixmap_SP_TrashIcon: QStyle_StandardPixmap = 14;
29    pub const QStyle_StandardPixmap_SP_ComputerIcon: QStyle_StandardPixmap = 15;
30    pub const QStyle_StandardPixmap_SP_DriveFDIcon: QStyle_StandardPixmap = 16;
31    pub const QStyle_StandardPixmap_SP_DriveHDIcon: QStyle_StandardPixmap = 17;
32    pub const QStyle_StandardPixmap_SP_DriveCDIcon: QStyle_StandardPixmap = 18;
33    pub const QStyle_StandardPixmap_SP_DriveDVDIcon: QStyle_StandardPixmap = 19;
34    pub const QStyle_StandardPixmap_SP_DriveNetIcon: QStyle_StandardPixmap = 20;
35    pub const QStyle_StandardPixmap_SP_DirOpenIcon: QStyle_StandardPixmap = 21;
36    pub const QStyle_StandardPixmap_SP_DirClosedIcon: QStyle_StandardPixmap = 22;
37    pub const QStyle_StandardPixmap_SP_DirLinkIcon: QStyle_StandardPixmap = 23;
38    pub const QStyle_StandardPixmap_SP_DirLinkOpenIcon: QStyle_StandardPixmap = 24;
39    pub const QStyle_StandardPixmap_SP_FileIcon: QStyle_StandardPixmap = 25;
40    pub const QStyle_StandardPixmap_SP_FileLinkIcon: QStyle_StandardPixmap = 26;
41    pub const QStyle_StandardPixmap_SP_ToolBarHorizontalExtensionButton: QStyle_StandardPixmap = 27;
42    pub const QStyle_StandardPixmap_SP_ToolBarVerticalExtensionButton: QStyle_StandardPixmap = 28;
43    pub const QStyle_StandardPixmap_SP_FileDialogStart: QStyle_StandardPixmap = 29;
44    pub const QStyle_StandardPixmap_SP_FileDialogEnd: QStyle_StandardPixmap = 30;
45    pub const QStyle_StandardPixmap_SP_FileDialogToParent: QStyle_StandardPixmap = 31;
46    pub const QStyle_StandardPixmap_SP_FileDialogNewFolder: QStyle_StandardPixmap = 32;
47    pub const QStyle_StandardPixmap_SP_FileDialogDetailedView: QStyle_StandardPixmap = 33;
48    pub const QStyle_StandardPixmap_SP_FileDialogInfoView: QStyle_StandardPixmap = 34;
49    pub const QStyle_StandardPixmap_SP_FileDialogContentsView: QStyle_StandardPixmap = 35;
50    pub const QStyle_StandardPixmap_SP_FileDialogListView: QStyle_StandardPixmap = 36;
51    pub const QStyle_StandardPixmap_SP_FileDialogBack: QStyle_StandardPixmap = 37;
52    pub const QStyle_StandardPixmap_SP_DirIcon: QStyle_StandardPixmap = 38;
53    pub const QStyle_StandardPixmap_SP_DialogOkButton: QStyle_StandardPixmap = 39;
54    pub const QStyle_StandardPixmap_SP_DialogCancelButton: QStyle_StandardPixmap = 40;
55    pub const QStyle_StandardPixmap_SP_DialogHelpButton: QStyle_StandardPixmap = 41;
56    pub const QStyle_StandardPixmap_SP_DialogOpenButton: QStyle_StandardPixmap = 42;
57    pub const QStyle_StandardPixmap_SP_DialogSaveButton: QStyle_StandardPixmap = 43;
58    pub const QStyle_StandardPixmap_SP_DialogCloseButton: QStyle_StandardPixmap = 44;
59    pub const QStyle_StandardPixmap_SP_DialogApplyButton: QStyle_StandardPixmap = 45;
60    pub const QStyle_StandardPixmap_SP_DialogResetButton: QStyle_StandardPixmap = 46;
61    pub const QStyle_StandardPixmap_SP_DialogDiscardButton: QStyle_StandardPixmap = 47;
62    pub const QStyle_StandardPixmap_SP_DialogYesButton: QStyle_StandardPixmap = 48;
63    pub const QStyle_StandardPixmap_SP_DialogNoButton: QStyle_StandardPixmap = 49;
64    pub const QStyle_StandardPixmap_SP_ArrowUp: QStyle_StandardPixmap = 50;
65    pub const QStyle_StandardPixmap_SP_ArrowDown: QStyle_StandardPixmap = 51;
66    pub const QStyle_StandardPixmap_SP_ArrowLeft: QStyle_StandardPixmap = 52;
67    pub const QStyle_StandardPixmap_SP_ArrowRight: QStyle_StandardPixmap = 53;
68    pub const QStyle_StandardPixmap_SP_ArrowBack: QStyle_StandardPixmap = 54;
69    pub const QStyle_StandardPixmap_SP_ArrowForward: QStyle_StandardPixmap = 55;
70    pub const QStyle_StandardPixmap_SP_DirHomeIcon: QStyle_StandardPixmap = 56;
71    pub const QStyle_StandardPixmap_SP_CommandLink: QStyle_StandardPixmap = 57;
72    pub const QStyle_StandardPixmap_SP_VistaShield: QStyle_StandardPixmap = 58;
73    pub const QStyle_StandardPixmap_SP_BrowserReload: QStyle_StandardPixmap = 59;
74    pub const QStyle_StandardPixmap_SP_BrowserStop: QStyle_StandardPixmap = 60;
75    pub const QStyle_StandardPixmap_SP_MediaPlay: QStyle_StandardPixmap = 61;
76    pub const QStyle_StandardPixmap_SP_MediaStop: QStyle_StandardPixmap = 62;
77    pub const QStyle_StandardPixmap_SP_MediaPause: QStyle_StandardPixmap = 63;
78    pub const QStyle_StandardPixmap_SP_MediaSkipForward: QStyle_StandardPixmap = 64;
79    pub const QStyle_StandardPixmap_SP_MediaSkipBackward: QStyle_StandardPixmap = 65;
80    pub const QStyle_StandardPixmap_SP_MediaSeekForward: QStyle_StandardPixmap = 66;
81    pub const QStyle_StandardPixmap_SP_MediaSeekBackward: QStyle_StandardPixmap = 67;
82    pub const QStyle_StandardPixmap_SP_MediaVolume: QStyle_StandardPixmap = 68;
83    pub const QStyle_StandardPixmap_SP_MediaVolumeMuted: QStyle_StandardPixmap = 69;
84    pub const QStyle_StandardPixmap_SP_LineEditClearButton: QStyle_StandardPixmap = 70;
85    pub const QStyle_StandardPixmap_SP_DialogYesToAllButton: QStyle_StandardPixmap = 71;
86    pub const QStyle_StandardPixmap_SP_DialogNoToAllButton: QStyle_StandardPixmap = 72;
87    pub const QStyle_StandardPixmap_SP_DialogSaveAllButton: QStyle_StandardPixmap = 73;
88    pub const QStyle_StandardPixmap_SP_DialogAbortButton: QStyle_StandardPixmap = 74;
89    pub const QStyle_StandardPixmap_SP_DialogRetryButton: QStyle_StandardPixmap = 75;
90    pub const QStyle_StandardPixmap_SP_DialogIgnoreButton: QStyle_StandardPixmap = 76;
91    pub const QStyle_StandardPixmap_SP_RestoreDefaultsButton: QStyle_StandardPixmap = 77;
92    pub const QStyle_StandardPixmap_SP_CustomBase: QStyle_StandardPixmap = 4026531840;
93    pub type QStyle_StandardPixmap = ::std::os::raw::c_uint;
94}
95
96use i_slint_core::{
97    input::{FocusEventResult, KeyEventType},
98    items::StandardButtonKind,
99    platform::PointerEventButton,
100};
101use standard_button::*;
102
103type ActualStandardButtonKind = Option<StandardButtonKind>;
104
105#[repr(C)]
106#[derive(FieldOffsets, Default, SlintElement)]
107#[pin]
108pub struct NativeButton {
109    pub text: Property<SharedString>,
110    pub icon: Property<i_slint_core::graphics::Image>,
111    pub icon_size: Property<LogicalLength>,
112    pub pressed: Property<bool>,
113    pub has_hover: Property<bool>,
114    pub checkable: Property<bool>,
115    pub checked: Property<bool>,
116    pub primary: Property<bool>,
117    pub has_focus: Property<bool>,
118    pub clicked: Callback<VoidArg>,
119    pub enabled: Property<bool>,
120    pub colorize_icon: Property<bool>,
121    pub standard_button_kind: Property<StandardButtonKind>,
122    pub is_standard_button: Property<bool>,
123    widget_ptr: std::cell::Cell<SlintTypeErasedWidgetPtr>,
124    animation_tracker: Property<i32>,
125    pub cached_rendering_data: CachedRenderingData,
126}
127
128impl NativeButton {
129    fn actual_standard_button_kind(self: Pin<&Self>) -> ActualStandardButtonKind {
130        self.is_standard_button().then(|| self.standard_button_kind())
131    }
132
133    fn actual_text(
134        self: Pin<&Self>,
135        standard_button_kind: ActualStandardButtonKind,
136    ) -> qttypes::QString {
137        // We would need to use the private API to get the text from QPlatformTheme
138        match standard_button_kind {
139            Some(StandardButtonKind::Ok) => "OK".into(),
140            Some(StandardButtonKind::Cancel) => "Cancel".into(),
141            Some(StandardButtonKind::Apply) => "Apply".into(),
142            Some(StandardButtonKind::Close) => "Close".into(),
143            Some(StandardButtonKind::Reset) => "Reset".into(),
144            Some(StandardButtonKind::Help) => "Help".into(),
145            Some(StandardButtonKind::Yes) => "Yes".into(),
146            Some(StandardButtonKind::No) => "No".into(),
147            Some(StandardButtonKind::Abort) => "Abort".into(),
148            Some(StandardButtonKind::Retry) => "Retry".into(),
149            Some(StandardButtonKind::Ignore) => "Ignore".into(),
150            None => self.text().as_str().into(),
151        }
152    }
153
154    fn actual_icon(
155        self: Pin<&Self>,
156        standard_button_kind: ActualStandardButtonKind,
157    ) -> qttypes::QPixmap {
158        let style_icon = match standard_button_kind {
159            Some(StandardButtonKind::Ok) => QStyle_StandardPixmap_SP_DialogOkButton,
160            Some(StandardButtonKind::Cancel) => QStyle_StandardPixmap_SP_DialogCancelButton,
161            Some(StandardButtonKind::Apply) => QStyle_StandardPixmap_SP_DialogApplyButton,
162            Some(StandardButtonKind::Close) => QStyle_StandardPixmap_SP_DialogCloseButton,
163            Some(StandardButtonKind::Reset) => QStyle_StandardPixmap_SP_DialogResetButton,
164            Some(StandardButtonKind::Help) => QStyle_StandardPixmap_SP_DialogHelpButton,
165            Some(StandardButtonKind::Yes) => QStyle_StandardPixmap_SP_DialogYesButton,
166            Some(StandardButtonKind::No) => QStyle_StandardPixmap_SP_DialogNoButton,
167            Some(StandardButtonKind::Abort) => QStyle_StandardPixmap_SP_DialogAbortButton,
168            Some(StandardButtonKind::Retry) => QStyle_StandardPixmap_SP_DialogRetryButton,
169            Some(StandardButtonKind::Ignore) => QStyle_StandardPixmap_SP_DialogIgnoreButton,
170            None => {
171                let icon_size = self.icon_size().get().round() as u32;
172                let source_size = Some(euclid::Size2D::new(icon_size, icon_size));
173                return crate::qt_window::image_to_pixmap((&self.icon()).into(), source_size)
174                    .unwrap_or_default();
175            }
176        };
177        let widget_ptr: NonNull<()> = SlintTypeErasedWidgetPtr::qwidget_ptr(&self.widget_ptr);
178        cpp!(unsafe [style_icon as "QStyle::StandardPixmap", widget_ptr as "QWidget*"] -> qttypes::QPixmap as "QPixmap" {
179            ensure_initialized();
180            auto style = qApp->style();
181            if (!style->styleHint(QStyle::SH_DialogButtonBox_ButtonsHaveIcons, nullptr, widget_ptr))
182                return QPixmap();
183            return style->standardPixmap(style_icon);
184        })
185    }
186
187    fn activate(self: Pin<&Self>) {
188        Self::FIELD_OFFSETS.pressed.apply_pin(self).set(false);
189        if self.checkable() {
190            let checked = Self::FIELD_OFFSETS.checked.apply_pin(self);
191            checked.set(!checked.get());
192        }
193        Self::FIELD_OFFSETS.clicked.apply_pin(self).call(&());
194    }
195}
196
197impl Item for NativeButton {
198    fn init(self: Pin<&Self>, _self_rc: &ItemRc) {
199        let animation_tracker_property_ptr = Self::FIELD_OFFSETS.animation_tracker.apply_pin(self);
200        self.widget_ptr.set(cpp! { unsafe [animation_tracker_property_ptr as "void*"] -> SlintTypeErasedWidgetPtr as "std::unique_ptr<SlintTypeErasedWidget>" {
201            return make_unique_animated_widget<QPushButton>(animation_tracker_property_ptr);
202        }});
203        let widget_ptr: NonNull<()> = SlintTypeErasedWidgetPtr::qwidget_ptr(&self.widget_ptr);
204        let icon_size = unsafe {
205            cpp!([widget_ptr as "QWidget*" ] -> i32 as "int"
206            {
207                ensure_initialized();
208                return qApp->style()->pixelMetric(QStyle::PM_ButtonIconSize, 0, widget_ptr);
209            })
210        };
211        Self::FIELD_OFFSETS
212            .icon_size
213            .apply_pin(self)
214            .set(LogicalLength::new(icon_size as i_slint_core::Coord));
215    }
216
217    fn layout_info(
218        self: Pin<&Self>,
219        orientation: Orientation,
220        _window_adapter: &Rc<dyn WindowAdapter>,
221        _self_rc: &ItemRc,
222    ) -> LayoutInfo {
223        let standard_button_kind = self.actual_standard_button_kind();
224        let mut text: qttypes::QString = self.actual_text(standard_button_kind);
225        let icon: qttypes::QPixmap = self.actual_icon(standard_button_kind);
226        let icon_size = self.icon_size().get() as i32;
227        let widget_ptr: NonNull<()> = SlintTypeErasedWidgetPtr::qwidget_ptr(&self.widget_ptr);
228        let size = cpp!(unsafe [
229            mut text as "QString",
230            icon as "QPixmap",
231            icon_size as "int",
232            widget_ptr as "QWidget*"
233        ] -> qttypes::QSize as "QSize" {
234            ensure_initialized();
235            QStyleOptionButton option;
236            if (text.isEmpty())
237                text = "**";
238            option.rect = option.fontMetrics.boundingRect(text);
239            option.text = std::move(text);
240            option.icon = icon;
241            option.iconSize = QSize(icon_size, icon_size);
242            if (!icon.isNull()) {
243                option.rect.setHeight(qMax(option.rect.height(), icon_size));
244                option.rect.setWidth(option.rect.width() + 4 + icon_size);
245            }
246            return qApp->style()->sizeFromContents(QStyle::CT_PushButton, &option, option.rect.size(), widget_ptr);
247        });
248        let min = match orientation {
249            Orientation::Horizontal => size.width as f32,
250            Orientation::Vertical => size.height as f32,
251        };
252        LayoutInfo { min, preferred: min, ..LayoutInfo::default() }
253    }
254
255    fn input_event_filter_before_children(
256        self: Pin<&Self>,
257        event: &MouseEvent,
258        _window_adapter: &Rc<dyn WindowAdapter>,
259        _self_rc: &ItemRc,
260    ) -> InputEventFilterResult {
261        Self::FIELD_OFFSETS.has_hover.apply_pin(self).set(!matches!(event, MouseEvent::Exit));
262        InputEventFilterResult::ForwardEvent
263    }
264
265    fn input_event(
266        self: Pin<&Self>,
267        event: &MouseEvent,
268        _window_adapter: &Rc<dyn WindowAdapter>,
269        self_rc: &i_slint_core::items::ItemRc,
270    ) -> InputEventResult {
271        if matches!(event, MouseEvent::Exit) {
272            Self::FIELD_OFFSETS.has_hover.apply_pin(self).set(false);
273        }
274        let enabled = self.enabled();
275        if !enabled {
276            return InputEventResult::EventIgnored;
277        }
278
279        let was_pressed = self.pressed();
280
281        Self::FIELD_OFFSETS.pressed.apply_pin(self).set(match event {
282            MouseEvent::Pressed { button, .. } => *button == PointerEventButton::Left,
283            MouseEvent::Exit | MouseEvent::Released { .. } => false,
284            MouseEvent::Moved { .. } => {
285                return if was_pressed {
286                    InputEventResult::GrabMouse
287                } else {
288                    InputEventResult::EventAccepted
289                }
290            }
291            MouseEvent::Wheel { .. } => return InputEventResult::EventIgnored,
292            MouseEvent::DragMove(..) | MouseEvent::Drop(..) => {
293                return InputEventResult::EventIgnored
294            }
295        });
296        if let MouseEvent::Released { position, .. } = event {
297            let geo = self_rc.geometry();
298            if LogicalRect::new(LogicalPoint::default(), geo.size).contains(*position)
299                && was_pressed
300            {
301                self.activate();
302            }
303            InputEventResult::EventAccepted
304        } else {
305            InputEventResult::GrabMouse
306        }
307    }
308
309    fn capture_key_event(
310        self: Pin<&Self>,
311        _event: &KeyEvent,
312        _window_adapter: &Rc<dyn WindowAdapter>,
313        _self_rc: &ItemRc,
314    ) -> KeyEventResult {
315        KeyEventResult::EventIgnored
316    }
317
318    fn key_event(
319        self: Pin<&Self>,
320        event: &KeyEvent,
321        _window_adapter: &Rc<dyn WindowAdapter>,
322        _self_rc: &ItemRc,
323    ) -> KeyEventResult {
324        match event.event_type {
325            KeyEventType::KeyPressed if event.text == " " || event.text == "\n" => {
326                Self::FIELD_OFFSETS.pressed.apply_pin(self).set(true);
327                KeyEventResult::EventAccepted
328            }
329            KeyEventType::KeyPressed => KeyEventResult::EventIgnored,
330            KeyEventType::KeyReleased if event.text == " " || event.text == "\n" => {
331                self.activate();
332                KeyEventResult::EventAccepted
333            }
334            KeyEventType::KeyReleased => KeyEventResult::EventIgnored,
335            KeyEventType::UpdateComposition | KeyEventType::CommitComposition => {
336                KeyEventResult::EventIgnored
337            }
338        }
339    }
340
341    fn focus_event(
342        self: Pin<&Self>,
343        event: &FocusEvent,
344        _window_adapter: &Rc<dyn WindowAdapter>,
345        _self_rc: &ItemRc,
346    ) -> FocusEventResult {
347        if self.enabled() {
348            Self::FIELD_OFFSETS
349                .has_focus
350                .apply_pin(self)
351                .set(matches!(event, FocusEvent::FocusIn(_)));
352            FocusEventResult::FocusAccepted
353        } else {
354            FocusEventResult::FocusIgnored
355        }
356    }
357
358    fn_render! { this dpr size painter widget initial_state =>
359        let down: bool = this.pressed();
360        let checked: bool = this.checked();
361        let standard_button_kind = this.actual_standard_button_kind();
362        let text: qttypes::QString = this.actual_text(standard_button_kind);
363        let icon: qttypes::QPixmap = this.actual_icon(standard_button_kind);
364        let enabled = this.enabled();
365        let has_focus = this.has_focus();
366        let has_hover = this.has_hover();
367        let primary = this.primary();
368        let icon_size = this.icon_size().get().round() as i32;
369        let colorize_icon = this.colorize_icon();
370
371        cpp!(unsafe [
372            painter as "QPainterPtr*",
373            widget as "QWidget*",
374            text as "QString",
375            icon as "QPixmap",
376            enabled as "bool",
377            size as "QSize",
378            down as "bool",
379            checked as "bool",
380            has_focus as "bool",
381            has_hover as "bool",
382            primary as "bool",
383            icon_size as "int",
384            colorize_icon as "bool",
385            dpr as "float",
386            initial_state as "int"
387        ] {
388            class ColorizedIconEngine : public QIconEngine
389            {
390            public:
391                ColorizedIconEngine(const QIcon &icon, const QColor &color) : m_icon(icon), m_color(color) { }
392
393                QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) override
394                {
395                    QPixmap iconPixmap = m_icon.pixmap(size, mode, state);
396                    if (!iconPixmap.isNull()) {
397                        QPainter colorizePainter(&iconPixmap);
398                        colorizePainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
399                        colorizePainter.fillRect(iconPixmap.rect(), m_color);
400                    }
401                    return iconPixmap;
402                }
403
404                void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) override
405                {
406                    painter->drawPixmap(rect, this->pixmap(rect.size(), mode, state));
407                }
408
409                QIconEngine *clone() const override { return new ColorizedIconEngine(m_icon, m_color); }
410
411            private:
412                QIcon m_icon;
413                QColor m_color;
414            };
415
416            QStyleOptionButton option;
417            option.styleObject = widget;
418            option.state |= QStyle::State(initial_state);
419            option.text = std::move(text);
420
421            QColor iconColor = qApp->palette().color(QPalette::ButtonText).rgba();
422
423            if (down) {
424                option.state |= QStyle::State_Sunken;
425            } else {
426                option.state |= QStyle::State_Raised;
427            }
428            if (checked) {
429                option.state |= QStyle::State_On;
430            }
431            if (enabled) {
432                option.state |= QStyle::State_Enabled;
433            } else {
434                option.palette.setCurrentColorGroup(QPalette::Disabled);
435                iconColor = qApp->palette().color(QPalette::Disabled, QPalette::ButtonText).rgba();
436            }
437            if (has_focus) {
438                option.state |= QStyle::State_HasFocus | QStyle::State_KeyboardFocusChange | QStyle::State_Item;
439            }
440            if (has_hover) {
441                option.state |= QStyle::State_MouseOver;
442            }
443            if (primary) {
444                option.features |= QStyleOptionButton::DefaultButton;
445            }
446            if (colorize_icon) {
447                option.icon = QIcon(new ColorizedIconEngine(icon, iconColor));
448            } else {
449                option.icon = icon;
450            }
451            option.iconSize = QSize(icon_size, icon_size);
452            option.rect = QRect(QPoint(), size / dpr);
453
454            qApp->style()->drawControl(QStyle::CE_PushButton, &option, painter->get(), widget);
455        });
456    }
457
458    fn bounding_rect(
459        self: core::pin::Pin<&Self>,
460        _window_adapter: &Rc<dyn WindowAdapter>,
461        _self_rc: &ItemRc,
462        geometry: LogicalRect,
463    ) -> LogicalRect {
464        geometry
465    }
466
467    fn clips_children(self: core::pin::Pin<&Self>) -> bool {
468        false
469    }
470}
471
472impl ItemConsts for NativeButton {
473    const cached_rendering_data_offset: const_field_offset::FieldOffset<Self, CachedRenderingData> =
474        Self::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection();
475}
476
477declare_item_vtable! {
478    fn slint_get_NativeButtonVTable() -> NativeButtonVTable for NativeButton
479}