mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-19 14:57:22 +00:00

- Take in account the scale factor (unfortunately we don't have access to the actual window scale factor so just use the Qt one) - Draw directly in the final SharedPixelBuffer - Use an intermediate Rectangle to center the image because otherwise we get height for width dependency that breaks layout.
229 lines
7.6 KiB
Rust
229 lines
7.6 KiB
Rust
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
|
// 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<LogicalLength>,
|
|
pub native_padding_right: Property<LogicalLength>,
|
|
pub native_padding_top: Property<LogicalLength>,
|
|
pub native_padding_bottom: Property<LogicalLength>,
|
|
pub has_focus: Property<bool>,
|
|
pub enabled: Property<bool>,
|
|
pub input_type: Property<InputType>,
|
|
pub clear_icon: Property<Image>,
|
|
widget_ptr: std::cell::Cell<SlintTypeErasedWidgetPtr>,
|
|
animation_tracker: Property<i32>,
|
|
}
|
|
|
|
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::<Rgba8Pixel>::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<SlintTypeErasedWidget>" {
|
|
return make_unique_animated_widget<QLineEdit>(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<dyn WindowAdapter>,
|
|
_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<dyn WindowAdapter>,
|
|
_self_rc: &ItemRc,
|
|
) -> InputEventFilterResult {
|
|
InputEventFilterResult::ForwardAndIgnore
|
|
}
|
|
|
|
fn input_event(
|
|
self: Pin<&Self>,
|
|
_: &MouseEvent,
|
|
_window_adapter: &Rc<dyn WindowAdapter>,
|
|
_self_rc: &i_slint_core::items::ItemRc,
|
|
) -> InputEventResult {
|
|
InputEventResult::EventIgnored
|
|
}
|
|
|
|
fn capture_key_event(
|
|
self: Pin<&Self>,
|
|
_event: &KeyEvent,
|
|
_window_adapter: &Rc<dyn WindowAdapter>,
|
|
_self_rc: &ItemRc,
|
|
) -> KeyEventResult {
|
|
KeyEventResult::EventIgnored
|
|
}
|
|
|
|
fn key_event(
|
|
self: Pin<&Self>,
|
|
_: &KeyEvent,
|
|
_window_adapter: &Rc<dyn WindowAdapter>,
|
|
_self_rc: &ItemRc,
|
|
) -> KeyEventResult {
|
|
KeyEventResult::EventIgnored
|
|
}
|
|
|
|
fn focus_event(
|
|
self: Pin<&Self>,
|
|
_: &FocusEvent,
|
|
_window_adapter: &Rc<dyn WindowAdapter>,
|
|
_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<dyn WindowAdapter>,
|
|
_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, CachedRenderingData> =
|
|
Self::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection();
|
|
}
|
|
|
|
declare_item_vtable! {
|
|
fn slint_get_NativeLineEditVTable() -> NativeLineEditVTable for NativeLineEdit
|
|
}
|