mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-01 14:21:16 +00:00

This removes the special code for the generated property getters and ensures type safety in the run-time library for property value setting. In the Rust generated code we continue to do arithmetic on the scalar values, that means we immediately extract the scalar, do arithmetic and rely on the compiler to only allow compatible units. Danger zone alert: In the interpreter Value::Number can now be converted to LogicalLength as-is.
273 lines
9 KiB
Rust
273 lines
9 KiB
Rust
// Copyright © SixtyFPS GmbH <info@slint-ui.com>
|
|
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
|
|
|
|
use i_slint_core::input::FocusEventResult;
|
|
|
|
use super::*;
|
|
|
|
#[repr(C)]
|
|
#[derive(FieldOffsets, Default, SlintElement)]
|
|
#[pin]
|
|
pub struct NativeComboBox {
|
|
pub x: Property<LogicalLength>,
|
|
pub y: Property<LogicalLength>,
|
|
pub width: Property<LogicalLength>,
|
|
pub height: Property<LogicalLength>,
|
|
pub enabled: Property<bool>,
|
|
pub pressed: Property<bool>,
|
|
pub is_open: Property<bool>,
|
|
pub current_value: Property<SharedString>,
|
|
pub cached_rendering_data: CachedRenderingData,
|
|
pub open_popup: Callback<VoidArg>,
|
|
}
|
|
|
|
impl Item for NativeComboBox {
|
|
fn init(self: Pin<&Self>, _window_adapter: &Rc<dyn WindowAdapter>) {}
|
|
|
|
fn geometry(self: Pin<&Self>) -> LogicalRect {
|
|
LogicalRect::new(
|
|
LogicalPoint::from_lengths(self.x(), self.y()),
|
|
LogicalSize::from_lengths(self.width(), self.height()),
|
|
)
|
|
}
|
|
|
|
fn layout_info(
|
|
self: Pin<&Self>,
|
|
orientation: Orientation,
|
|
_window_adapter: &Rc<dyn WindowAdapter>,
|
|
) -> LayoutInfo {
|
|
let size = cpp!(unsafe [] -> qttypes::QSize as "QSize" {
|
|
ensure_initialized();
|
|
QStyleOptionComboBox option;
|
|
// FIXME
|
|
option.rect = option.fontMetrics.boundingRect("*************");
|
|
option.subControls = QStyle::SC_All;
|
|
return qApp->style()->sizeFromContents(QStyle::CT_ComboBox, &option, option.rect.size(), nullptr);
|
|
});
|
|
LayoutInfo {
|
|
min: match orientation {
|
|
Orientation::Horizontal => size.width,
|
|
Orientation::Vertical => size.height,
|
|
} as f32,
|
|
..LayoutInfo::default()
|
|
}
|
|
}
|
|
|
|
fn input_event_filter_before_children(
|
|
self: Pin<&Self>,
|
|
_: MouseEvent,
|
|
_window_adapter: &Rc<dyn WindowAdapter>,
|
|
_self_rc: &ItemRc,
|
|
) -> InputEventFilterResult {
|
|
InputEventFilterResult::ForwardEvent
|
|
}
|
|
|
|
fn input_event(
|
|
self: Pin<&Self>,
|
|
event: MouseEvent,
|
|
_window_adapter: &Rc<dyn WindowAdapter>,
|
|
_self_rc: &i_slint_core::items::ItemRc,
|
|
) -> InputEventResult {
|
|
let enabled = self.enabled();
|
|
if !enabled {
|
|
return InputEventResult::EventIgnored;
|
|
}
|
|
// FIXME: this is the input event of a button, but we need to do the proper hit test
|
|
|
|
Self::FIELD_OFFSETS.pressed.apply_pin(self).set(match event {
|
|
MouseEvent::Pressed { .. } => true,
|
|
MouseEvent::Exit | MouseEvent::Released { .. } => false,
|
|
MouseEvent::Moved { .. } => {
|
|
return if self.pressed() {
|
|
InputEventResult::GrabMouse
|
|
} else {
|
|
InputEventResult::EventIgnored
|
|
}
|
|
}
|
|
MouseEvent::Wheel { .. } => return InputEventResult::EventIgnored,
|
|
});
|
|
if matches!(event, MouseEvent::Released { .. }) {
|
|
Self::FIELD_OFFSETS.is_open.apply_pin(self).set(true);
|
|
Self::FIELD_OFFSETS.open_popup.apply_pin(self).call(&());
|
|
InputEventResult::EventAccepted
|
|
} else {
|
|
InputEventResult::GrabMouse
|
|
}
|
|
}
|
|
|
|
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 down: bool = this.pressed();
|
|
let is_open: bool = this.is_open();
|
|
let text: qttypes::QString =
|
|
this.current_value().as_str().into();
|
|
let enabled = this.enabled();
|
|
cpp!(unsafe [
|
|
painter as "QPainterPtr*",
|
|
widget as "QWidget*",
|
|
text as "QString",
|
|
enabled as "bool",
|
|
size as "QSize",
|
|
down as "bool",
|
|
is_open as "bool",
|
|
dpr as "float",
|
|
initial_state as "int"
|
|
] {
|
|
ensure_initialized();
|
|
QStyleOptionComboBox option;
|
|
option.state |= QStyle::State(initial_state);
|
|
option.currentText = std::move(text);
|
|
option.rect = QRect(QPoint(), size / dpr);
|
|
if (down)
|
|
option.state |= QStyle::State_Sunken;
|
|
else
|
|
option.state |= QStyle::State_Raised;
|
|
if (enabled) {
|
|
option.state |= QStyle::State_Enabled;
|
|
} else {
|
|
option.palette.setCurrentColorGroup(QPalette::Disabled);
|
|
}
|
|
if (is_open) {
|
|
// FIXME: This is commented out to workaround #456
|
|
// option.state |= QStyle::State_On;
|
|
}
|
|
option.subControls = QStyle::SC_All;
|
|
qApp->style()->drawComplexControl(QStyle::CC_ComboBox, &option, painter->get(), widget);
|
|
qApp->style()->drawControl(QStyle::CE_ComboBoxLabel, &option, painter->get(), widget);
|
|
});
|
|
}
|
|
}
|
|
|
|
impl ItemConsts for NativeComboBox {
|
|
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_NativeComboBoxVTable() -> NativeComboBoxVTable for NativeComboBox
|
|
}
|
|
|
|
#[repr(C)]
|
|
#[derive(FieldOffsets, Default, SlintElement)]
|
|
#[pin]
|
|
pub struct NativeComboBoxPopup {
|
|
pub x: Property<LogicalLength>,
|
|
pub y: Property<LogicalLength>,
|
|
pub width: Property<LogicalLength>,
|
|
pub height: Property<LogicalLength>,
|
|
pub cached_rendering_data: CachedRenderingData,
|
|
}
|
|
|
|
impl Item for NativeComboBoxPopup {
|
|
fn init(self: Pin<&Self>, _window_adapter: &Rc<dyn WindowAdapter>) {}
|
|
|
|
fn geometry(self: Pin<&Self>) -> LogicalRect {
|
|
LogicalRect::new(
|
|
LogicalPoint::from_lengths(self.x(), self.y()),
|
|
LogicalSize::from_lengths(self.width(), self.height()),
|
|
)
|
|
}
|
|
|
|
fn layout_info(
|
|
self: Pin<&Self>,
|
|
_orientation: Orientation,
|
|
_window_adapter: &Rc<dyn WindowAdapter>,
|
|
) -> LayoutInfo {
|
|
Default::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 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 =>
|
|
cpp!(unsafe [
|
|
painter as "QPainterPtr*",
|
|
widget as "QWidget*",
|
|
size as "QSize",
|
|
dpr as "float",
|
|
initial_state as "int"
|
|
] {
|
|
ensure_initialized();
|
|
QStyleOptionFrame option;
|
|
option.state |= QStyle::State(initial_state);
|
|
option.lineWidth = 0;
|
|
option.midLineWidth = 0;
|
|
option.rect = QRect(QPoint(), size / dpr);
|
|
option.state |= QStyle::State_Sunken | QStyle::State_Enabled;
|
|
|
|
auto style = qApp->style();
|
|
|
|
if (style->styleHint(QStyle::SH_ComboBox_Popup, &option, widget)) {
|
|
style->drawPrimitive(QStyle::PE_PanelMenu, &option, painter->get(), widget);
|
|
} else {
|
|
option.lineWidth = 1;
|
|
}
|
|
auto frameStyle = style->styleHint(QStyle::SH_ComboBox_PopupFrameStyle, &option, widget);
|
|
if ((frameStyle & QFrame::Shadow_Mask) == QFrame::Sunken)
|
|
option.state |= QStyle::State_Sunken;
|
|
else if ((frameStyle & QFrame::Shadow_Mask) == QFrame::Raised)
|
|
option.state |= QStyle::State_Raised;
|
|
option.frameShape = QFrame::Shape(frameStyle & QFrame::Shape_Mask);
|
|
style->drawControl(QStyle::CE_ShapedFrame, &option, painter->get(), widget);
|
|
});
|
|
}
|
|
}
|
|
|
|
impl ItemConsts for NativeComboBoxPopup {
|
|
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_NativeComboBoxPopupVTable() -> NativeComboBoxPopupVTable for NativeComboBoxPopup
|
|
}
|