i_slint_backend_qt/qt_widgets/
lineedit.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
4use i_slint_core::graphics::{Image, Rgba8Pixel, SharedPixelBuffer};
5use i_slint_core::input::FocusEventResult;
6use i_slint_core::items::InputType;
7
8use super::*;
9
10#[repr(C)]
11#[derive(FieldOffsets, Default, SlintElement)]
12#[pin]
13pub struct NativeLineEdit {
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    pub has_focus: Property<bool>,
20    pub enabled: Property<bool>,
21    pub input_type: Property<InputType>,
22    pub clear_icon: Property<Image>,
23    widget_ptr: std::cell::Cell<SlintTypeErasedWidgetPtr>,
24    animation_tracker: Property<i32>,
25}
26
27fn get_clear_icon() -> Image {
28    let dpr = cpp!(unsafe [] -> f32 as "float" {
29        return qApp->devicePixelRatio();
30    });
31
32    let size = cpp!(unsafe [] -> u32 as "uint" {
33        #if QT_VERSION < QT_VERSION_CHECK(6, 2, 0)
34        return qApp->style()->pixelMetric(QStyle::PM_SmallIconSize, nullptr, nullptr);
35        #else
36        return qApp->style()->pixelMetric(QStyle::PM_LineEditIconSize, nullptr, nullptr);
37        #endif
38    });
39
40    let width = (size as f32 * dpr).ceil() as u32;
41    let height = width;
42
43    let mut pixel_buffer = SharedPixelBuffer::<Rgba8Pixel>::new(width, height);
44    let ptr = pixel_buffer.make_mut_bytes().as_mut_ptr();
45
46    cpp!(unsafe [
47        width as "uint32_t",
48        height as "uint32_t",
49        ptr as "uint8_t*"
50    ] {
51        QStyleOptionFrame option;
52        const QIcon icon = qApp->style()->standardIcon(QStyle::SP_LineEditClearButton, &option);
53        QImage image(ptr, width, height, QImage::Format_RGBA8888);
54        image.setDevicePixelRatio(1.0);
55        QPainter painter(&image);
56        icon.paint(&painter, 0, 0, width, height, Qt::AlignCenter);
57    });
58
59    Image::from_rgba8(pixel_buffer)
60}
61
62impl Item for NativeLineEdit {
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<QLineEdit>(animation_tracker_property_ptr);
67        }});
68
69        let paddings = Rc::pin(Property::default());
70
71        paddings.as_ref().set_binding(move || {
72            cpp!(unsafe [] -> qttypes::QMargins as "QMargins" {
73                ensure_initialized();
74                QStyleOptionFrame option;
75                option.state |= QStyle::State_Enabled;
76                option.lineWidth = 1;
77                option.midLineWidth = 0;
78                // Just some size big enough to be sure that the frame fits in it
79                option.rect = QRect(0, 0, 10000, 10000);
80                QRect contentsRect = qApp->style()->subElementRect(
81                    QStyle::SE_LineEditContents, &option);
82
83                // ### remove extra margins
84
85                return {
86                    (2 + contentsRect.left()),
87                    (4 + contentsRect.top()),
88                    (2 + option.rect.right() - contentsRect.right()),
89                    (4 + option.rect.bottom() - contentsRect.bottom())
90                };
91            })
92        });
93
94        self.native_padding_left.set_binding({
95            let paddings = paddings.clone();
96            move || LogicalLength::new(paddings.as_ref().get().left as _)
97        });
98        self.native_padding_right.set_binding({
99            let paddings = paddings.clone();
100            move || LogicalLength::new(paddings.as_ref().get().right as _)
101        });
102        self.native_padding_top.set_binding({
103            let paddings = paddings.clone();
104            move || LogicalLength::new(paddings.as_ref().get().top as _)
105        });
106        self.native_padding_bottom.set_binding({
107            let paddings = paddings;
108            move || LogicalLength::new(paddings.as_ref().get().bottom as _)
109        });
110
111        self.clear_icon.set(get_clear_icon());
112    }
113
114    fn layout_info(
115        self: Pin<&Self>,
116        orientation: Orientation,
117        _window_adapter: &Rc<dyn WindowAdapter>,
118        _self_rc: &ItemRc,
119    ) -> LayoutInfo {
120        let min = match orientation {
121            Orientation::Horizontal => self.native_padding_left() + self.native_padding_right(),
122            Orientation::Vertical => self.native_padding_top() + self.native_padding_bottom(),
123        }
124        .get();
125        LayoutInfo {
126            min,
127            preferred: min,
128            stretch: if orientation == Orientation::Horizontal { 1. } else { 0. },
129            ..LayoutInfo::default()
130        }
131    }
132
133    fn input_event_filter_before_children(
134        self: Pin<&Self>,
135        _: &MouseEvent,
136        _window_adapter: &Rc<dyn WindowAdapter>,
137        _self_rc: &ItemRc,
138    ) -> InputEventFilterResult {
139        InputEventFilterResult::ForwardAndIgnore
140    }
141
142    fn input_event(
143        self: Pin<&Self>,
144        _: &MouseEvent,
145        _window_adapter: &Rc<dyn WindowAdapter>,
146        _self_rc: &i_slint_core::items::ItemRc,
147    ) -> InputEventResult {
148        InputEventResult::EventIgnored
149    }
150
151    fn capture_key_event(
152        self: Pin<&Self>,
153        _event: &KeyEvent,
154        _window_adapter: &Rc<dyn WindowAdapter>,
155        _self_rc: &ItemRc,
156    ) -> KeyEventResult {
157        KeyEventResult::EventIgnored
158    }
159
160    fn key_event(
161        self: Pin<&Self>,
162        _: &KeyEvent,
163        _window_adapter: &Rc<dyn WindowAdapter>,
164        _self_rc: &ItemRc,
165    ) -> KeyEventResult {
166        KeyEventResult::EventIgnored
167    }
168
169    fn focus_event(
170        self: Pin<&Self>,
171        _: &FocusEvent,
172        _window_adapter: &Rc<dyn WindowAdapter>,
173        _self_rc: &ItemRc,
174    ) -> FocusEventResult {
175        FocusEventResult::FocusIgnored
176    }
177
178    fn_render! { this dpr size painter widget initial_state =>
179        let has_focus: bool = this.has_focus();
180        let enabled: bool = this.enabled();
181
182        cpp!(unsafe [
183            painter as "QPainterPtr*",
184            widget as "QWidget*",
185            size as "QSize",
186            dpr as "float",
187            enabled as "bool",
188            has_focus as "bool",
189            initial_state as "int"
190        ] {
191            QStyleOptionFrame option;
192            option.styleObject = widget;
193            option.state |= QStyle::State(initial_state);
194            option.rect = QRect(QPoint(), size / dpr);
195            option.lineWidth = 1;
196            option.midLineWidth = 0;
197            if (enabled) {
198                option.state |= QStyle::State_Enabled;
199                if (has_focus)
200                    option.state |= QStyle::State_HasFocus;
201            } else {
202                option.palette.setCurrentColorGroup(QPalette::Disabled);
203            }
204            qApp->style()->drawPrimitive(QStyle::PE_PanelLineEdit, &option, painter->get(), widget);
205        });
206    }
207
208    fn bounding_rect(
209        self: core::pin::Pin<&Self>,
210        _window_adapter: &Rc<dyn WindowAdapter>,
211        _self_rc: &ItemRc,
212        geometry: LogicalRect,
213    ) -> LogicalRect {
214        geometry
215    }
216
217    fn clips_children(self: core::pin::Pin<&Self>) -> bool {
218        false
219    }
220}
221
222impl ItemConsts for NativeLineEdit {
223    const cached_rendering_data_offset: const_field_offset::FieldOffset<Self, CachedRenderingData> =
224        Self::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection();
225}
226
227declare_item_vtable! {
228fn slint_get_NativeLineEditVTable() -> NativeLineEditVTable for NativeLineEdit
229}