mirror of
https://github.com/slint-ui/slint.git
synced 2025-11-03 21:24:17 +00:00
Experimental support for Drag & Drop
Add a `DragArea` and `DropArea` elements. It is currently gated as experimental.
This commit is contained in:
parent
47a556d0e7
commit
3823c1e8da
21 changed files with 610 additions and 113 deletions
|
|
@ -373,6 +373,7 @@ if (SLINT_BUILD_RUNTIME)
|
|||
${CMAKE_CURRENT_BINARY_DIR}/generated_include/slint_sharedvector_internal.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/generated_include/slint_string_internal.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/generated_include/slint_timer_internal.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/generated_include/slint_events_internal.h
|
||||
)
|
||||
|
||||
if(SLINT_FEATURE_INTERPRETER)
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@ fn builtin_structs(path: &Path) -> anyhow::Result<()> {
|
|||
writeln!(structs_priv, "// This file is auto-generated from {}", file!())?;
|
||||
writeln!(structs_priv, "#include \"slint_builtin_structs.h\"")?;
|
||||
writeln!(structs_priv, "#include \"slint_enums_internal.h\"")?;
|
||||
writeln!(structs_priv, "#include \"slint_point.h\"")?;
|
||||
writeln!(structs_priv, "namespace slint::cbindgen_private {{")?;
|
||||
writeln!(structs_priv, "enum class KeyEventType : uint8_t;")?;
|
||||
macro_rules! struct_file {
|
||||
|
|
@ -218,6 +219,7 @@ fn default_config() -> cbindgen::Config {
|
|||
("FocusReasonArg".into(), "FocusReason".into()),
|
||||
("KeyEventArg".into(), "KeyEvent".into()),
|
||||
("PointerEventArg".into(), "PointerEvent".into()),
|
||||
("DropEventArg".into(), "DropEvent".into()),
|
||||
("PointerScrollEventArg".into(), "PointerScrollEvent".into()),
|
||||
("PointArg".into(), "slint::LogicalPosition".into()),
|
||||
("FloatArg".into(), "float".into()),
|
||||
|
|
@ -286,6 +288,8 @@ fn gen_corelib(
|
|||
"Rectangle",
|
||||
"BasicBorderRectangle",
|
||||
"BorderRectangle",
|
||||
"DragArea",
|
||||
"DropArea",
|
||||
"ImageItem",
|
||||
"ClippedImage",
|
||||
"TouchArea",
|
||||
|
|
@ -368,7 +372,6 @@ fn gen_corelib(
|
|||
"Property",
|
||||
"Slice",
|
||||
"Timer",
|
||||
"TimerMode",
|
||||
"PropertyHandleOpaque",
|
||||
"Callback",
|
||||
"slint_property_listener_scope_evaluate",
|
||||
|
|
@ -377,6 +380,7 @@ fn gen_corelib(
|
|||
"CallbackOpaque",
|
||||
"WindowAdapterRc",
|
||||
"VoidArg",
|
||||
"DropEventArg",
|
||||
"FocusReasonArg",
|
||||
"KeyEventArg",
|
||||
"PointerEventArg",
|
||||
|
|
@ -385,29 +389,6 @@ fn gen_corelib(
|
|||
"Point",
|
||||
"MenuEntryModel",
|
||||
"MenuEntryArg",
|
||||
"slint_color_brighter",
|
||||
"slint_color_darker",
|
||||
"slint_color_transparentize",
|
||||
"slint_color_mix",
|
||||
"slint_color_with_alpha",
|
||||
"slint_color_to_hsva",
|
||||
"slint_color_from_hsva",
|
||||
"slint_image_size",
|
||||
"slint_image_path",
|
||||
"slint_image_load_from_path",
|
||||
"slint_image_load_from_embedded_data",
|
||||
"slint_image_from_embedded_textures",
|
||||
"slint_image_compare_equal",
|
||||
"slint_image_set_nine_slice_edges",
|
||||
"slint_image_to_rgb8",
|
||||
"slint_image_to_rgba8",
|
||||
"slint_image_to_rgba8_premultiplied",
|
||||
"slint_timer_start",
|
||||
"slint_timer_singleshot",
|
||||
"slint_timer_destroy",
|
||||
"slint_timer_stop",
|
||||
"slint_timer_restart",
|
||||
"slint_timer_running",
|
||||
"Coord",
|
||||
"LogicalRect",
|
||||
"LogicalPoint",
|
||||
|
|
@ -483,15 +464,9 @@ fn gen_corelib(
|
|||
.iter()
|
||||
.map(|s| s.to_string())
|
||||
.collect();
|
||||
tmp.export.exclude = config
|
||||
.export
|
||||
.exclude
|
||||
.iter()
|
||||
.filter(|exclusion| !tmp.export.include.iter().any(|inclusion| inclusion == *exclusion))
|
||||
.cloned()
|
||||
.collect();
|
||||
tmp
|
||||
};
|
||||
config.export.exclude.extend(timer_config.export.include.iter().cloned());
|
||||
cbindgen::Builder::new()
|
||||
.with_config(timer_config)
|
||||
.with_src(crate_dir.join("timers.rs"))
|
||||
|
|
@ -499,7 +474,7 @@ fn gen_corelib(
|
|||
.context("Unable to generate bindings for slint_timer_internal.h")?
|
||||
.write_to_file(include_dir.join("slint_timer_internal.h"));
|
||||
|
||||
for (rust_types, extra_excluded_types, internal_header, prelude) in [
|
||||
for (rust_types, internal_header, prelude) in [
|
||||
(
|
||||
vec![
|
||||
"ImageInner",
|
||||
|
|
@ -521,7 +496,6 @@ fn gen_corelib(
|
|||
"StaticTextures",
|
||||
"BorrowedOpenGLTextureOrigin"
|
||||
],
|
||||
vec!["Color"],
|
||||
"slint_image_internal.h",
|
||||
"namespace slint::cbindgen_private { struct ParsedSVG{}; struct HTMLImage{}; using namespace vtable; namespace types{ struct NineSliceImage{}; } }",
|
||||
),
|
||||
|
|
@ -532,22 +506,31 @@ fn gen_corelib(
|
|||
"slint_color_with_alpha",
|
||||
"slint_color_to_hsva",
|
||||
"slint_color_from_hsva",],
|
||||
vec![],
|
||||
"slint_color_internal.h",
|
||||
"",
|
||||
),
|
||||
(
|
||||
vec!["PathData", "PathElement", "slint_new_path_elements", "slint_new_path_events"],
|
||||
vec![],
|
||||
vec!["PathData", "PathElement", "slint_new_path_elements", "slint_new_path_events", "Point"],
|
||||
"slint_pathdata_internal.h",
|
||||
"",
|
||||
"#include \"slint_sharedvector.h\"\n#include \"slint_point.h\"",
|
||||
),
|
||||
(
|
||||
vec!["Brush", "LinearGradient", "GradientStop", "RadialGradient"],
|
||||
vec!["Color"],
|
||||
"slint_brush_internal.h",
|
||||
"",
|
||||
),
|
||||
(
|
||||
vec!["MouseEvent"],
|
||||
"slint_events_internal.h",
|
||||
"#include \"slint_point.h\"
|
||||
namespace slint::cbindgen_private {
|
||||
struct KeyEvent; struct PointerEvent;
|
||||
struct Rect;
|
||||
using LogicalRect = Rect;
|
||||
using LogicalPoint = Point2D<float>;
|
||||
using LogicalLength = float;
|
||||
}",
|
||||
)
|
||||
]
|
||||
.iter()
|
||||
{
|
||||
|
|
@ -591,33 +574,15 @@ fn gen_corelib(
|
|||
"slint_windowrc_is_minimized",
|
||||
"slint_windowrc_is_maximized",
|
||||
"slint_windowrc_take_snapshot",
|
||||
"slint_new_path_elements",
|
||||
"slint_new_path_events",
|
||||
"slint_color_brighter",
|
||||
"slint_color_darker",
|
||||
"slint_color_transparentize",
|
||||
"slint_color_mix",
|
||||
"slint_color_with_alpha",
|
||||
"slint_color_to_hsva",
|
||||
"slint_color_from_hsva",
|
||||
"slint_image_size",
|
||||
"slint_image_path",
|
||||
"slint_image_load_from_path",
|
||||
"slint_image_load_from_embedded_data",
|
||||
"slint_image_set_nine_slice_edges",
|
||||
"slint_image_to_rgb8",
|
||||
"slint_image_to_rgba8",
|
||||
"slint_image_to_rgba8_premultiplied",
|
||||
"slint_image_from_embedded_textures",
|
||||
"slint_image_compare_equal",
|
||||
]
|
||||
.iter()
|
||||
.filter(|exclusion| !rust_types.iter().any(|inclusion| inclusion == *exclusion))
|
||||
.chain(extra_excluded_types.iter())
|
||||
.chain(public_exported_types.iter())
|
||||
.into_iter()
|
||||
.chain(config.export.exclude.iter().map(|s| s.as_str()))
|
||||
.filter(|exclusion| !rust_types.iter().any(|inclusion| inclusion == exclusion))
|
||||
.map(|s| s.to_string())
|
||||
.collect();
|
||||
|
||||
config.export.exclude.extend(rust_types.iter().map(|s| s.to_string()));
|
||||
|
||||
special_config.enumeration = cbindgen::EnumConfig {
|
||||
derive_tagged_enum_copy_assignment: true,
|
||||
derive_tagged_enum_copy_constructor: true,
|
||||
|
|
@ -646,7 +611,7 @@ fn gen_corelib(
|
|||
.with_src(crate_dir.join("graphics/image.rs"))
|
||||
.with_src(crate_dir.join("graphics/image/cache.rs"))
|
||||
.with_src(crate_dir.join("animations.rs"))
|
||||
// .with_src(crate_dir.join("input.rs"))
|
||||
.with_src(crate_dir.join("input.rs"))
|
||||
.with_src(crate_dir.join("item_rendering.rs"))
|
||||
.with_src(crate_dir.join("window.rs"))
|
||||
.with_include("slint_enums_internal.h")
|
||||
|
|
@ -759,6 +724,7 @@ fn gen_corelib(
|
|||
.with_include("slint_point.h")
|
||||
.with_include("slint_timer.h")
|
||||
.with_include("slint_builtin_structs_internal.h")
|
||||
.with_include("slint_events_internal.h")
|
||||
.with_after_include(
|
||||
r"
|
||||
namespace slint {
|
||||
|
|
@ -766,17 +732,14 @@ namespace slint {
|
|||
namespace cbindgen_private {
|
||||
using slint::private_api::WindowAdapterRc;
|
||||
using namespace vtable;
|
||||
struct KeyEvent; struct PointerEvent;
|
||||
using private_api::Property;
|
||||
using private_api::PathData;
|
||||
using private_api::Point;
|
||||
struct Rect;
|
||||
using LogicalRect = Rect;
|
||||
using LogicalPoint = Point2D<float>;
|
||||
using LogicalLength = float;
|
||||
struct ItemTreeVTable;
|
||||
struct ItemVTable;
|
||||
using types::IntRect;
|
||||
using types::Size;
|
||||
using types::MouseEvent;
|
||||
}
|
||||
template<typename ModelData> class Model;
|
||||
}",
|
||||
|
|
|
|||
|
|
@ -499,12 +499,8 @@ public:
|
|||
void dispatch_pointer_press_event(LogicalPosition pos, PointerEventButton button)
|
||||
{
|
||||
private_api::assert_main_thread();
|
||||
using slint::cbindgen_private::MouseEvent;
|
||||
MouseEvent event { .tag = MouseEvent::Tag::Pressed,
|
||||
.pressed = MouseEvent::Pressed_Body { .position = { pos.x, pos.y },
|
||||
.button = button,
|
||||
.click_count = 0 } };
|
||||
inner.dispatch_pointer_event(event);
|
||||
inner.dispatch_pointer_event(
|
||||
slint::cbindgen_private::MouseEvent::Pressed({ pos.x, pos.y }, button, 0));
|
||||
}
|
||||
/// Dispatches a pointer or mouse release event to the scene.
|
||||
///
|
||||
|
|
@ -516,12 +512,8 @@ public:
|
|||
void dispatch_pointer_release_event(LogicalPosition pos, PointerEventButton button)
|
||||
{
|
||||
private_api::assert_main_thread();
|
||||
using slint::cbindgen_private::MouseEvent;
|
||||
MouseEvent event { .tag = MouseEvent::Tag::Released,
|
||||
.released = MouseEvent::Released_Body { .position = { pos.x, pos.y },
|
||||
.button = button,
|
||||
.click_count = 0 } };
|
||||
inner.dispatch_pointer_event(event);
|
||||
inner.dispatch_pointer_event(
|
||||
slint::cbindgen_private::MouseEvent::Released({ pos.x, pos.y }, button, 0));
|
||||
}
|
||||
/// Dispatches a pointer exit event to the scene.
|
||||
///
|
||||
|
|
@ -532,9 +524,7 @@ public:
|
|||
void dispatch_pointer_exit_event()
|
||||
{
|
||||
private_api::assert_main_thread();
|
||||
using slint::cbindgen_private::MouseEvent;
|
||||
MouseEvent event { .tag = MouseEvent::Tag::Exit, .moved = {} };
|
||||
inner.dispatch_pointer_event(event);
|
||||
inner.dispatch_pointer_event(slint::cbindgen_private::MouseEvent::Exit());
|
||||
}
|
||||
|
||||
/// Dispatches a pointer move event to the scene.
|
||||
|
|
@ -546,10 +536,7 @@ public:
|
|||
void dispatch_pointer_move_event(LogicalPosition pos)
|
||||
{
|
||||
private_api::assert_main_thread();
|
||||
using slint::cbindgen_private::MouseEvent;
|
||||
MouseEvent event { .tag = MouseEvent::Tag::Moved,
|
||||
.moved = MouseEvent::Moved_Body { .position = { pos.x, pos.y } } };
|
||||
inner.dispatch_pointer_event(event);
|
||||
inner.dispatch_pointer_event(slint::cbindgen_private::MouseEvent::Moved({ pos.x, pos.y }));
|
||||
}
|
||||
|
||||
/// Dispatches a scroll (or wheel) event to the scene.
|
||||
|
|
@ -563,12 +550,8 @@ public:
|
|||
void dispatch_pointer_scroll_event(LogicalPosition pos, float delta_x, float delta_y)
|
||||
{
|
||||
private_api::assert_main_thread();
|
||||
using slint::cbindgen_private::MouseEvent;
|
||||
MouseEvent event { .tag = MouseEvent::Tag::Wheel,
|
||||
.wheel = MouseEvent::Wheel_Body { .position = { pos.x, pos.y },
|
||||
.delta_x = delta_x,
|
||||
.delta_y = delta_y } };
|
||||
inner.dispatch_pointer_event(event);
|
||||
inner.dispatch_pointer_event(
|
||||
slint::cbindgen_private::MouseEvent::Wheel({ pos.x, pos.y }, delta_x, delta_y));
|
||||
}
|
||||
|
||||
/// Set the logical size of this window after a resize event
|
||||
|
|
|
|||
|
|
@ -272,6 +272,9 @@ impl Item for NativeButton {
|
|||
}
|
||||
}
|
||||
MouseEvent::Wheel { .. } => return InputEventResult::EventIgnored,
|
||||
MouseEvent::DragMove(..) | MouseEvent::Drop(..) => {
|
||||
return InputEventResult::EventIgnored
|
||||
}
|
||||
});
|
||||
if let MouseEvent::Released { position, .. } = event {
|
||||
let geo = self_rc.geometry();
|
||||
|
|
|
|||
|
|
@ -225,6 +225,7 @@ impl Item for NativeScrollView {
|
|||
}
|
||||
InputEventResult::EventAccepted
|
||||
}
|
||||
MouseEvent::DragMove(..) | MouseEvent::Drop(..) => InputEventResult::EventIgnored,
|
||||
};
|
||||
self.data.set(data);
|
||||
result
|
||||
|
|
|
|||
|
|
@ -251,6 +251,7 @@ impl Item for NativeSlider {
|
|||
debug_assert_ne!(*button, PointerEventButton::Left);
|
||||
InputEventResult::EventIgnored
|
||||
}
|
||||
MouseEvent::DragMove(..) | MouseEvent::Drop(..) => InputEventResult::EventIgnored,
|
||||
};
|
||||
data.active_controls = new_control;
|
||||
|
||||
|
|
|
|||
|
|
@ -224,6 +224,7 @@ impl Item for NativeSpinBox {
|
|||
|
||||
true
|
||||
}
|
||||
MouseEvent::DragMove(..) | MouseEvent::Drop(..) => false,
|
||||
};
|
||||
data.active_controls = new_control;
|
||||
if changed {
|
||||
|
|
|
|||
|
|
@ -451,6 +451,9 @@ impl Item for NativeTab {
|
|||
}
|
||||
}
|
||||
MouseEvent::Wheel { .. } => return InputEventResult::EventIgnored,
|
||||
MouseEvent::DragMove(..) | MouseEvent::Drop(..) => {
|
||||
return InputEventResult::EventIgnored
|
||||
}
|
||||
});
|
||||
let click_on_press = cpp!(unsafe [] -> bool as "bool" {
|
||||
return qApp->style()->styleHint(QStyle::SH_TabBar_SelectMouseType, nullptr, nullptr) == QEvent::MouseButtonPress;
|
||||
|
|
|
|||
|
|
@ -115,6 +115,21 @@ macro_rules! for_each_builtin_structs {
|
|||
}
|
||||
}
|
||||
|
||||
/// This structure is passed to the callbacks of the `DropArea` element
|
||||
struct DropEvent {
|
||||
@name = "slint::private_api::DropEvent"
|
||||
export {
|
||||
/// The mime type of the data being dragged
|
||||
mime_type: SharedString,
|
||||
/// The data being dragged
|
||||
data: SharedString,
|
||||
/// The current mouse position in coordinates of the `DropArea` element
|
||||
position: LogicalPosition,
|
||||
}
|
||||
private {
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents an item in a StandardListView and a StandardTableView.
|
||||
#[non_exhaustive]
|
||||
struct StandardListViewItem {
|
||||
|
|
|
|||
|
|
@ -186,6 +186,22 @@ export component SwipeGestureHandler {
|
|||
//-default_size_binding:expands_to_parent_geometry
|
||||
}
|
||||
|
||||
export component DragArea {
|
||||
in property <bool> enabled: true;
|
||||
//out property <bool> dragging;
|
||||
in property <string> mime-type;
|
||||
in property <string> data;
|
||||
//-default_size_binding:expands_to_parent_geometry
|
||||
|
||||
}
|
||||
export component DropArea {
|
||||
in property <bool> enabled: true;
|
||||
callback can-drop(event: DropEvent) -> bool;
|
||||
callback dropped(event: DropEvent);
|
||||
out property <bool> contains-drag;
|
||||
//-default_size_binding:expands_to_parent_geometry
|
||||
}
|
||||
|
||||
component MenuItem {
|
||||
in property <string> title;
|
||||
callback activated();
|
||||
|
|
|
|||
|
|
@ -408,6 +408,7 @@ impl TypeRegister {
|
|||
($pub_type:ident, SharedString) => { Type::String };
|
||||
($pub_type:ident, Image) => { Type::Image };
|
||||
($pub_type:ident, Coord) => { Type::LogicalLength };
|
||||
($pub_type:ident, LogicalPosition) => { logical_point_type() };
|
||||
($pub_type:ident, KeyboardModifiers) => { $pub_type.clone() };
|
||||
($pub_type:ident, $_:ident) => {
|
||||
BUILTIN.with(|e| Type::Enumeration(e.enums.$pub_type.clone()))
|
||||
|
|
@ -547,8 +548,12 @@ impl TypeRegister {
|
|||
pub fn builtin() -> Rc<RefCell<Self>> {
|
||||
let mut register = Self::builtin_internal();
|
||||
|
||||
register.elements.remove("ComponentContainer");
|
||||
register.types.remove("component-factory");
|
||||
register.elements.remove("ComponentContainer").unwrap();
|
||||
register.types.remove("component-factory").unwrap();
|
||||
|
||||
register.elements.remove("DragArea").unwrap();
|
||||
register.elements.remove("DropArea").unwrap();
|
||||
register.types.remove("DropEvent").unwrap(); // Also removed in xtask/src/slintdocs.rs
|
||||
|
||||
Rc::new(RefCell::new(register))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@
|
|||
use crate::item_tree::ItemTreeRc;
|
||||
use crate::item_tree::{ItemRc, ItemWeak, VisitChildrenResult};
|
||||
pub use crate::items::PointerEventButton;
|
||||
use crate::items::{DropEvent, ItemRef, TextCursorDirection};
|
||||
pub use crate::items::{FocusReason, KeyEvent, KeyboardModifiers};
|
||||
use crate::items::{ItemRef, TextCursorDirection};
|
||||
use crate::lengths::{LogicalPoint, LogicalVector};
|
||||
use crate::timers::Timer;
|
||||
use crate::window::{WindowAdapter, WindowInner};
|
||||
|
|
@ -26,7 +26,7 @@ use core::time::Duration;
|
|||
/// The only difference with [`crate::platform::WindowEvent`] us that it uses untyped `Point`
|
||||
/// TODO: merge with platform::WindowEvent
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum MouseEvent {
|
||||
/// The mouse or finger was pressed
|
||||
|
|
@ -46,6 +46,12 @@ pub enum MouseEvent {
|
|||
/// `delta_x` is the amount of pixels to scroll in horizontal direction,
|
||||
/// `delta_y` is the amount of pixels to scroll in vertical direction.
|
||||
Wheel { position: LogicalPoint, delta_x: Coord, delta_y: Coord },
|
||||
/// The mouse is being dragged over this item.
|
||||
/// [`InputEventResult::EventIgnored`] means that the item does not handle the drag operation
|
||||
/// and [`InputEventResult::EventAccepted`] means that the item can accept it.
|
||||
DragMove(DropEvent),
|
||||
/// The mouse is released while dregging over this item.
|
||||
Drop(DropEvent),
|
||||
/// The mouse exited the item or component
|
||||
Exit,
|
||||
}
|
||||
|
|
@ -58,6 +64,9 @@ impl MouseEvent {
|
|||
MouseEvent::Released { position, .. } => Some(*position),
|
||||
MouseEvent::Moved { position } => Some(*position),
|
||||
MouseEvent::Wheel { position, .. } => Some(*position),
|
||||
MouseEvent::DragMove(e) | MouseEvent::Drop(e) => {
|
||||
Some(crate::lengths::logical_point_from_api(e.position))
|
||||
}
|
||||
MouseEvent::Exit => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -69,6 +78,12 @@ impl MouseEvent {
|
|||
MouseEvent::Released { position, .. } => Some(position),
|
||||
MouseEvent::Moved { position } => Some(position),
|
||||
MouseEvent::Wheel { position, .. } => Some(position),
|
||||
MouseEvent::DragMove(e) | MouseEvent::Drop(e) => {
|
||||
e.position = crate::api::LogicalPosition::from_euclid(
|
||||
crate::lengths::logical_point_from_api(e.position) + vec,
|
||||
);
|
||||
None
|
||||
}
|
||||
MouseEvent::Exit => None,
|
||||
};
|
||||
if let Some(pos) = pos {
|
||||
|
|
@ -102,6 +117,8 @@ pub enum InputEventResult {
|
|||
EventIgnored,
|
||||
/// All further mouse event need to be sent to this item or component
|
||||
GrabMouse,
|
||||
/// Will start a drag operation. Can only be returned from a [`crate::items::DragArea`] item.
|
||||
StartDrag,
|
||||
}
|
||||
|
||||
/// This value is returned by the `input_event_filter_before_children` function, which
|
||||
|
|
@ -540,6 +557,9 @@ pub struct MouseInputState {
|
|||
pub(crate) offset: LogicalPoint,
|
||||
/// true if the top item of the stack has the mouse grab
|
||||
grabbed: bool,
|
||||
/// When this is Some, it means we are in the middle of a drag-drop operation and it contains the dragged data.
|
||||
/// The `position` field has no signification
|
||||
pub(crate) drag_data: Option<DropEvent>,
|
||||
delayed: Option<(crate::timers::Timer, MouseEvent)>,
|
||||
delayed_exit_items: Vec<ItemWeak>,
|
||||
}
|
||||
|
|
@ -613,16 +633,27 @@ pub(crate) fn handle_mouse_grab(
|
|||
|
||||
let grabber = mouse_input_state.top_item().unwrap();
|
||||
let input_result = grabber.borrow().as_ref().input_event(&event, window_adapter, &grabber);
|
||||
if input_result != InputEventResult::GrabMouse {
|
||||
mouse_input_state.grabbed = false;
|
||||
// Return a move event so that the new position can be registered properly
|
||||
Some(
|
||||
mouse_event
|
||||
.position()
|
||||
.map_or(MouseEvent::Exit, |position| MouseEvent::Moved { position }),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
match input_result {
|
||||
InputEventResult::GrabMouse => None,
|
||||
InputEventResult::StartDrag => {
|
||||
mouse_input_state.grabbed = false;
|
||||
let drag_area_item = grabber.downcast::<crate::items::DragArea>().unwrap();
|
||||
mouse_input_state.drag_data = Some(DropEvent {
|
||||
mime_type: drag_area_item.as_pin_ref().mime_type(),
|
||||
data: drag_area_item.as_pin_ref().data(),
|
||||
position: Default::default(),
|
||||
});
|
||||
None
|
||||
}
|
||||
_ => {
|
||||
mouse_input_state.grabbed = false;
|
||||
// Return a move event so that the new position can be registered properly
|
||||
Some(
|
||||
mouse_event
|
||||
.position()
|
||||
.map_or(MouseEvent::Exit, |position| MouseEvent::Moved { position }),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -671,6 +702,7 @@ pub fn process_mouse_input(
|
|||
mouse_input_state: MouseInputState,
|
||||
) -> MouseInputState {
|
||||
let mut result = MouseInputState::default();
|
||||
result.drag_data = mouse_input_state.drag_data.clone();
|
||||
let r = send_mouse_event_to_item(
|
||||
mouse_event,
|
||||
root.clone(),
|
||||
|
|
@ -841,6 +873,18 @@ fn send_mouse_event_to_item(
|
|||
result.grabbed = true;
|
||||
VisitChildrenResult::abort(item_rc.index(), 0)
|
||||
}
|
||||
InputEventResult::StartDrag => {
|
||||
result.item_stack.last_mut().unwrap().1 =
|
||||
InputEventFilterResult::ForwardAndInterceptGrab;
|
||||
result.grabbed = false;
|
||||
let drag_area_item = item_rc.downcast::<crate::items::DragArea>().unwrap();
|
||||
result.drag_data = Some(DropEvent {
|
||||
mime_type: drag_area_item.as_pin_ref().mime_type(),
|
||||
data: drag_area_item.as_pin_ref().data(),
|
||||
position: Default::default(),
|
||||
});
|
||||
VisitChildrenResult::abort(item_rc.index(), 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ When adding an item or a property, it needs to be kept in sync with different pl
|
|||
#![allow(non_upper_case_globals)]
|
||||
#![allow(missing_docs)] // because documenting each property of items is redundant
|
||||
|
||||
use crate::api::LogicalPosition;
|
||||
use crate::graphics::{Brush, Color, FontRequest};
|
||||
use crate::input::{
|
||||
FocusEvent, FocusEventResult, InputEventFilterResult, InputEventResult, KeyEventResult,
|
||||
|
|
@ -32,6 +33,7 @@ use crate::lengths::{
|
|||
LogicalBorderRadius, LogicalLength, LogicalRect, LogicalSize, LogicalVector, PointLengths,
|
||||
RectLengths,
|
||||
};
|
||||
pub use crate::menus::MenuItem;
|
||||
#[cfg(feature = "rtti")]
|
||||
use crate::rtti::*;
|
||||
use crate::window::{WindowAdapter, WindowAdapterRc, WindowInner};
|
||||
|
|
@ -54,9 +56,10 @@ mod input_items;
|
|||
pub use input_items::*;
|
||||
mod image;
|
||||
pub use self::image::*;
|
||||
mod drag_n_drop;
|
||||
pub use drag_n_drop::*;
|
||||
#[cfg(feature = "std")]
|
||||
mod path;
|
||||
pub use crate::menus::MenuItem;
|
||||
#[cfg(feature = "std")]
|
||||
pub use path::*;
|
||||
|
||||
|
|
@ -70,7 +73,7 @@ pub type KeyEventArg = (KeyEvent,);
|
|||
type FocusReasonArg = (FocusReason,);
|
||||
type PointerEventArg = (PointerEvent,);
|
||||
type PointerScrollEventArg = (PointerScrollEvent,);
|
||||
type PointArg = (crate::api::LogicalPosition,);
|
||||
type PointArg = (LogicalPosition,);
|
||||
type MenuEntryArg = (MenuEntry,);
|
||||
type MenuEntryModel = crate::model::ModelRc<MenuEntry>;
|
||||
|
||||
|
|
@ -1047,6 +1050,14 @@ declare_item_vtable! {
|
|||
fn slint_get_FlickableVTable() -> FlickableVTable for Flickable
|
||||
}
|
||||
|
||||
declare_item_vtable! {
|
||||
fn slint_get_DragAreaVTable() -> DragAreaVTable for DragArea
|
||||
}
|
||||
|
||||
declare_item_vtable! {
|
||||
fn slint_get_DropAreaVTable() -> DropAreaVTable for DropArea
|
||||
}
|
||||
|
||||
/// The implementation of the `PropertyAnimation` element
|
||||
#[repr(C)]
|
||||
#[derive(FieldOffsets, SlintElement, Clone, Debug)]
|
||||
|
|
@ -1349,7 +1360,7 @@ impl Item for ContextMenu {
|
|||
}
|
||||
match event {
|
||||
MouseEvent::Pressed { position, button: PointerEventButton::Right, .. } => {
|
||||
self.show.call(&(crate::api::LogicalPosition::from_euclid(position),));
|
||||
self.show.call(&(LogicalPosition::from_euclid(*position),));
|
||||
InputEventResult::EventAccepted
|
||||
}
|
||||
#[cfg(target_os = "android")]
|
||||
|
|
|
|||
312
internal/core/items/drag_n_drop.rs
Normal file
312
internal/core/items/drag_n_drop.rs
Normal file
|
|
@ -0,0 +1,312 @@
|
|||
// 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 super::{
|
||||
DropEvent, Item, ItemConsts, ItemRc, MouseCursor, PointerEventButton, RenderingResult,
|
||||
};
|
||||
use crate::input::{
|
||||
FocusEvent, FocusEventResult, InputEventFilterResult, InputEventResult, KeyEvent,
|
||||
KeyEventResult, MouseEvent,
|
||||
};
|
||||
use crate::item_rendering::{CachedRenderingData, ItemRenderer};
|
||||
use crate::layout::{LayoutInfo, Orientation};
|
||||
use crate::lengths::{LogicalPoint, LogicalRect, LogicalSize};
|
||||
#[cfg(feature = "rtti")]
|
||||
use crate::rtti::*;
|
||||
use crate::window::WindowAdapter;
|
||||
use crate::{Callback, Property, SharedString};
|
||||
use alloc::rc::Rc;
|
||||
use const_field_offset::FieldOffsets;
|
||||
use core::cell::Cell;
|
||||
use core::pin::Pin;
|
||||
use i_slint_core_macros::*;
|
||||
|
||||
pub type DropEventArg = (DropEvent,);
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(FieldOffsets, Default, SlintElement)]
|
||||
#[pin]
|
||||
/// The implementation of the `DragArea` element
|
||||
pub struct DragArea {
|
||||
pub enabled: Property<bool>,
|
||||
pub mime_type: Property<SharedString>,
|
||||
pub data: Property<SharedString>,
|
||||
pressed: Cell<bool>,
|
||||
pressed_position: Cell<LogicalPoint>,
|
||||
pub cached_rendering_data: CachedRenderingData,
|
||||
}
|
||||
|
||||
impl Item for DragArea {
|
||||
fn init(self: Pin<&Self>, _self_rc: &ItemRc) {}
|
||||
|
||||
fn layout_info(
|
||||
self: Pin<&Self>,
|
||||
_: Orientation,
|
||||
_window_adapter: &Rc<dyn WindowAdapter>,
|
||||
_self_rc: &ItemRc,
|
||||
) -> LayoutInfo {
|
||||
LayoutInfo { stretch: 1., ..LayoutInfo::default() }
|
||||
}
|
||||
|
||||
fn input_event_filter_before_children(
|
||||
self: Pin<&Self>,
|
||||
event: &MouseEvent,
|
||||
_window_adapter: &Rc<dyn WindowAdapter>,
|
||||
_self_rc: &ItemRc,
|
||||
) -> InputEventFilterResult {
|
||||
if !self.enabled() {
|
||||
self.cancel();
|
||||
return InputEventFilterResult::ForwardAndIgnore;
|
||||
}
|
||||
|
||||
match event {
|
||||
MouseEvent::Pressed { position, button: PointerEventButton::Left, .. } => {
|
||||
self.pressed_position.set(*position);
|
||||
self.pressed.set(true);
|
||||
InputEventFilterResult::ForwardAndInterceptGrab
|
||||
}
|
||||
MouseEvent::Exit => {
|
||||
self.cancel();
|
||||
InputEventFilterResult::ForwardAndIgnore
|
||||
}
|
||||
MouseEvent::Released { button: PointerEventButton::Left, .. } => {
|
||||
self.pressed.set(false);
|
||||
InputEventFilterResult::ForwardAndIgnore
|
||||
}
|
||||
|
||||
MouseEvent::Moved { position } => {
|
||||
if !self.pressed.get() {
|
||||
InputEventFilterResult::ForwardEvent
|
||||
} else {
|
||||
let pressed_pos = self.pressed_position.get();
|
||||
let dx = (position.x - pressed_pos.x).abs();
|
||||
let dy = (position.y - pressed_pos.y).abs();
|
||||
let threshold = super::flickable::DISTANCE_THRESHOLD.get();
|
||||
if dy > threshold || dx > threshold {
|
||||
InputEventFilterResult::Intercept
|
||||
} else {
|
||||
InputEventFilterResult::ForwardAndInterceptGrab
|
||||
}
|
||||
}
|
||||
}
|
||||
MouseEvent::Wheel { .. } => InputEventFilterResult::ForwardAndIgnore,
|
||||
// Not the left button
|
||||
MouseEvent::Pressed { .. } | MouseEvent::Released { .. } => {
|
||||
InputEventFilterResult::ForwardAndIgnore
|
||||
}
|
||||
MouseEvent::DragMove(..) | MouseEvent::Drop(..) => {
|
||||
InputEventFilterResult::ForwardAndIgnore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn input_event(
|
||||
self: Pin<&Self>,
|
||||
event: &MouseEvent,
|
||||
_window_adapter: &Rc<dyn WindowAdapter>,
|
||||
_self_rc: &ItemRc,
|
||||
) -> InputEventResult {
|
||||
match event {
|
||||
MouseEvent::Pressed { .. } => InputEventResult::EventAccepted,
|
||||
MouseEvent::Exit => {
|
||||
self.cancel();
|
||||
InputEventResult::EventIgnored
|
||||
}
|
||||
MouseEvent::Released { .. } => {
|
||||
self.cancel();
|
||||
InputEventResult::EventIgnored
|
||||
}
|
||||
MouseEvent::Moved { position } => {
|
||||
if !self.pressed.get() || !self.enabled() {
|
||||
return InputEventResult::EventIgnored;
|
||||
}
|
||||
let pressed_pos = self.pressed_position.get();
|
||||
let dx = (position.x - pressed_pos.x).abs();
|
||||
let dy = (position.y - pressed_pos.y).abs();
|
||||
let threshold = super::flickable::DISTANCE_THRESHOLD.get();
|
||||
let start_drag = dx > threshold || dy > threshold;
|
||||
if start_drag {
|
||||
self.pressed.set(false);
|
||||
InputEventResult::StartDrag
|
||||
} else {
|
||||
InputEventResult::EventAccepted
|
||||
}
|
||||
}
|
||||
MouseEvent::Wheel { .. } => InputEventResult::EventIgnored,
|
||||
MouseEvent::DragMove(..) | MouseEvent::Drop(..) => 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(
|
||||
self: Pin<&Self>,
|
||||
_: &mut &mut dyn ItemRenderer,
|
||||
_self_rc: &ItemRc,
|
||||
_size: LogicalSize,
|
||||
) -> RenderingResult {
|
||||
RenderingResult::ContinueRenderingChildren
|
||||
}
|
||||
|
||||
fn bounding_rect(
|
||||
self: core::pin::Pin<&Self>,
|
||||
_window_adapter: &Rc<dyn WindowAdapter>,
|
||||
_self_rc: &ItemRc,
|
||||
mut geometry: LogicalRect,
|
||||
) -> LogicalRect {
|
||||
geometry.size = LogicalSize::zero();
|
||||
geometry
|
||||
}
|
||||
|
||||
fn clips_children(self: core::pin::Pin<&Self>) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl ItemConsts for DragArea {
|
||||
const cached_rendering_data_offset: const_field_offset::FieldOffset<
|
||||
DragArea,
|
||||
CachedRenderingData,
|
||||
> = DragArea::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection();
|
||||
}
|
||||
|
||||
impl DragArea {
|
||||
fn cancel(self: Pin<&Self>) {
|
||||
self.pressed.set(false)
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(FieldOffsets, Default, SlintElement)]
|
||||
#[pin]
|
||||
/// The implementation of the `DropArea` element
|
||||
pub struct DropArea {
|
||||
pub enabled: Property<bool>,
|
||||
pub contains_drag: Property<bool>,
|
||||
pub can_drop: Callback<DropEventArg, bool>,
|
||||
pub dropped: Callback<DropEventArg>,
|
||||
|
||||
pub cached_rendering_data: CachedRenderingData,
|
||||
}
|
||||
|
||||
impl Item for DropArea {
|
||||
fn init(self: Pin<&Self>, _self_rc: &ItemRc) {}
|
||||
|
||||
fn layout_info(
|
||||
self: Pin<&Self>,
|
||||
_: Orientation,
|
||||
_window_adapter: &Rc<dyn WindowAdapter>,
|
||||
_self_rc: &ItemRc,
|
||||
) -> LayoutInfo {
|
||||
LayoutInfo { stretch: 1., ..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: &ItemRc,
|
||||
) -> InputEventResult {
|
||||
if !self.enabled() {
|
||||
return InputEventResult::EventIgnored;
|
||||
}
|
||||
match event {
|
||||
MouseEvent::DragMove(event) => {
|
||||
let r = Self::FIELD_OFFSETS.can_drop.apply_pin(self).call(&(event.clone(),));
|
||||
if r {
|
||||
self.contains_drag.set(true);
|
||||
if let Some(window_adapter) = window_adapter.internal(crate::InternalToken) {
|
||||
window_adapter.set_mouse_cursor(MouseCursor::Copy);
|
||||
}
|
||||
InputEventResult::EventAccepted
|
||||
} else {
|
||||
self.contains_drag.set(false);
|
||||
InputEventResult::EventIgnored
|
||||
}
|
||||
}
|
||||
MouseEvent::Drop(event) => {
|
||||
self.contains_drag.set(false);
|
||||
Self::FIELD_OFFSETS.dropped.apply_pin(self).call(&(event.clone(),));
|
||||
InputEventResult::EventAccepted
|
||||
}
|
||||
MouseEvent::Exit => {
|
||||
self.contains_drag.set(false);
|
||||
InputEventResult::EventIgnored
|
||||
}
|
||||
_ => 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(
|
||||
self: Pin<&Self>,
|
||||
_: &mut &mut dyn ItemRenderer,
|
||||
_self_rc: &ItemRc,
|
||||
_size: LogicalSize,
|
||||
) -> RenderingResult {
|
||||
RenderingResult::ContinueRenderingChildren
|
||||
}
|
||||
|
||||
fn bounding_rect(
|
||||
self: core::pin::Pin<&Self>,
|
||||
_window_adapter: &Rc<dyn WindowAdapter>,
|
||||
_self_rc: &ItemRc,
|
||||
mut geometry: LogicalRect,
|
||||
) -> LogicalRect {
|
||||
geometry.size = LogicalSize::zero();
|
||||
geometry
|
||||
}
|
||||
|
||||
fn clips_children(self: core::pin::Pin<&Self>) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl ItemConsts for DropArea {
|
||||
const cached_rendering_data_offset: const_field_offset::FieldOffset<
|
||||
DropArea,
|
||||
CachedRenderingData,
|
||||
> = DropArea::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection();
|
||||
}
|
||||
|
|
@ -313,6 +313,9 @@ impl FlickableData {
|
|||
MouseEvent::Pressed { .. } | MouseEvent::Released { .. } => {
|
||||
InputEventFilterResult::ForwardAndIgnore
|
||||
}
|
||||
MouseEvent::DragMove(..) | MouseEvent::Drop(..) => {
|
||||
InputEventFilterResult::ForwardAndIgnore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -406,6 +409,7 @@ impl FlickableData {
|
|||
}
|
||||
InputEventResult::EventAccepted
|
||||
}
|
||||
MouseEvent::DragMove(..) | MouseEvent::Drop(..) => InputEventResult::EventIgnored,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -202,6 +202,7 @@ impl Item for TouchArea {
|
|||
}
|
||||
}
|
||||
}
|
||||
MouseEvent::DragMove(..) | MouseEvent::Drop(..) => InputEventResult::EventIgnored,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -487,6 +488,9 @@ impl Item for SwipeGestureHandler {
|
|||
MouseEvent::Pressed { .. } | MouseEvent::Released { .. } => {
|
||||
InputEventFilterResult::ForwardAndIgnore
|
||||
}
|
||||
MouseEvent::DragMove(..) | MouseEvent::Drop(..) => {
|
||||
InputEventFilterResult::ForwardAndIgnore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -539,6 +543,7 @@ impl Item for SwipeGestureHandler {
|
|||
InputEventResult::GrabMouse
|
||||
}
|
||||
MouseEvent::Wheel { .. } => InputEventResult::EventIgnored,
|
||||
MouseEvent::DragMove(..) | MouseEvent::Drop(..) => InputEventResult::EventIgnored,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ macro_rules! declare_ValueType_2 {
|
|||
crate::api::LogicalPosition,
|
||||
crate::items::FontMetrics,
|
||||
crate::items::MenuEntry,
|
||||
crate::items::DropEvent,
|
||||
crate::model::ModelRc<crate::items::MenuEntry>,
|
||||
$(crate::items::$Name,)*
|
||||
];
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use crate::api::{
|
|||
};
|
||||
use crate::input::{
|
||||
key_codes, ClickState, FocusEvent, FocusReason, InternalKeyboardModifierState, KeyEvent,
|
||||
KeyEventType, MouseEvent, MouseInputState, TextCursorBlinker,
|
||||
KeyEventType, MouseEvent, MouseInputState, PointerEventButton, TextCursorBlinker,
|
||||
};
|
||||
use crate::item_tree::{
|
||||
ItemRc, ItemTreeRc, ItemTreeRef, ItemTreeVTable, ItemTreeWeak, ItemWeak,
|
||||
|
|
@ -585,11 +585,35 @@ impl WindowInner {
|
|||
// handle multiple press release
|
||||
event = self.click_state.check_repeat(event, self.ctx.platform().click_interval());
|
||||
|
||||
let window_adapter = self.window_adapter();
|
||||
let mut mouse_input_state = self.mouse_input_state.take();
|
||||
if let Some(mut drop_event) = mouse_input_state.drag_data.clone() {
|
||||
match &event {
|
||||
MouseEvent::Released { position, button: PointerEventButton::Left, .. } => {
|
||||
if let Some(window_adapter) = window_adapter.internal(crate::InternalToken) {
|
||||
window_adapter.set_mouse_cursor(MouseCursor::Default);
|
||||
}
|
||||
drop_event.position = crate::lengths::logical_position_to_api(*position);
|
||||
event = MouseEvent::Drop(drop_event);
|
||||
mouse_input_state.drag_data = None;
|
||||
}
|
||||
MouseEvent::Moved { position } => {
|
||||
if let Some(window_adapter) = window_adapter.internal(crate::InternalToken) {
|
||||
window_adapter.set_mouse_cursor(MouseCursor::NoDrop);
|
||||
}
|
||||
drop_event.position = crate::lengths::logical_position_to_api(*position);
|
||||
event = MouseEvent::DragMove(drop_event);
|
||||
}
|
||||
MouseEvent::Exit => {
|
||||
mouse_input_state.drag_data = None;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let pressed_event = matches!(event, MouseEvent::Pressed { .. });
|
||||
let released_event = matches!(event, MouseEvent::Released { .. });
|
||||
|
||||
let window_adapter = self.window_adapter();
|
||||
let mut mouse_input_state = self.mouse_input_state.take();
|
||||
let last_top_item = mouse_input_state.top_item_including_delayed();
|
||||
if released_event {
|
||||
mouse_input_state =
|
||||
|
|
|
|||
|
|
@ -997,6 +997,8 @@ fn generate_rtti() -> HashMap<&'static str, Rc<ItemRTTI>> {
|
|||
rtti_for::<Rotate>(),
|
||||
rtti_for::<Opacity>(),
|
||||
rtti_for::<Layer>(),
|
||||
rtti_for::<DragArea>(),
|
||||
rtti_for::<DropArea>(),
|
||||
rtti_for::<ContextMenu>(),
|
||||
rtti_for::<MenuItem>(),
|
||||
]
|
||||
|
|
|
|||
100
tests/cases/elements/dragarea_droparea.slint
Normal file
100
tests/cases/elements/dragarea_droparea.slint
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
// 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
|
||||
|
||||
export component TestCase inherits Window {
|
||||
width: 100px;
|
||||
height: 200px;
|
||||
in-out property <string> result;
|
||||
out property <bool> contains-drag <=> da.contains-drag;
|
||||
VerticalLayout {
|
||||
Rectangle {
|
||||
background: inner_touch_area.has-hover ? yellow : red;
|
||||
DragArea {
|
||||
mime-type: "text/plain";
|
||||
data: "Hello World";
|
||||
|
||||
inner_touch_area := TouchArea {
|
||||
x: 50px;
|
||||
width: 50px;
|
||||
clicked => { result += "InnerClicked;"; }
|
||||
}
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
background: da.contains-drag ? green : blue;
|
||||
da := DropArea {
|
||||
can-drop(event) => {
|
||||
debug("can-drop", event);
|
||||
true
|
||||
}
|
||||
dropped(event) => {
|
||||
result += "D[" + event.data + "];";
|
||||
debug("dropped", event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
```rust
|
||||
use slint::{platform::WindowEvent, LogicalPosition, platform::PointerEventButton};
|
||||
|
||||
let instance = TestCase::new().unwrap();
|
||||
assert_eq!(instance.get_contains_drag(), false);
|
||||
assert_eq!(instance.get_result(), "");
|
||||
|
||||
instance.window().dispatch_event(WindowEvent::PointerPressed { position: LogicalPosition::new(20.0, 25.0), button: PointerEventButton::Left });
|
||||
slint_testing::mock_elapsed_time(20);
|
||||
assert_eq!(instance.get_contains_drag(), false);
|
||||
assert_eq!(instance.get_result(), "");
|
||||
|
||||
instance.window().dispatch_event(WindowEvent::PointerMoved { position: LogicalPosition::new(21.0, 40.0) });
|
||||
slint_testing::mock_elapsed_time(20);
|
||||
assert_eq!(instance.get_contains_drag(), false);
|
||||
assert_eq!(instance.get_result(), "");
|
||||
|
||||
instance.window().dispatch_event(WindowEvent::PointerMoved { position: LogicalPosition::new(22.0, 120.0) });
|
||||
slint_testing::mock_elapsed_time(20);
|
||||
assert_eq!(instance.get_contains_drag(), true);
|
||||
assert_eq!(instance.get_result(), "");
|
||||
|
||||
instance.window().dispatch_event(WindowEvent::PointerReleased { position: LogicalPosition::new(22.0, 120.0), button: PointerEventButton::Left });
|
||||
slint_testing::mock_elapsed_time(20);
|
||||
assert_eq!(instance.get_result(), "D[Hello World];");
|
||||
assert_eq!(instance.get_contains_drag(), false);
|
||||
|
||||
instance.set_result("".into());
|
||||
instance.window().dispatch_event(WindowEvent::PointerPressed { position: LogicalPosition::new(51.0, 50.0), button: PointerEventButton::Left });
|
||||
slint_testing::mock_elapsed_time(20);
|
||||
assert_eq!(instance.get_contains_drag(), false);
|
||||
assert_eq!(instance.get_result(), "");
|
||||
instance.window().dispatch_event(WindowEvent::PointerMoved { position: LogicalPosition::new(52.0, 50.0) });
|
||||
slint_testing::mock_elapsed_time(20);
|
||||
assert_eq!(instance.get_contains_drag(), false);
|
||||
assert_eq!(instance.get_result(), "");
|
||||
instance.window().dispatch_event(WindowEvent::PointerReleased { position: LogicalPosition::new(52.0, 50.0), button: PointerEventButton::Left });
|
||||
slint_testing::mock_elapsed_time(20);
|
||||
assert_eq!(instance.get_result(), "InnerClicked;");
|
||||
assert_eq!(instance.get_contains_drag(), false);
|
||||
|
||||
instance.set_result("".into());
|
||||
instance.window().dispatch_event(WindowEvent::PointerPressed { position: LogicalPosition::new(51.0, 15.0), button: PointerEventButton::Left });
|
||||
slint_testing::mock_elapsed_time(20);
|
||||
assert_eq!(instance.get_contains_drag(), false);
|
||||
assert_eq!(instance.get_result(), "");
|
||||
instance.window().dispatch_event(WindowEvent::PointerMoved { position: LogicalPosition::new(58.0, 40.0) });
|
||||
slint_testing::mock_elapsed_time(20);
|
||||
assert_eq!(instance.get_contains_drag(), false);
|
||||
assert_eq!(instance.get_result(), "");
|
||||
instance.window().dispatch_event(WindowEvent::PointerMoved { position: LogicalPosition::new(58.0, 120.0) });
|
||||
assert_eq!(instance.get_contains_drag(), true);
|
||||
assert_eq!(instance.get_result(), "");
|
||||
instance.window().dispatch_event(WindowEvent::PointerReleased { position: LogicalPosition::new(58.0, 20.0), button: PointerEventButton::Left });
|
||||
slint_testing::mock_elapsed_time(20);
|
||||
assert_eq!(instance.get_contains_drag(), false);
|
||||
assert_eq!(instance.get_result(), "");
|
||||
```
|
||||
|
||||
*/
|
||||
|
|
@ -213,8 +213,10 @@ pub fn extract_builtin_structs() -> std::collections::BTreeMap<String, StructDoc
|
|||
|
||||
// `StateInfo` should not be in the documentation, so remove it again
|
||||
structs.remove("StateInfo");
|
||||
// Experimental type
|
||||
// Internal type
|
||||
structs.remove("MenuEntry");
|
||||
// Experimental type
|
||||
structs.remove("DropEvent");
|
||||
|
||||
structs
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue