i_slint_backend_qt/qt_widgets/
groupbox.rs1use i_slint_core::input::FocusEventResult;
5
6use super::*;
7
8#[repr(C)]
9#[derive(FieldOffsets, Default, SlintElement)]
10#[pin]
11pub struct NativeGroupBox {
12 pub enabled: Property<bool>,
13 pub title: Property<SharedString>,
14 pub cached_rendering_data: CachedRenderingData,
15 pub native_padding_left: Property<LogicalLength>,
16 pub native_padding_right: Property<LogicalLength>,
17 pub native_padding_top: Property<LogicalLength>,
18 pub native_padding_bottom: Property<LogicalLength>,
19 widget_ptr: std::cell::Cell<SlintTypeErasedWidgetPtr>,
20 animation_tracker: Property<i32>,
21}
22
23#[repr(C)]
24#[derive(FieldOffsets, Default)]
25#[pin]
26struct GroupBoxData {
27 title: Property<SharedString>,
28 paddings: Property<qttypes::QMargins>,
29}
30
31cpp! {{
32 QStyleOptionGroupBox create_group_box_option(QString title) {
33 QStyleOptionGroupBox option;
34 option.text = title;
35 option.lineWidth = 1;
36 option.midLineWidth = 0;
37 option.subControls = QStyle::SC_GroupBoxFrame;
38 if (!title.isEmpty()) {
39 option.subControls |= QStyle::SC_GroupBoxLabel;
40 }
41 option.textColor = QColor(qApp->style()->styleHint(
42 QStyle::SH_GroupBox_TextLabelColor, &option));
43
44 return option;
45 }
46}}
47
48fn minimum_group_box_size(title: qttypes::QString) -> qttypes::QSize {
49 cpp!(unsafe [title as "QString"] -> qttypes::QSize as "QSize" {
50 ensure_initialized();
51
52 QStyleOptionGroupBox option = create_group_box_option(title);
53
54 QFontMetrics metrics = option.fontMetrics;
55 int baseWidth = metrics.horizontalAdvance(title) + metrics.horizontalAdvance(QLatin1Char(' '));
56 int baseHeight = metrics.height();
57
58 return qApp->style()->sizeFromContents(QStyle::CT_GroupBox, &option, QSize(baseWidth, baseHeight), nullptr);
59 })
60}
61
62impl Item for NativeGroupBox {
63 fn init(self: Pin<&Self>, _self_rc: &ItemRc) {
64 let animation_tracker_property_ptr = Self::FIELD_OFFSETS.animation_tracker.apply_pin(self);
65 self.widget_ptr.set(cpp! { unsafe [animation_tracker_property_ptr as "void*"] -> SlintTypeErasedWidgetPtr as "std::unique_ptr<SlintTypeErasedWidget>" {
66 return make_unique_animated_widget<QGroupBox>(animation_tracker_property_ptr);
67 }});
68
69 let shared_data = Rc::pin(GroupBoxData::default());
70
71 Property::link_two_way(
72 Self::FIELD_OFFSETS.title.apply_pin(self),
73 GroupBoxData::FIELD_OFFSETS.title.apply_pin(shared_data.as_ref()),
74 );
75
76 shared_data.paddings.set_binding({
77 let shared_data_weak = pin_weak::rc::PinWeak::downgrade(shared_data.clone());
78 move || {
79 let shared_data = shared_data_weak.upgrade().unwrap();
80
81 let text: qttypes::QString = GroupBoxData::FIELD_OFFSETS.title.apply_pin(shared_data.as_ref()).get().as_str().into();
82
83 cpp!(unsafe [
84 text as "QString"
85 ] -> qttypes::QMargins as "QMargins" {
86 ensure_initialized();
87 QStyleOptionGroupBox option = create_group_box_option(text);
88
89 option.rect = QRect(0, 0, 10000, 10000);
91 QRect contentsRect = qApp->style()->subControlRect(
92 QStyle::CC_GroupBox, &option, QStyle::SC_GroupBoxContents);
93 auto hs = qApp->style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing, &option);
97 auto vs = qApp->style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing, &option);
98
99 return {
100 (contentsRect.left() + hs),
101 (contentsRect.top() + vs),
102 (option.rect.right() - contentsRect.right() + hs),
103 (option.rect.bottom() - contentsRect.bottom() + vs)
104 };
105 })
106 }
107 });
108
109 self.native_padding_left.set_binding({
110 let shared_data = shared_data.clone();
111 move || {
112 let margins =
113 GroupBoxData::FIELD_OFFSETS.paddings.apply_pin(shared_data.as_ref()).get();
114 LogicalLength::new(margins.left as _)
115 }
116 });
117
118 self.native_padding_right.set_binding({
119 let shared_data = shared_data.clone();
120 move || {
121 let margins =
122 GroupBoxData::FIELD_OFFSETS.paddings.apply_pin(shared_data.as_ref()).get();
123 LogicalLength::new(margins.right as _)
124 }
125 });
126
127 self.native_padding_top.set_binding({
128 let shared_data = shared_data.clone();
129 move || {
130 let margins =
131 GroupBoxData::FIELD_OFFSETS.paddings.apply_pin(shared_data.as_ref()).get();
132 LogicalLength::new(margins.top as _)
133 }
134 });
135
136 self.native_padding_bottom.set_binding({
137 move || {
138 let margins =
139 GroupBoxData::FIELD_OFFSETS.paddings.apply_pin(shared_data.as_ref()).get();
140 LogicalLength::new(margins.bottom as _)
141 }
142 });
143 }
144
145 fn layout_info(
146 self: Pin<&Self>,
147 orientation: Orientation,
148 _window_adapter: &Rc<dyn WindowAdapter>,
149 _self_rc: &ItemRc,
150 ) -> LayoutInfo {
151 let text: qttypes::QString = self.title().as_str().into();
152
153 let size = minimum_group_box_size(text);
154
155 let min = match orientation {
156 Orientation::Horizontal => size.width as f32,
157 Orientation::Vertical => size.height as f32,
158 };
159 LayoutInfo { min, preferred: min, stretch: 1., ..LayoutInfo::default() }
160 }
161
162 fn input_event_filter_before_children(
163 self: Pin<&Self>,
164 _: &MouseEvent,
165 _window_adapter: &Rc<dyn WindowAdapter>,
166 _self_rc: &ItemRc,
167 ) -> InputEventFilterResult {
168 InputEventFilterResult::ForwardEvent
169 }
170
171 fn input_event(
172 self: Pin<&Self>,
173 _: &MouseEvent,
174 _window_adapter: &Rc<dyn WindowAdapter>,
175 _self_rc: &i_slint_core::items::ItemRc,
176 ) -> InputEventResult {
177 InputEventResult::EventIgnored
178 }
179
180 fn capture_key_event(
181 self: Pin<&Self>,
182 _event: &KeyEvent,
183 _window_adapter: &Rc<dyn WindowAdapter>,
184 _self_rc: &ItemRc,
185 ) -> KeyEventResult {
186 KeyEventResult::EventIgnored
187 }
188
189 fn key_event(
190 self: Pin<&Self>,
191 _: &KeyEvent,
192 _window_adapter: &Rc<dyn WindowAdapter>,
193 _self_rc: &ItemRc,
194 ) -> KeyEventResult {
195 KeyEventResult::EventIgnored
196 }
197
198 fn focus_event(
199 self: Pin<&Self>,
200 _: &FocusEvent,
201 _window_adapter: &Rc<dyn WindowAdapter>,
202 _self_rc: &ItemRc,
203 ) -> FocusEventResult {
204 FocusEventResult::FocusIgnored
205 }
206
207 fn_render! { this dpr size painter widget initial_state =>
208 let text: qttypes::QString =
209 this.title().as_str().into();
210 let enabled = this.enabled();
211
212 cpp!(unsafe [
213 painter as "QPainterPtr*",
214 widget as "QWidget*",
215 text as "QString",
216 enabled as "bool",
217 size as "QSize",
218 dpr as "float",
219 initial_state as "int"
220 ] {
221 if (auto groupbox = qobject_cast<QGroupBox *>(widget)) {
222 groupbox->setTitle(text);
225 }
226 QStyleOptionGroupBox option;
227 option.styleObject = widget;
228 option.state |= QStyle::State(initial_state);
229 if (enabled) {
230 option.state |= QStyle::State_Enabled;
231 } else {
232 option.palette.setCurrentColorGroup(QPalette::Disabled);
233 }
234 option.rect = QRect(QPoint(), size / dpr);
235 option.text = text;
236 option.lineWidth = 1;
237 option.midLineWidth = 0;
238 option.subControls = QStyle::SC_GroupBoxFrame;
239 if (!text.isEmpty()) {
240 option.subControls |= QStyle::SC_GroupBoxLabel;
241 }
242 option.textColor = QColor(qApp->style()->styleHint(
243 QStyle::SH_GroupBox_TextLabelColor, &option));
244 qApp->style()->drawComplexControl(QStyle::CC_GroupBox, &option, painter->get(), widget);
245 });
246 }
247
248 fn bounding_rect(
249 self: core::pin::Pin<&Self>,
250 _window_adapter: &Rc<dyn WindowAdapter>,
251 _self_rc: &ItemRc,
252 geometry: LogicalRect,
253 ) -> LogicalRect {
254 geometry
255 }
256
257 fn clips_children(self: core::pin::Pin<&Self>) -> bool {
258 false
259 }
260}
261
262impl ItemConsts for NativeGroupBox {
263 const cached_rendering_data_offset: const_field_offset::FieldOffset<Self, CachedRenderingData> =
264 Self::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection();
265}
266
267declare_item_vtable! {
268fn slint_get_NativeGroupBoxVTable() -> NativeGroupBoxVTable for NativeGroupBox
269}