mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-02 06:41:14 +00:00
Draw the frame properly around the native scrollview
This commit is contained in:
parent
6aa292eac1
commit
e4e601de83
8 changed files with 341 additions and 193 deletions
|
@ -48,7 +48,7 @@ extern const cbindgen_private::ItemVTable NativeSpinBoxVTable;
|
||||||
extern const cbindgen_private::ItemVTable NativeSliderVTable;
|
extern const cbindgen_private::ItemVTable NativeSliderVTable;
|
||||||
extern const cbindgen_private::ItemVTable NativeGroupBoxVTable;
|
extern const cbindgen_private::ItemVTable NativeGroupBoxVTable;
|
||||||
extern const cbindgen_private::ItemVTable NativeLineEditVTable;
|
extern const cbindgen_private::ItemVTable NativeLineEditVTable;
|
||||||
extern const cbindgen_private::ItemVTable NativeScrollBarVTable;
|
extern const cbindgen_private::ItemVTable NativeScrollViewVTable;
|
||||||
extern const cbindgen_private::ItemVTable NativeStandardListViewItemVTable;
|
extern const cbindgen_private::ItemVTable NativeStandardListViewItemVTable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -129,7 +129,7 @@ using cbindgen_private::NativeButton;
|
||||||
using cbindgen_private::NativeCheckBox;
|
using cbindgen_private::NativeCheckBox;
|
||||||
using cbindgen_private::NativeGroupBox;
|
using cbindgen_private::NativeGroupBox;
|
||||||
using cbindgen_private::NativeLineEdit;
|
using cbindgen_private::NativeLineEdit;
|
||||||
using cbindgen_private::NativeScrollBar;
|
using cbindgen_private::NativeScrollView;
|
||||||
using cbindgen_private::NativeSlider;
|
using cbindgen_private::NativeSlider;
|
||||||
using cbindgen_private::NativeSpinBox;
|
using cbindgen_private::NativeSpinBox;
|
||||||
using cbindgen_private::NativeStandardListViewItem;
|
using cbindgen_private::NativeStandardListViewItem;
|
||||||
|
|
|
@ -88,6 +88,7 @@ App := Window {
|
||||||
StandardListView {
|
StandardListView {
|
||||||
model: [
|
model: [
|
||||||
{text: "Lorem"}, {text: "ipsum"},{text: "dolor"},{text: "sit"},{text: "amet"},{text: "consetetur"},
|
{text: "Lorem"}, {text: "ipsum"},{text: "dolor"},{text: "sit"},{text: "amet"},{text: "consetetur"},
|
||||||
|
{text: "Lorem"}, {text: "ipsum"},{text: "dolor"},{text: "sit"},{text: "amet"},{text: "consetetur"},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -944,16 +944,22 @@ impl TypeRegister {
|
||||||
);
|
);
|
||||||
native_class(
|
native_class(
|
||||||
&mut r,
|
&mut r,
|
||||||
"NativeScrollBar",
|
"NativeScrollView",
|
||||||
&[
|
&[
|
||||||
("x", Type::Length),
|
("x", Type::Length),
|
||||||
("y", Type::Length),
|
("y", Type::Length),
|
||||||
("width", Type::Length),
|
("width", Type::Length),
|
||||||
("height", Type::Length),
|
("height", Type::Length),
|
||||||
("horizontal", Type::Bool),
|
("horizontal_max", Type::Length),
|
||||||
("max", Type::Length),
|
("horizontal_page_size", Type::Length),
|
||||||
("page_size", Type::Length),
|
("horizontal_value", Type::Length),
|
||||||
("value", Type::Length),
|
("vertical_max", Type::Length),
|
||||||
|
("vertical_page_size", Type::Length),
|
||||||
|
("vertical_value", Type::Length),
|
||||||
|
("native_padding_left", Type::Length),
|
||||||
|
("native_padding_right", Type::Length),
|
||||||
|
("native_padding_top", Type::Length),
|
||||||
|
("native_padding_bottom", Type::Length),
|
||||||
],
|
],
|
||||||
&[],
|
&[],
|
||||||
);
|
);
|
||||||
|
|
|
@ -40,7 +40,7 @@ export LineEdit := NativeLineEdit {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export ScrollView := Rectangle {
|
export ScrollView := NativeScrollView {
|
||||||
property <length> viewport_width <=> fli.viewport_width;
|
property <length> viewport_width <=> fli.viewport_width;
|
||||||
property <length> viewport_height <=> fli.viewport_height;
|
property <length> viewport_height <=> fli.viewport_height;
|
||||||
property <length> viewport_x <=> fli.viewport_x;
|
property <length> viewport_x <=> fli.viewport_x;
|
||||||
|
@ -48,33 +48,24 @@ export ScrollView := Rectangle {
|
||||||
property <length> visible_width <=> fli.width;
|
property <length> visible_width <=> fli.width;
|
||||||
property <length> visible_height <=> fli.height;
|
property <length> visible_height <=> fli.height;
|
||||||
|
|
||||||
// FIXME: the frame should be drawn by a native control
|
vertical_max: fli.viewport_height - fli.height;
|
||||||
border_width: 1px;
|
vertical_page_size: fli.height;
|
||||||
border_color: #d0d3cf;
|
|
||||||
color: white;
|
|
||||||
|
|
||||||
GridLayout {
|
horizontal_max: fli.viewport_width - fli.width;
|
||||||
padding_left: 1px;
|
horizontal_page_size: fli.width;
|
||||||
padding_top: 1px;
|
|
||||||
fli := Flickable {
|
fli := Flickable {
|
||||||
$children
|
x: root.native_padding_left;
|
||||||
interactive: false;
|
width: root.width - root.native_padding_left - root.native_padding_right;
|
||||||
viewport_y: -vbar.value;
|
y: root.native_padding_top;
|
||||||
viewport_x: -hbar.value;
|
height: root.height - root.native_padding_top - root.native_padding_bottom;
|
||||||
|
|
||||||
|
$children
|
||||||
|
interactive: false;
|
||||||
|
viewport_y: -root.vertical_value;
|
||||||
|
viewport_x: -root.horizontal_value;
|
||||||
viewport_height: 1000px;
|
viewport_height: 1000px;
|
||||||
viewport_width: 1000px;
|
viewport_width: 1000px;
|
||||||
}
|
|
||||||
vbar := NativeScrollBar {
|
|
||||||
max: fli.viewport_height - fli.height;
|
|
||||||
page_size: fli.height;
|
|
||||||
}
|
|
||||||
hbar := NativeScrollBar {
|
|
||||||
horizontal: true;
|
|
||||||
row: 1;
|
|
||||||
max: fli.viewport_width - fli.width;
|
|
||||||
page_size: fli.width;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ pub type NativeWidgets =
|
||||||
(widgets::NativeSpinBox,
|
(widgets::NativeSpinBox,
|
||||||
(widgets::NativeGroupBox,
|
(widgets::NativeGroupBox,
|
||||||
(widgets::NativeLineEdit,
|
(widgets::NativeLineEdit,
|
||||||
(widgets::NativeScrollBar,
|
(widgets::NativeScrollView,
|
||||||
(widgets::NativeStandardListViewItem,
|
(widgets::NativeStandardListViewItem,
|
||||||
()))))))));
|
()))))))));
|
||||||
|
|
||||||
|
|
|
@ -99,3 +99,16 @@ pub struct QMargins {
|
||||||
pub right: i32,
|
pub right: i32,
|
||||||
pub bottom: i32,
|
pub bottom: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// FIXME: qreal is not always f64
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub type qreal = f64;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Default, Clone, Copy, PartialEq, Debug)]
|
||||||
|
pub struct QRectF {
|
||||||
|
pub x: qreal,
|
||||||
|
pub y: qreal,
|
||||||
|
pub width: qreal,
|
||||||
|
pub height: qreal,
|
||||||
|
}
|
||||||
|
|
|
@ -517,7 +517,8 @@ ItemVTable_static! { #[no_mangle] pub static NativeSpinBoxVTable for NativeSpinB
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
struct NativeSliderData {
|
struct NativeSliderData {
|
||||||
active_controls: u32,
|
active_controls: u32,
|
||||||
pressed: bool,
|
/// For sliders, this is a bool, For scroll area: 1 == horizontal, 2 == vertical
|
||||||
|
pressed: u8,
|
||||||
pressed_x: f32,
|
pressed_x: f32,
|
||||||
pressed_val: f32,
|
pressed_val: f32,
|
||||||
}
|
}
|
||||||
|
@ -649,7 +650,7 @@ impl Item for NativeSlider {
|
||||||
let max = Self::FIELD_OFFSETS.max.apply_pin(self).get() as f32;
|
let max = Self::FIELD_OFFSETS.max.apply_pin(self).get() as f32;
|
||||||
let mut data = Self::FIELD_OFFSETS.data.apply_pin(self).get();
|
let mut data = Self::FIELD_OFFSETS.data.apply_pin(self).get();
|
||||||
let active_controls = data.active_controls;
|
let active_controls = data.active_controls;
|
||||||
let pressed = data.pressed;
|
let pressed: bool = data.pressed != 0;
|
||||||
let pos = qttypes::QPoint { x: event.pos.x as _, y: event.pos.y as _ };
|
let pos = qttypes::QPoint { x: event.pos.x as _, y: event.pos.y as _ };
|
||||||
|
|
||||||
let new_control = cpp!(unsafe [
|
let new_control = cpp!(unsafe [
|
||||||
|
@ -671,16 +672,16 @@ impl Item for NativeSlider {
|
||||||
let result = match event.what {
|
let result = match event.what {
|
||||||
MouseEventType::MousePressed => {
|
MouseEventType::MousePressed => {
|
||||||
data.pressed_x = event.pos.x as f32;
|
data.pressed_x = event.pos.x as f32;
|
||||||
data.pressed = true;
|
data.pressed = 1;
|
||||||
data.pressed_val = value;
|
data.pressed_val = value;
|
||||||
InputEventResult::GrabMouse
|
InputEventResult::GrabMouse
|
||||||
}
|
}
|
||||||
MouseEventType::MouseExit | MouseEventType::MouseReleased => {
|
MouseEventType::MouseExit | MouseEventType::MouseReleased => {
|
||||||
data.pressed = false;
|
data.pressed = 0;
|
||||||
InputEventResult::EventAccepted
|
InputEventResult::EventAccepted
|
||||||
}
|
}
|
||||||
MouseEventType::MouseMoved => {
|
MouseEventType::MouseMoved => {
|
||||||
if data.pressed {
|
if data.pressed != 0 {
|
||||||
// FIXME: use QStyle::subControlRect to find out the actual size of the groove
|
// FIXME: use QStyle::subControlRect to find out the actual size of the groove
|
||||||
let new_val = data.pressed_val
|
let new_val = data.pressed_val
|
||||||
+ ((event.pos.x as f32) - data.pressed_x) * (max - min) / size.width as f32;
|
+ ((event.pos.x as f32) - data.pressed_x) * (max - min) / size.width as f32;
|
||||||
|
@ -1045,21 +1046,85 @@ ItemVTable_static! { #[no_mangle] pub static NativeLineEditVTable for NativeLine
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(FieldOffsets, Default, BuiltinItem)]
|
#[derive(FieldOffsets, Default, BuiltinItem)]
|
||||||
#[pin]
|
#[pin]
|
||||||
pub struct NativeScrollBar {
|
pub struct NativeScrollView {
|
||||||
pub x: Property<f32>,
|
pub x: Property<f32>,
|
||||||
pub y: Property<f32>,
|
pub y: Property<f32>,
|
||||||
pub width: Property<f32>,
|
pub width: Property<f32>,
|
||||||
pub height: Property<f32>,
|
pub height: Property<f32>,
|
||||||
pub max: Property<f32>,
|
pub horizontal_max: Property<f32>,
|
||||||
pub page_size: Property<f32>,
|
pub horizontal_page_size: Property<f32>,
|
||||||
pub value: Property<f32>,
|
pub horizontal_value: Property<f32>,
|
||||||
pub horizontal: Property<bool>,
|
pub vertical_max: Property<f32>,
|
||||||
|
pub vertical_page_size: Property<f32>,
|
||||||
|
pub vertical_value: Property<f32>,
|
||||||
pub cached_rendering_data: CachedRenderingData,
|
pub cached_rendering_data: CachedRenderingData,
|
||||||
|
pub native_padding_left: Property<f32>,
|
||||||
|
pub native_padding_right: Property<f32>,
|
||||||
|
pub native_padding_top: Property<f32>,
|
||||||
|
pub native_padding_bottom: Property<f32>,
|
||||||
data: Property<NativeSliderData>,
|
data: Property<NativeSliderData>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Item for NativeScrollBar {
|
impl Item for NativeScrollView {
|
||||||
fn init(self: Pin<&Self>, _window: &ComponentWindow) {}
|
fn init(self: Pin<&Self>, window: &ComponentWindow) {
|
||||||
|
let paddings = Rc::pin(Property::default());
|
||||||
|
|
||||||
|
paddings.as_ref().set_binding({
|
||||||
|
let window_weak = Rc::downgrade(&window.0.clone());
|
||||||
|
move || {
|
||||||
|
let dpr = window_weak.upgrade().unwrap().scale_factor();
|
||||||
|
|
||||||
|
cpp!(unsafe [
|
||||||
|
dpr as "float"
|
||||||
|
] -> qttypes::QMargins as "QMargins" {
|
||||||
|
ensure_initialized();
|
||||||
|
QStyleOptionSlider option;
|
||||||
|
initQSliderOptions(option, false, 0, 0, 1000, 1000);
|
||||||
|
|
||||||
|
int extent = qApp->style()->pixelMetric(QStyle::PM_ScrollBarExtent, &option, global_widget());
|
||||||
|
int sliderMin = qApp->style()->pixelMetric(QStyle::PM_ScrollBarSliderMin, &option, global_widget());
|
||||||
|
auto horizontal_size = qApp->style()->sizeFromContents(QStyle::CT_ScrollBar, &option, QSize(extent * 2 + sliderMin, extent), global_widget());
|
||||||
|
option.state ^= QStyle::State_Horizontal;
|
||||||
|
option.orientation = Qt::Vertical;
|
||||||
|
extent = qApp->style()->pixelMetric(QStyle::PM_ScrollBarExtent, &option, global_widget());
|
||||||
|
sliderMin = qApp->style()->pixelMetric(QStyle::PM_ScrollBarSliderMin, &option, global_widget());
|
||||||
|
auto vertical_size = qApp->style()->sizeFromContents(QStyle::CT_ScrollBar, &option, QSize(extent, extent * 2 + sliderMin), global_widget());
|
||||||
|
|
||||||
|
/*int hscrollOverlap = hbar->style()->pixelMetric(QStyle::PM_ScrollView_ScrollBarOverlap, &opt, hbar);
|
||||||
|
int vscrollOverlap = vbar->style()->pixelMetric(QStyle::PM_ScrollView_ScrollBarOverlap, &opt, vbar);*/
|
||||||
|
|
||||||
|
QStyleOptionFrame frameOption;
|
||||||
|
frameOption.rect = QRect(QPoint(), QSize(1000, 1000));
|
||||||
|
frameOption.frameShape = QFrame::StyledPanel;
|
||||||
|
frameOption.lineWidth = 1;
|
||||||
|
frameOption.midLineWidth = 0;
|
||||||
|
QRect cr = qApp->style()->subElementRect(QStyle::SE_ShapedFrameContents, &frameOption, global_widget());
|
||||||
|
return {
|
||||||
|
qRound(cr.left() * dpr),
|
||||||
|
qRound(cr.top() * dpr),
|
||||||
|
qRound((vertical_size.width() + frameOption.rect.right() - cr.right()) * dpr),
|
||||||
|
qRound((horizontal_size.height() + frameOption.rect.bottom() - cr.bottom()) * dpr) };
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
self.native_padding_left.set_binding({
|
||||||
|
let paddings = paddings.clone();
|
||||||
|
move || paddings.as_ref().get().left as _
|
||||||
|
});
|
||||||
|
self.native_padding_right.set_binding({
|
||||||
|
let paddings = paddings.clone();
|
||||||
|
move || paddings.as_ref().get().right as _
|
||||||
|
});
|
||||||
|
self.native_padding_top.set_binding({
|
||||||
|
let paddings = paddings.clone();
|
||||||
|
move || paddings.as_ref().get().top as _
|
||||||
|
});
|
||||||
|
self.native_padding_bottom.set_binding({
|
||||||
|
let paddings = paddings.clone();
|
||||||
|
move || paddings.as_ref().get().bottom as _
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
fn geometry(self: Pin<&Self>) -> Rect {
|
fn geometry(self: Pin<&Self>) -> Rect {
|
||||||
euclid::rect(
|
euclid::rect(
|
||||||
|
@ -1075,41 +1140,98 @@ impl Item for NativeScrollBar {
|
||||||
) -> HighLevelRenderingPrimitive {
|
) -> HighLevelRenderingPrimitive {
|
||||||
let size: qttypes::QSize = get_size!(self);
|
let size: qttypes::QSize = get_size!(self);
|
||||||
let dpr = window.scale_factor();
|
let dpr = window.scale_factor();
|
||||||
let value = Self::FIELD_OFFSETS.value.apply_pin(self).get() as i32;
|
|
||||||
let max = (Self::FIELD_OFFSETS.max.apply_pin(self).get() as i32).max(0);
|
|
||||||
let page_size = Self::FIELD_OFFSETS.page_size.apply_pin(self).get() as i32;
|
|
||||||
let horizontal: bool = Self::FIELD_OFFSETS.horizontal.apply_pin(self).get();
|
|
||||||
let data = Self::FIELD_OFFSETS.data.apply_pin(self).get();
|
let data = Self::FIELD_OFFSETS.data.apply_pin(self).get();
|
||||||
let active_controls = data.active_controls;
|
let left = Self::FIELD_OFFSETS.native_padding_left.apply_pin(self).get();
|
||||||
let pressed = data.pressed;
|
let right = Self::FIELD_OFFSETS.native_padding_right.apply_pin(self).get();
|
||||||
|
let top = Self::FIELD_OFFSETS.native_padding_top.apply_pin(self).get();
|
||||||
let img = cpp!(unsafe [
|
let bottom = Self::FIELD_OFFSETS.native_padding_bottom.apply_pin(self).get();
|
||||||
value as "int",
|
let corner_rect = qttypes::QRectF {
|
||||||
page_size as "int",
|
x: ((size.width as f32 - (right - left)) / dpr) as _,
|
||||||
max as "int",
|
y: ((size.height as f32 - (bottom - top)) / dpr) as _,
|
||||||
size as "QSize",
|
width: ((right - left) / dpr) as _,
|
||||||
active_controls as "int",
|
height: ((bottom - top) / dpr) as _,
|
||||||
pressed as "bool",
|
};
|
||||||
dpr as "float",
|
let mut img = cpp!(unsafe [dpr as "float",size as "QSize", corner_rect as "QRectF"] -> qttypes::QImage as "QImage" {
|
||||||
horizontal as "bool"
|
ensure_initialized();
|
||||||
] -> qttypes::QImage as "QImage" {
|
|
||||||
auto [img, rect] = offline_style_rendering_image(size, dpr);
|
auto [img, rect] = offline_style_rendering_image(size, dpr);
|
||||||
|
QStyleOptionFrame frameOption;
|
||||||
|
frameOption.frameShape = QFrame::StyledPanel;
|
||||||
|
frameOption.lineWidth = 1;
|
||||||
|
frameOption.midLineWidth = 0;
|
||||||
|
frameOption.rect = corner_rect.toAlignedRect();
|
||||||
QPainter p(&img);
|
QPainter p(&img);
|
||||||
QStyleOptionSlider option;
|
qApp->style()->drawPrimitive(QStyle::PE_PanelScrollAreaCorner, &frameOption, &p, global_widget());
|
||||||
option.rect = rect;
|
frameOption.rect = QRect(QPoint(), corner_rect.toAlignedRect().topLeft());
|
||||||
initQSliderOptions(option, pressed, active_controls, 0, max / dpr, value / dpr);
|
qApp->style()->drawControl(QStyle::CE_ShapedFrame, &frameOption, &p, global_widget());
|
||||||
option.subControls = QStyle::SC_All;
|
|
||||||
option.pageStep = page_size / dpr;
|
|
||||||
|
|
||||||
if (!horizontal) {
|
|
||||||
option.state ^= QStyle::State_Horizontal;
|
|
||||||
option.orientation = Qt::Vertical;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto style = qApp->style();
|
|
||||||
style->drawComplexControl(QStyle::CC_ScrollBar, &option, &p, global_widget());
|
|
||||||
return img;
|
return img;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let mut draw_scrollbar = |horizontal: bool,
|
||||||
|
rect: qttypes::QRectF,
|
||||||
|
value: i32,
|
||||||
|
page_size: i32,
|
||||||
|
max: i32,
|
||||||
|
active_controls: u32,
|
||||||
|
pressed: bool| {
|
||||||
|
cpp!(unsafe [
|
||||||
|
mut img as "QImage",
|
||||||
|
value as "int",
|
||||||
|
page_size as "int",
|
||||||
|
max as "int",
|
||||||
|
rect as "QRectF",
|
||||||
|
active_controls as "int",
|
||||||
|
pressed as "bool",
|
||||||
|
dpr as "float",
|
||||||
|
horizontal as "bool"
|
||||||
|
] {
|
||||||
|
QPainter p(&img);
|
||||||
|
auto r = rect.toAlignedRect();
|
||||||
|
p.translate(r.topLeft()); // There is bugs in the styles if the scrollbar is not in (0,0)
|
||||||
|
QStyleOptionSlider option;
|
||||||
|
option.rect = QRect(QPoint(), r.size());
|
||||||
|
initQSliderOptions(option, pressed, active_controls, 0, max / dpr, value / dpr);
|
||||||
|
option.subControls = QStyle::SC_All;
|
||||||
|
option.pageStep = page_size / dpr;
|
||||||
|
|
||||||
|
if (!horizontal) {
|
||||||
|
option.state ^= QStyle::State_Horizontal;
|
||||||
|
option.orientation = Qt::Vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto style = qApp->style();
|
||||||
|
style->drawComplexControl(QStyle::CC_ScrollBar, &option, &p, global_widget());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
draw_scrollbar(
|
||||||
|
false,
|
||||||
|
qttypes::QRectF {
|
||||||
|
x: ((size.width as f32 - right + left) / dpr) as _,
|
||||||
|
y: 0.,
|
||||||
|
width: ((right - left) / dpr) as _,
|
||||||
|
height: ((size.height as f32 - bottom + top) / dpr) as _,
|
||||||
|
},
|
||||||
|
Self::FIELD_OFFSETS.vertical_value.apply_pin(self).get() as i32,
|
||||||
|
Self::FIELD_OFFSETS.vertical_page_size.apply_pin(self).get() as i32,
|
||||||
|
Self::FIELD_OFFSETS.vertical_max.apply_pin(self).get() as i32,
|
||||||
|
data.active_controls,
|
||||||
|
data.pressed == 2,
|
||||||
|
);
|
||||||
|
draw_scrollbar(
|
||||||
|
true,
|
||||||
|
qttypes::QRectF {
|
||||||
|
x: 0.,
|
||||||
|
y: ((size.height as f32 - bottom + top) / dpr) as _,
|
||||||
|
width: ((size.width as f32 - right + left) / dpr) as _,
|
||||||
|
height: ((bottom - top) / dpr) as _,
|
||||||
|
},
|
||||||
|
Self::FIELD_OFFSETS.horizontal_value.apply_pin(self).get() as i32,
|
||||||
|
Self::FIELD_OFFSETS.horizontal_page_size.apply_pin(self).get() as i32,
|
||||||
|
Self::FIELD_OFFSETS.horizontal_max.apply_pin(self).get() as i32,
|
||||||
|
data.active_controls,
|
||||||
|
data.pressed == 1,
|
||||||
|
);
|
||||||
|
|
||||||
return HighLevelRenderingPrimitive::Image { source: to_resource(img) };
|
return HighLevelRenderingPrimitive::Image { source: to_resource(img) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1120,39 +1242,12 @@ impl Item for NativeScrollBar {
|
||||||
SharedArray::default()
|
SharedArray::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layouting_info(self: Pin<&Self>, window: &ComponentWindow) -> LayoutInfo {
|
fn layouting_info(self: Pin<&Self>, _window: &ComponentWindow) -> LayoutInfo {
|
||||||
let dpr = window.scale_factor();
|
let left = Self::FIELD_OFFSETS.native_padding_left.apply_pin(self).get();
|
||||||
let horizontal: bool = Self::FIELD_OFFSETS.horizontal.apply_pin(self).get();
|
let right = Self::FIELD_OFFSETS.native_padding_right.apply_pin(self).get();
|
||||||
|
let top = Self::FIELD_OFFSETS.native_padding_top.apply_pin(self).get();
|
||||||
let s = cpp!(unsafe [
|
let bottom = Self::FIELD_OFFSETS.native_padding_bottom.apply_pin(self).get();
|
||||||
horizontal as "bool"
|
LayoutInfo { min_width: left + right, min_height: top + bottom, ..LayoutInfo::default() }
|
||||||
] -> qttypes::QSize as "QSize" {
|
|
||||||
ensure_initialized();
|
|
||||||
|
|
||||||
QStyleOptionSlider option;
|
|
||||||
// int overlap = qApp->style()->pixelMetric(QStyle::PM_ScrollView_ScrollBarOverlap, &option, global_widget());
|
|
||||||
|
|
||||||
initQSliderOptions(option, false, 0, 0, 1000, 1000);
|
|
||||||
if (!horizontal) {
|
|
||||||
option.state ^= QStyle::State_Horizontal;
|
|
||||||
option.orientation = Qt::Vertical;
|
|
||||||
}
|
|
||||||
int extent = qApp->style()->pixelMetric(QStyle::PM_ScrollBarExtent, &option, global_widget());
|
|
||||||
int sliderMin = qApp->style()->pixelMetric(QStyle::PM_ScrollBarSliderMin, &option, global_widget());
|
|
||||||
auto csize = horizontal ? QSize(extent * 2 + sliderMin, extent) : QSize(extent,extent * 2 + sliderMin) ;
|
|
||||||
return qApp->style()->sizeFromContents(QStyle::CT_ScrollBar, &option, csize, global_widget());
|
|
||||||
});
|
|
||||||
let mut result = LayoutInfo {
|
|
||||||
min_width: s.width as f32 * dpr,
|
|
||||||
min_height: s.height as f32 * dpr,
|
|
||||||
..LayoutInfo::default()
|
|
||||||
};
|
|
||||||
if horizontal {
|
|
||||||
result.max_height = result.min_height;
|
|
||||||
} else {
|
|
||||||
result.max_width = result.min_width;
|
|
||||||
}
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn input_event(
|
fn input_event(
|
||||||
|
@ -1162,98 +1257,140 @@ impl Item for NativeScrollBar {
|
||||||
_app_component: ComponentRefPin,
|
_app_component: ComponentRefPin,
|
||||||
) -> InputEventResult {
|
) -> InputEventResult {
|
||||||
let dpr = window.scale_factor();
|
let dpr = window.scale_factor();
|
||||||
let pos = qttypes::QPoint { x: (event.pos.x / dpr) as _, y: (event.pos.y / dpr) as _ };
|
|
||||||
let size: qttypes::QSize = get_size!(self);
|
let size: qttypes::QSize = get_size!(self);
|
||||||
let value = Self::FIELD_OFFSETS.value.apply_pin(self).get() as i32;
|
|
||||||
let max = (Self::FIELD_OFFSETS.max.apply_pin(self).get() as i32).max(0);
|
|
||||||
let page_size = Self::FIELD_OFFSETS.page_size.apply_pin(self).get() as i32;
|
|
||||||
let horizontal: bool = Self::FIELD_OFFSETS.horizontal.apply_pin(self).get();
|
|
||||||
let mut data = Self::FIELD_OFFSETS.data.apply_pin(self).get();
|
let mut data = Self::FIELD_OFFSETS.data.apply_pin(self).get();
|
||||||
let active_controls = data.active_controls;
|
let active_controls = data.active_controls;
|
||||||
let pressed = data.pressed;
|
let pressed = data.pressed;
|
||||||
|
let left = Self::FIELD_OFFSETS.native_padding_left.apply_pin(self).get();
|
||||||
|
let right = Self::FIELD_OFFSETS.native_padding_right.apply_pin(self).get();
|
||||||
|
let top = Self::FIELD_OFFSETS.native_padding_top.apply_pin(self).get();
|
||||||
|
let bottom = Self::FIELD_OFFSETS.native_padding_bottom.apply_pin(self).get();
|
||||||
|
|
||||||
let new_control = cpp!(unsafe [
|
let mut handle_scrollbar = |horizontal: bool,
|
||||||
pos as "QPoint",
|
pos: qttypes::QPoint,
|
||||||
value as "int",
|
size: qttypes::QSize,
|
||||||
page_size as "int",
|
value_prop: Pin<&Property<f32>>,
|
||||||
max as "int",
|
page_size: i32,
|
||||||
size as "QSize",
|
max: i32| {
|
||||||
active_controls as "int",
|
let pressed: bool = data.pressed != 0;
|
||||||
pressed as "bool",
|
let value: i32 = value_prop.get() as i32;
|
||||||
dpr as "float",
|
let new_control = cpp!(unsafe [
|
||||||
horizontal as "bool"
|
pos as "QPoint",
|
||||||
] -> u32 as "int" {
|
value as "int",
|
||||||
ensure_initialized();
|
page_size as "int",
|
||||||
QStyleOptionSlider option;
|
max as "int",
|
||||||
initQSliderOptions(option, pressed, active_controls, 0, max / dpr, value / dpr);
|
size as "QSize",
|
||||||
option.pageStep = page_size / dpr;
|
active_controls as "int",
|
||||||
if (!horizontal) {
|
pressed as "bool",
|
||||||
option.state ^= QStyle::State_Horizontal;
|
dpr as "float",
|
||||||
option.orientation = Qt::Vertical;
|
horizontal as "bool"
|
||||||
}
|
] -> u32 as "int" {
|
||||||
auto style = qApp->style();
|
ensure_initialized();
|
||||||
option.rect = { QPoint{}, size / dpr };
|
QStyleOptionSlider option;
|
||||||
return style->hitTestComplexControl(QStyle::CC_ScrollBar, &option, pos, nullptr);
|
initQSliderOptions(option, pressed, active_controls, 0, max / dpr, value / dpr);
|
||||||
});
|
option.pageStep = page_size / dpr;
|
||||||
|
if (!horizontal) {
|
||||||
#[allow(non_snake_case)]
|
option.state ^= QStyle::State_Horizontal;
|
||||||
let SC_ScrollBarSlider =
|
option.orientation = Qt::Vertical;
|
||||||
cpp!(unsafe []->u32 as "int" { return QStyle::SC_ScrollBarSlider;});
|
|
||||||
|
|
||||||
let (pos, size) =
|
|
||||||
if horizontal { (event.pos.x, size.width) } else { (event.pos.y, size.height) };
|
|
||||||
|
|
||||||
let result = match event.what {
|
|
||||||
MouseEventType::MousePressed => {
|
|
||||||
data.pressed = true;
|
|
||||||
if new_control == SC_ScrollBarSlider {
|
|
||||||
data.pressed_x = pos as f32;
|
|
||||||
data.pressed_val = value as f32;
|
|
||||||
}
|
}
|
||||||
data.active_controls = new_control;
|
auto style = qApp->style();
|
||||||
InputEventResult::GrabMouse
|
option.rect = { QPoint{}, size / dpr };
|
||||||
}
|
return style->hitTestComplexControl(QStyle::CC_ScrollBar, &option, pos / dpr, nullptr);
|
||||||
MouseEventType::MouseExit => {
|
});
|
||||||
data.pressed = false;
|
|
||||||
InputEventResult::EventIgnored
|
#[allow(non_snake_case)]
|
||||||
}
|
let SC_ScrollBarSlider =
|
||||||
MouseEventType::MouseReleased => {
|
cpp!(unsafe []->u32 as "int" { return QStyle::SC_ScrollBarSlider;});
|
||||||
data.pressed = false;
|
|
||||||
let new_val = cpp!(unsafe [active_controls as "int", value as "int", max as "int", page_size as "int", dpr as "float"] -> i32 as "int" {
|
let (pos, size) = if horizontal { (pos.x, size.width) } else { (pos.y, size.height) };
|
||||||
switch (active_controls) {
|
|
||||||
case QStyle::SC_ScrollBarAddPage:
|
let result = match event.what {
|
||||||
return value + page_size;
|
MouseEventType::MousePressed => {
|
||||||
case QStyle::SC_ScrollBarSubPage:
|
data.pressed = if horizontal { 1 } else { 2 };
|
||||||
return value - page_size;
|
if new_control == SC_ScrollBarSlider {
|
||||||
case QStyle::SC_ScrollBarAddLine:
|
data.pressed_x = pos as f32;
|
||||||
return value + dpr;
|
data.pressed_val = value as f32;
|
||||||
case QStyle::SC_ScrollBarSubLine:
|
|
||||||
return value - dpr;
|
|
||||||
case QStyle::SC_ScrollBarFirst:
|
|
||||||
return 0;
|
|
||||||
case QStyle::SC_ScrollBarLast:
|
|
||||||
return max;
|
|
||||||
default:
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
});
|
data.active_controls = new_control;
|
||||||
self.value.set(new_val.max(0).min(max) as f32);
|
|
||||||
InputEventResult::EventIgnored
|
|
||||||
}
|
|
||||||
MouseEventType::MouseMoved => {
|
|
||||||
if data.pressed && data.active_controls == SC_ScrollBarSlider {
|
|
||||||
let max = max as f32;
|
|
||||||
let new_val =
|
|
||||||
data.pressed_val + ((pos as f32) - data.pressed_x) * max / size as f32;
|
|
||||||
self.value.set(new_val.max(0.).min(max));
|
|
||||||
InputEventResult::GrabMouse
|
InputEventResult::GrabMouse
|
||||||
} else {
|
|
||||||
InputEventResult::EventAccepted
|
|
||||||
}
|
}
|
||||||
}
|
MouseEventType::MouseExit => {
|
||||||
|
data.pressed = 0;
|
||||||
|
InputEventResult::EventIgnored
|
||||||
|
}
|
||||||
|
MouseEventType::MouseReleased => {
|
||||||
|
data.pressed = 0;
|
||||||
|
let new_val = cpp!(unsafe [active_controls as "int", value as "int", max as "int", page_size as "int", dpr as "float"] -> i32 as "int" {
|
||||||
|
switch (active_controls) {
|
||||||
|
case QStyle::SC_ScrollBarAddPage:
|
||||||
|
return value + page_size;
|
||||||
|
case QStyle::SC_ScrollBarSubPage:
|
||||||
|
return value - page_size;
|
||||||
|
case QStyle::SC_ScrollBarAddLine:
|
||||||
|
return value + dpr;
|
||||||
|
case QStyle::SC_ScrollBarSubLine:
|
||||||
|
return value - dpr;
|
||||||
|
case QStyle::SC_ScrollBarFirst:
|
||||||
|
return 0;
|
||||||
|
case QStyle::SC_ScrollBarLast:
|
||||||
|
return max;
|
||||||
|
default:
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
value_prop.set(new_val.max(0).min(max) as f32);
|
||||||
|
InputEventResult::EventIgnored
|
||||||
|
}
|
||||||
|
MouseEventType::MouseMoved => {
|
||||||
|
if data.pressed != 0 && data.active_controls == SC_ScrollBarSlider {
|
||||||
|
let max = max as f32;
|
||||||
|
let new_val = data.pressed_val
|
||||||
|
+ ((pos as f32) - data.pressed_x) * (max + (page_size as f32))
|
||||||
|
/ size as f32;
|
||||||
|
value_prop.set(new_val.max(0.).min(max));
|
||||||
|
InputEventResult::GrabMouse
|
||||||
|
} else {
|
||||||
|
InputEventResult::EventAccepted
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.data.set(data);
|
||||||
|
result
|
||||||
};
|
};
|
||||||
self.data.set(data);
|
|
||||||
result
|
if pressed == 2 || (pressed == 0 && event.pos.x > (size.width as f32 - right)) {
|
||||||
|
handle_scrollbar(
|
||||||
|
false,
|
||||||
|
qttypes::QPoint {
|
||||||
|
x: (event.pos.x - (size.width as f32 - right)) as _,
|
||||||
|
y: (event.pos.y - top) as _,
|
||||||
|
},
|
||||||
|
qttypes::QSize {
|
||||||
|
width: (right - left) as _,
|
||||||
|
height: (size.height as f32 - (bottom + top)) as _,
|
||||||
|
},
|
||||||
|
Self::FIELD_OFFSETS.vertical_value.apply_pin(self),
|
||||||
|
Self::FIELD_OFFSETS.vertical_page_size.apply_pin(self).get() as i32,
|
||||||
|
Self::FIELD_OFFSETS.vertical_max.apply_pin(self).get() as i32,
|
||||||
|
)
|
||||||
|
} else if pressed == 1 || event.pos.y > (size.height as f32 - bottom) {
|
||||||
|
handle_scrollbar(
|
||||||
|
true,
|
||||||
|
qttypes::QPoint {
|
||||||
|
x: (event.pos.x - left) as _,
|
||||||
|
y: (event.pos.y - (size.height as f32 - bottom)) as _,
|
||||||
|
},
|
||||||
|
qttypes::QSize {
|
||||||
|
width: (size.width as f32 - (right + left)) as _,
|
||||||
|
height: (bottom - top) as _,
|
||||||
|
},
|
||||||
|
Self::FIELD_OFFSETS.horizontal_value.apply_pin(self),
|
||||||
|
Self::FIELD_OFFSETS.horizontal_page_size.apply_pin(self).get() as i32,
|
||||||
|
Self::FIELD_OFFSETS.horizontal_max.apply_pin(self).get() as i32,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Default::default()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn key_event(self: Pin<&Self>, _: &KeyEvent, _window: &ComponentWindow) -> KeyEventResult {
|
fn key_event(self: Pin<&Self>, _: &KeyEvent, _window: &ComponentWindow) -> KeyEventResult {
|
||||||
|
@ -1263,12 +1400,12 @@ impl Item for NativeScrollBar {
|
||||||
fn focus_event(self: Pin<&Self>, _: &FocusEvent, _window: &ComponentWindow) {}
|
fn focus_event(self: Pin<&Self>, _: &FocusEvent, _window: &ComponentWindow) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ItemConsts for NativeScrollBar {
|
impl ItemConsts for NativeScrollView {
|
||||||
const cached_rendering_data_offset: const_field_offset::FieldOffset<Self, CachedRenderingData> =
|
const cached_rendering_data_offset: const_field_offset::FieldOffset<Self, CachedRenderingData> =
|
||||||
Self::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection();
|
Self::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection();
|
||||||
}
|
}
|
||||||
|
|
||||||
ItemVTable_static! { #[no_mangle] pub static NativeScrollBarVTable for NativeScrollBar }
|
ItemVTable_static! { #[no_mangle] pub static NativeScrollViewVTable for NativeScrollView }
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(FieldOffsets, Default, BuiltinItem)]
|
#[derive(FieldOffsets, Default, BuiltinItem)]
|
||||||
|
|
|
@ -228,7 +228,7 @@ fn gen_backend_qt(include_dir: &Path) -> anyhow::Result<()> {
|
||||||
"NativeSlider",
|
"NativeSlider",
|
||||||
"NativeGroupBox",
|
"NativeGroupBox",
|
||||||
"NativeLineEdit",
|
"NativeLineEdit",
|
||||||
"NativeScrollBar",
|
"NativeScrollView",
|
||||||
"NativeStandardListViewItem",
|
"NativeStandardListViewItem",
|
||||||
]
|
]
|
||||||
.iter()
|
.iter()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue