mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-01 06:11:16 +00:00
Strawman implementation of a combo box
This commit is contained in:
parent
a6dbd0d7fa
commit
8a95b806c7
9 changed files with 291 additions and 5 deletions
|
@ -50,6 +50,7 @@ extern const cbindgen_private::ItemVTable NativeGroupBoxVTable;
|
|||
extern const cbindgen_private::ItemVTable NativeLineEditVTable;
|
||||
extern const cbindgen_private::ItemVTable NativeScrollViewVTable;
|
||||
extern const cbindgen_private::ItemVTable NativeStandardListViewItemVTable;
|
||||
extern const cbindgen_private::ItemVTable NativeComboBoxVTable;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -134,6 +135,7 @@ using cbindgen_private::NativeSlider;
|
|||
using cbindgen_private::NativeSpinBox;
|
||||
using cbindgen_private::NativeStandardListViewItem;
|
||||
using cbindgen_private::NativeStyleMetrics;
|
||||
using cbindgen_private::NativeComboBox;
|
||||
|
||||
namespace private_api {
|
||||
constexpr inline ItemTreeNode make_item_node(std::uintptr_t offset,
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
This file is also available under commercial licensing terms.
|
||||
Please contact info@sixtyfps.io for more information.
|
||||
LICENSE END */
|
||||
import { Button, CheckBox, SpinBox, Slider, GroupBox, LineEdit, StandardListView } from "sixtyfps_widgets.60";
|
||||
import { Button, CheckBox, SpinBox, Slider, GroupBox, LineEdit, StandardListView, ComboBox } from "sixtyfps_widgets.60";
|
||||
|
||||
App := Window {
|
||||
width: 500px;
|
||||
|
@ -67,6 +67,13 @@ App := Window {
|
|||
value: 42;
|
||||
}
|
||||
}
|
||||
|
||||
GroupBox {
|
||||
title: "ComboBox";
|
||||
ComboBox {
|
||||
model: ["Select Something", "From this", "Combobox"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GroupBox {
|
||||
|
|
|
@ -532,6 +532,20 @@ impl TypeRegister {
|
|||
],
|
||||
&[],
|
||||
);
|
||||
native_class(
|
||||
&mut register,
|
||||
"NativeComboBox",
|
||||
&[
|
||||
("x", Type::Length),
|
||||
("y", Type::Length),
|
||||
("width", Type::Length),
|
||||
("height", Type::Length),
|
||||
("current_value", Type::String),
|
||||
("is_open", Type::Bool),
|
||||
("enabled", Type::Bool),
|
||||
],
|
||||
&[],
|
||||
);
|
||||
|
||||
let mut native_style_metrics =
|
||||
BuiltinElement::new(Rc::new(NativeClass::new_with_properties(
|
||||
|
|
|
@ -103,3 +103,37 @@ export StandardListView := ListView {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export ComboBox := NativeComboBox {
|
||||
property <[string]> model;
|
||||
property <int> current_index : -1;
|
||||
enabled: true;
|
||||
|
||||
if (is_open && enabled) : Rectangle {
|
||||
y: root.height;
|
||||
width: root.width;
|
||||
for value[idx] in root.model: Rectangle {
|
||||
y: idx * height;
|
||||
width: 100%;
|
||||
height: root.height;
|
||||
color: idx == root.current_index ? lightblue : white;
|
||||
Text {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text: value;
|
||||
}
|
||||
TouchArea {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
clicked => {
|
||||
if (root.enabled) {
|
||||
root.current_index = idx;
|
||||
root.current_value = value;
|
||||
}
|
||||
is_open = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -418,3 +418,72 @@ export StandardListView := ListView {
|
|||
}
|
||||
}
|
||||
|
||||
export ComboBox := Rectangle {
|
||||
property <[string]> model;
|
||||
property <int> current_index : -1;
|
||||
property <string> current_value;
|
||||
property <bool> is_open: false;
|
||||
property <bool> enabled: true;
|
||||
|
||||
border_width: 2px;
|
||||
border_radius: 10px;
|
||||
border_color: Palette.text_color;
|
||||
color: touch_area.pressed ? Palette.heighlight_background : Palette.button_background;
|
||||
animate color { duration: 100ms; }
|
||||
horizontal-stretch: 0;
|
||||
vertical-stretch: 0;
|
||||
minimum_width: 170px;
|
||||
|
||||
HorizontalLayout {
|
||||
padding: root.border_radius;
|
||||
t := Text {
|
||||
text <=> root.current_value;
|
||||
horizontal_alignment: align_center;
|
||||
vertical_alignment: align_center;
|
||||
color: root.enabled ? Palette.text_color : Palette.text_color_disabled;
|
||||
horizontal-stretch: 1;
|
||||
}
|
||||
Text {
|
||||
text:"▼";
|
||||
color: root.enabled ? Palette.text_color : Palette.text_color_disabled;
|
||||
horizontal-stretch: 0;
|
||||
}
|
||||
}
|
||||
|
||||
touch_area := TouchArea {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
clicked => {
|
||||
if (root.enabled) {
|
||||
is_open = !is_open;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_open && enabled) : Rectangle {
|
||||
y: root.height;
|
||||
width: root.width;
|
||||
for value[idx] in root.model: Rectangle {
|
||||
y: idx * height;
|
||||
width: 100%;
|
||||
height: t.height;
|
||||
color: idx == root.current_index ? Palette.heighlight_background : Palette.base_background_color;
|
||||
Text {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text: value;
|
||||
}
|
||||
TouchArea {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
clicked => {
|
||||
if (root.enabled) {
|
||||
root.current_index = idx;
|
||||
root.current_value = value;
|
||||
}
|
||||
is_open = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,11 +14,10 @@ When adding an item or a property, it needs to be kept in sync with different pl
|
|||
(This is less than ideal and maybe we can have some automation later)
|
||||
|
||||
- It needs to be changed in this module
|
||||
- The ItemVTable_static at the end of datastructures.rs (new items only)
|
||||
- In the compiler: typeregister.rs
|
||||
- In the vewer: main.rs
|
||||
- For the C++ code (new item only): the build.rs to export the new item, and the `using` declaration in sixtyfps.h
|
||||
|
||||
- Don't forget to update the documentation
|
||||
*/
|
||||
|
||||
#![allow(unsafe_code)]
|
||||
|
|
|
@ -64,7 +64,8 @@ pub type NativeWidgets =
|
|||
(widgets::NativeLineEdit,
|
||||
(widgets::NativeScrollView,
|
||||
(widgets::NativeStandardListViewItem,
|
||||
()))))))));
|
||||
(widgets::NativeComboBox,
|
||||
())))))))));
|
||||
|
||||
#[cfg(not(no_qt))]
|
||||
#[rustfmt::skip]
|
||||
|
|
|
@ -7,7 +7,23 @@
|
|||
This file is also available under commercial licensing terms.
|
||||
Please contact info@sixtyfps.io for more information.
|
||||
LICENSE END */
|
||||
|
||||
/*!
|
||||
|
||||
This module contains all the native Qt widgetimplementation that forwards to QStyle.
|
||||
|
||||
Same as in sixtyfps_corelib::items, when When adding an item or a property,
|
||||
it needs to be kept in sync with different place.
|
||||
|
||||
- It needs to be changed in this module
|
||||
- the Widget list in lib.rs
|
||||
- In the compiler: typeregister.rs
|
||||
- For the C++ code (new item only): the build.rs to export the new item, and the `using` declaration in sixtyfps.h
|
||||
- Don't forget to update the documentation
|
||||
*/
|
||||
|
||||
#![allow(non_upper_case_globals)]
|
||||
|
||||
use const_field_offset::FieldOffsets;
|
||||
use core::pin::Pin;
|
||||
use cpp::cpp;
|
||||
|
@ -1600,6 +1616,149 @@ impl ItemConsts for NativeStandardListViewItem {
|
|||
|
||||
ItemVTable_static! { #[no_mangle] pub static NativeStandardListViewItemVTable for NativeStandardListViewItem }
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(FieldOffsets, Default, BuiltinItem)]
|
||||
#[pin]
|
||||
pub struct NativeComboBox {
|
||||
pub x: Property<f32>,
|
||||
pub y: Property<f32>,
|
||||
pub width: Property<f32>,
|
||||
pub height: Property<f32>,
|
||||
pub enabled: Property<bool>,
|
||||
pub pressed: Property<bool>,
|
||||
pub is_open: Property<bool>,
|
||||
pub current_value: Property<SharedString>,
|
||||
pub cached_rendering_data: CachedRenderingData,
|
||||
}
|
||||
|
||||
impl Item for NativeComboBox {
|
||||
fn init(self: Pin<&Self>, _window: &ComponentWindow) {}
|
||||
|
||||
fn geometry(self: Pin<&Self>) -> Rect {
|
||||
euclid::rect(
|
||||
Self::FIELD_OFFSETS.x.apply_pin(self).get(),
|
||||
Self::FIELD_OFFSETS.y.apply_pin(self).get(),
|
||||
Self::FIELD_OFFSETS.width.apply_pin(self).get(),
|
||||
Self::FIELD_OFFSETS.height.apply_pin(self).get(),
|
||||
)
|
||||
}
|
||||
fn rendering_primitive(
|
||||
self: Pin<&Self>,
|
||||
window: &ComponentWindow,
|
||||
) -> HighLevelRenderingPrimitive {
|
||||
let down: bool = Self::FIELD_OFFSETS.pressed.apply_pin(self).get();
|
||||
let is_open: bool = Self::FIELD_OFFSETS.is_open.apply_pin(self).get();
|
||||
let text: qttypes::QString =
|
||||
Self::FIELD_OFFSETS.current_value.apply_pin(self).get().as_str().into();
|
||||
let enabled = Self::FIELD_OFFSETS.enabled.apply_pin(self).get();
|
||||
let size: qttypes::QSize = get_size!(self);
|
||||
let dpr = window.scale_factor();
|
||||
|
||||
let mut imgarray = QImageWrapArray::new(size, dpr);
|
||||
let img = &mut imgarray.img;
|
||||
|
||||
cpp!(unsafe [
|
||||
img as "QImage*",
|
||||
text as "QString",
|
||||
enabled as "bool",
|
||||
size as "QSize",
|
||||
down as "bool",
|
||||
is_open as "bool",
|
||||
dpr as "float"
|
||||
] {
|
||||
QPainter p(img);
|
||||
QStyleOptionComboBox option;
|
||||
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;
|
||||
if (is_open)
|
||||
option.state |= QStyle::State_On;
|
||||
option.subControls = QStyle::SC_All;
|
||||
qApp->style()->drawComplexControl(QStyle::CC_ComboBox, &option, &p, nullptr);
|
||||
qApp->style()->drawControl(QStyle::CE_ComboBoxLabel, &option, &p, nullptr);
|
||||
});
|
||||
return HighLevelRenderingPrimitive::Image { source: imgarray.to_resource() };
|
||||
}
|
||||
|
||||
fn rendering_variables(
|
||||
self: Pin<&Self>,
|
||||
_window: &ComponentWindow,
|
||||
) -> SharedArray<RenderingVariable> {
|
||||
SharedArray::default()
|
||||
}
|
||||
|
||||
fn layouting_info(self: Pin<&Self>, window: &ComponentWindow) -> LayoutInfo {
|
||||
let text: qttypes::QString =
|
||||
Self::FIELD_OFFSETS.current_value.apply_pin(self).get().as_str().into();
|
||||
let dpr = window.scale_factor();
|
||||
let size = cpp!(unsafe [
|
||||
text as "QString",
|
||||
dpr as "float"
|
||||
] -> qttypes::QSize as "QSize" {
|
||||
ensure_initialized();
|
||||
QStyleOptionButton option;
|
||||
// FIXME
|
||||
option.rect = option.fontMetrics.boundingRect("*****************");
|
||||
option.text = std::move(text);
|
||||
return qApp->style()->sizeFromContents(QStyle::CT_ComboBox, &option, option.rect.size(), nullptr) * dpr;
|
||||
});
|
||||
LayoutInfo {
|
||||
min_width: size.width as f32,
|
||||
min_height: size.height as f32,
|
||||
..LayoutInfo::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn input_event(
|
||||
self: Pin<&Self>,
|
||||
event: MouseEvent,
|
||||
_window: &ComponentWindow,
|
||||
_app_component: ComponentRefPin,
|
||||
) -> InputEventResult {
|
||||
let enabled = Self::FIELD_OFFSETS.enabled.apply_pin(self).get();
|
||||
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.what {
|
||||
MouseEventType::MousePressed => true,
|
||||
MouseEventType::MouseExit | MouseEventType::MouseReleased => false,
|
||||
MouseEventType::MouseMoved => {
|
||||
return if Self::FIELD_OFFSETS.pressed.apply_pin(self).get() {
|
||||
InputEventResult::GrabMouse
|
||||
} else {
|
||||
InputEventResult::EventIgnored
|
||||
}
|
||||
}
|
||||
});
|
||||
if matches!(event.what, MouseEventType::MouseReleased) {
|
||||
Self::FIELD_OFFSETS.is_open.apply_pin(self).set(true);
|
||||
InputEventResult::EventAccepted
|
||||
} else {
|
||||
InputEventResult::GrabMouse
|
||||
}
|
||||
}
|
||||
|
||||
fn key_event(self: Pin<&Self>, _: &KeyEvent, _window: &ComponentWindow) -> KeyEventResult {
|
||||
KeyEventResult::EventIgnored
|
||||
}
|
||||
|
||||
fn focus_event(self: Pin<&Self>, _: &FocusEvent, _window: &ComponentWindow) {}
|
||||
}
|
||||
|
||||
impl ItemConsts for NativeComboBox {
|
||||
const cached_rendering_data_offset: const_field_offset::FieldOffset<Self, CachedRenderingData> =
|
||||
Self::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection();
|
||||
}
|
||||
|
||||
ItemVTable_static! { #[no_mangle] pub static NativeComboBoxVTable for NativeComboBox }
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(FieldOffsets, BuiltinItem)]
|
||||
#[pin]
|
||||
|
|
|
@ -236,6 +236,7 @@ fn gen_backend_qt(include_dir: &Path) -> anyhow::Result<()> {
|
|||
"NativeLineEdit",
|
||||
"NativeScrollView",
|
||||
"NativeStandardListViewItem",
|
||||
"NativeComboBox",
|
||||
]
|
||||
.iter()
|
||||
.map(|x| x.to_string())
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue