// Copyright © SixtyFPS GmbH // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 use i_slint_core::graphics::{Image, Rgba8Pixel, SharedPixelBuffer}; use i_slint_core::input::FocusEventResult; use i_slint_core::items::InputType; use super::*; #[repr(C)] #[derive(FieldOffsets, Default, SlintElement)] #[pin] pub struct NativeLineEdit { pub cached_rendering_data: CachedRenderingData, pub native_padding_left: Property, pub native_padding_right: Property, pub native_padding_top: Property, pub native_padding_bottom: Property, pub has_focus: Property, pub enabled: Property, pub input_type: Property, pub clear_icon: Property, widget_ptr: std::cell::Cell, animation_tracker: Property, } fn get_clear_icon() -> Image { let dpr = cpp!(unsafe [] -> f32 as "float" { return qApp->devicePixelRatio(); }); let size = cpp!(unsafe [] -> u32 as "uint" { #if QT_VERSION < QT_VERSION_CHECK(6, 2, 0) return qApp->style()->pixelMetric(QStyle::PM_SmallIconSize, nullptr, nullptr); #else return qApp->style()->pixelMetric(QStyle::PM_LineEditIconSize, nullptr, nullptr); #endif }); let width = (size as f32 * dpr).ceil() as u32; let height = width; let mut pixel_buffer = SharedPixelBuffer::::new(width, height); let ptr = pixel_buffer.make_mut_bytes().as_mut_ptr(); cpp!(unsafe [ width as "uint32_t", height as "uint32_t", ptr as "uint8_t*" ] { QStyleOptionFrame option; const QIcon icon = qApp->style()->standardIcon(QStyle::SP_LineEditClearButton, &option); QImage image(ptr, width, height, QImage::Format_RGBA8888); image.setDevicePixelRatio(1.0); QPainter painter(&image); icon.paint(&painter, 0, 0, width, height, Qt::AlignCenter); }); Image::from_rgba8(pixel_buffer) } impl Item for NativeLineEdit { fn init(self: Pin<&Self>, _self_rc: &ItemRc) { let animation_tracker_property_ptr = Self::FIELD_OFFSETS.animation_tracker.apply_pin(self); self.widget_ptr.set(cpp! { unsafe [animation_tracker_property_ptr as "void*"] -> SlintTypeErasedWidgetPtr as "std::unique_ptr" { return make_unique_animated_widget(animation_tracker_property_ptr); }}); let paddings = Rc::pin(Property::default()); paddings.as_ref().set_binding(move || { cpp!(unsafe [] -> qttypes::QMargins as "QMargins" { ensure_initialized(); QStyleOptionFrame option; option.state |= QStyle::State_Enabled; option.lineWidth = 1; option.midLineWidth = 0; // Just some size big enough to be sure that the frame fits in it option.rect = QRect(0, 0, 10000, 10000); QRect contentsRect = qApp->style()->subElementRect( QStyle::SE_LineEditContents, &option); // ### remove extra margins return { (2 + contentsRect.left()), (4 + contentsRect.top()), (2 + option.rect.right() - contentsRect.right()), (4 + option.rect.bottom() - contentsRect.bottom()) }; }) }); self.native_padding_left.set_binding({ let paddings = paddings.clone(); move || LogicalLength::new(paddings.as_ref().get().left as _) }); self.native_padding_right.set_binding({ let paddings = paddings.clone(); move || LogicalLength::new(paddings.as_ref().get().right as _) }); self.native_padding_top.set_binding({ let paddings = paddings.clone(); move || LogicalLength::new(paddings.as_ref().get().top as _) }); self.native_padding_bottom.set_binding({ let paddings = paddings; move || LogicalLength::new(paddings.as_ref().get().bottom as _) }); self.clear_icon.set(get_clear_icon()); } fn layout_info( self: Pin<&Self>, orientation: Orientation, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> LayoutInfo { let min = match orientation { Orientation::Horizontal => self.native_padding_left() + self.native_padding_right(), Orientation::Vertical => self.native_padding_top() + self.native_padding_bottom(), } .get(); LayoutInfo { min, preferred: min, stretch: if orientation == Orientation::Horizontal { 1. } else { 0. }, ..LayoutInfo::default() } } fn input_event_filter_before_children( self: Pin<&Self>, _: &MouseEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> InputEventFilterResult { InputEventFilterResult::ForwardAndIgnore } fn input_event( self: Pin<&Self>, _: &MouseEvent, _window_adapter: &Rc, _self_rc: &i_slint_core::items::ItemRc, ) -> InputEventResult { InputEventResult::EventIgnored } fn capture_key_event( self: Pin<&Self>, _event: &KeyEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> KeyEventResult { KeyEventResult::EventIgnored } fn key_event( self: Pin<&Self>, _: &KeyEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> KeyEventResult { KeyEventResult::EventIgnored } fn focus_event( self: Pin<&Self>, _: &FocusEvent, _window_adapter: &Rc, _self_rc: &ItemRc, ) -> FocusEventResult { FocusEventResult::FocusIgnored } fn_render! { this dpr size painter widget initial_state => let has_focus: bool = this.has_focus(); let enabled: bool = this.enabled(); cpp!(unsafe [ painter as "QPainterPtr*", widget as "QWidget*", size as "QSize", dpr as "float", enabled as "bool", has_focus as "bool", initial_state as "int" ] { QStyleOptionFrame option; option.styleObject = widget; option.state |= QStyle::State(initial_state); option.rect = QRect(QPoint(), size / dpr); option.lineWidth = 1; option.midLineWidth = 0; if (enabled) { option.state |= QStyle::State_Enabled; if (has_focus) option.state |= QStyle::State_HasFocus; } else { option.palette.setCurrentColorGroup(QPalette::Disabled); } qApp->style()->drawPrimitive(QStyle::PE_PanelLineEdit, &option, painter->get(), widget); }); } fn bounding_rect( self: core::pin::Pin<&Self>, _window_adapter: &Rc, _self_rc: &ItemRc, geometry: LogicalRect, ) -> LogicalRect { geometry } fn clips_children(self: core::pin::Pin<&Self>) -> bool { false } } impl ItemConsts for NativeLineEdit { const cached_rendering_data_offset: const_field_offset::FieldOffset = Self::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection(); } declare_item_vtable! { fn slint_get_NativeLineEditVTable() -> NativeLineEditVTable for NativeLineEdit }