i_slint_backend_qt/qt_widgets/
checkbox.rs1use i_slint_core::{
5 input::{FocusEventResult, KeyEventType},
6 platform::PointerEventButton,
7};
8
9use super::*;
10
11#[repr(C)]
12#[derive(FieldOffsets, Default, SlintElement)]
13#[pin]
14pub struct NativeCheckBox {
15 pub enabled: Property<bool>,
16 pub has_focus: Property<bool>,
17 pub toggled: Callback<VoidArg>,
18 pub text: Property<SharedString>,
19 pub has_hover: Property<bool>,
20 pub checked: Property<bool>,
21 widget_ptr: std::cell::Cell<SlintTypeErasedWidgetPtr>,
22 animation_tracker: Property<i32>,
23 pub cached_rendering_data: CachedRenderingData,
24}
25
26impl Item for NativeCheckBox {
27 fn init(self: Pin<&Self>, _self_rc: &ItemRc) {
28 let animation_tracker_property_ptr = Self::FIELD_OFFSETS.animation_tracker.apply_pin(self);
29 self.widget_ptr.set(cpp! { unsafe [animation_tracker_property_ptr as "void*"] -> SlintTypeErasedWidgetPtr as "std::unique_ptr<SlintTypeErasedWidget>" {
30 return make_unique_animated_widget<QCheckBox>(animation_tracker_property_ptr);
31 }})
32 }
33
34 fn layout_info(
35 self: Pin<&Self>,
36 orientation: Orientation,
37 _window_adapter: &Rc<dyn WindowAdapter>,
38 _self_rc: &ItemRc,
39 ) -> LayoutInfo {
40 let text: qttypes::QString = self.text().as_str().into();
41 let widget: NonNull<()> = SlintTypeErasedWidgetPtr::qwidget_ptr(&self.widget_ptr);
42 let size = cpp!(unsafe [
43 text as "QString",
44 widget as "QWidget*"
45 ] -> qttypes::QSize as "QSize" {
46 ensure_initialized();
47 QStyleOptionButton option;
48 option.rect = option.fontMetrics.boundingRect(text);
49 option.text = std::move(text);
50 return qApp->style()->sizeFromContents(QStyle::CT_CheckBox, &option, option.rect.size(), widget);
51 });
52 match orientation {
53 Orientation::Horizontal => LayoutInfo {
54 min: size.width as f32,
55 preferred: size.width as f32,
56 stretch: 1.,
57 ..LayoutInfo::default()
58 },
59 Orientation::Vertical => LayoutInfo {
60 min: size.height as f32,
61 preferred: size.height as f32,
62 max: size.height as f32,
63 ..LayoutInfo::default()
64 },
65 }
66 }
67
68 fn input_event_filter_before_children(
69 self: Pin<&Self>,
70 event: &MouseEvent,
71 _window_adapter: &Rc<dyn WindowAdapter>,
72 _self_rc: &ItemRc,
73 ) -> InputEventFilterResult {
74 Self::FIELD_OFFSETS.has_hover.apply_pin(self).set(!matches!(event, MouseEvent::Exit));
75 InputEventFilterResult::ForwardEvent
76 }
77
78 fn input_event(
79 self: Pin<&Self>,
80 event: &MouseEvent,
81 _window_adapter: &Rc<dyn WindowAdapter>,
82 self_rc: &i_slint_core::items::ItemRc,
83 ) -> InputEventResult {
84 if matches!(event, MouseEvent::Exit) {
85 Self::FIELD_OFFSETS.has_hover.apply_pin(self).set(false);
86 }
87 if !self.enabled() {
88 return InputEventResult::EventIgnored;
89 }
90 if let MouseEvent::Released { position, button, .. } = event {
91 let geo = self_rc.geometry();
92 if *button == PointerEventButton::Left
93 && LogicalRect::new(LogicalPoint::default(), geo.size).contains(*position)
94 {
95 Self::FIELD_OFFSETS.checked.apply_pin(self).set(!self.checked());
96 Self::FIELD_OFFSETS.toggled.apply_pin(self).call(&())
97 }
98 }
99 InputEventResult::EventAccepted
100 }
101
102 fn capture_key_event(
103 self: Pin<&Self>,
104 _event: &KeyEvent,
105 _window_adapter: &Rc<dyn WindowAdapter>,
106 _self_rc: &ItemRc,
107 ) -> KeyEventResult {
108 KeyEventResult::EventIgnored
109 }
110
111 fn key_event(
112 self: Pin<&Self>,
113 event: &KeyEvent,
114 _window_adapter: &Rc<dyn WindowAdapter>,
115 _self_rc: &ItemRc,
116 ) -> KeyEventResult {
117 match event.event_type {
118 KeyEventType::KeyPressed if event.text == " " || event.text == "\n" => {
119 Self::FIELD_OFFSETS.checked.apply_pin(self).set(!self.checked());
120 Self::FIELD_OFFSETS.toggled.apply_pin(self).call(&());
121 KeyEventResult::EventAccepted
122 }
123 KeyEventType::KeyPressed => KeyEventResult::EventIgnored,
124 KeyEventType::KeyReleased => KeyEventResult::EventIgnored,
125 KeyEventType::UpdateComposition | KeyEventType::CommitComposition => {
126 KeyEventResult::EventIgnored
127 }
128 }
129 }
130
131 fn focus_event(
132 self: Pin<&Self>,
133 event: &FocusEvent,
134 _window_adapter: &Rc<dyn WindowAdapter>,
135 _self_rc: &ItemRc,
136 ) -> FocusEventResult {
137 if self.enabled() {
138 Self::FIELD_OFFSETS
139 .has_focus
140 .apply_pin(self)
141 .set(matches!(event, FocusEvent::FocusIn(_)));
142 FocusEventResult::FocusAccepted
143 } else {
144 FocusEventResult::FocusIgnored
145 }
146 }
147
148 fn_render! { this dpr size painter widget initial_state =>
149 let checked: bool = this.checked();
150 let enabled = this.enabled();
151 let has_focus = this.has_focus();
152 let has_hover = this.has_hover();
153 let text: qttypes::QString = this.text().as_str().into();
154
155 cpp!(unsafe [
156 painter as "QPainterPtr*",
157 widget as "QWidget*",
158 enabled as "bool",
159 text as "QString",
160 size as "QSize",
161 checked as "bool",
162 has_focus as "bool",
163 has_hover as "bool",
164 dpr as "float",
165 initial_state as "int"
166 ] {
167 QStyleOptionButton option;
168 option.styleObject = widget;
169 option.state |= QStyle::State(initial_state);
170 option.text = std::move(text);
171 option.rect = QRect(QPoint(), size / dpr);
172 option.state |= checked ? QStyle::State_On : QStyle::State_Off;
173 if (enabled) {
174 option.state |= QStyle::State_Enabled;
175 } else {
176 option.palette.setCurrentColorGroup(QPalette::Disabled);
177 }
178 if (has_focus) {
179 option.state |= QStyle::State_HasFocus | QStyle::State_KeyboardFocusChange | QStyle::State_Item;
180 }
181 if (has_hover) {
182 option.state |= QStyle::State_MouseOver;
183 }
184 qApp->style()->drawControl(QStyle::CE_CheckBox, &option, painter->get(), widget);
185 });
186 }
187
188 fn bounding_rect(
189 self: core::pin::Pin<&Self>,
190 _window_adapter: &Rc<dyn WindowAdapter>,
191 _self_rc: &ItemRc,
192 geometry: LogicalRect,
193 ) -> LogicalRect {
194 geometry
195 }
196
197 fn clips_children(self: core::pin::Pin<&Self>) -> bool {
198 false
199 }
200}
201
202impl ItemConsts for NativeCheckBox {
203 const cached_rendering_data_offset: const_field_offset::FieldOffset<Self, CachedRenderingData> =
204 Self::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection();
205}
206
207declare_item_vtable! {
208 fn slint_get_NativeCheckBoxVTable() -> NativeCheckBoxVTable for NativeCheckBox
209}