Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Passing events to sub-widgets in List Editor and refactoring of the slider component. #6433

Merged
merged 12 commits into from
Apr 27, 2023
21 changes: 11 additions & 10 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

169 changes: 42 additions & 127 deletions lib/rust/ensogl/component/list-editor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,11 @@
#![allow(clippy::bool_to_int_with_if)]
#![allow(clippy::let_and_return)]

use ensogl_core::display::shape::compound::rectangle::*;
use ensogl_core::display::world::*;
use ensogl_core::prelude::*;

use ensogl_core::application::Application;
use ensogl_core::control::io::mouse;
use ensogl_core::data::color;
use ensogl_core::display;
use ensogl_core::display::navigation::navigator::Navigator;
use ensogl_core::display::object::Event;
use ensogl_core::display::object::ObjectOps;
use ensogl_core::gui::cursor;
Expand Down Expand Up @@ -262,12 +258,12 @@ impl<T> From<StrongPlaceholder> for ItemOrPlaceholder<T> {
// === ListEditor ===
// ==================

ensogl_core::define_endpoints_2! { <T: ('static)>
ensogl_core::define_endpoints_2! { <T: ('static + Debug)>
Input {
/// Push a new element to the end of the list.
push(Weak<T>),
push(Rc<RefCell<Option<T>>>),

insert((Index, Weak<T>)),
insert((Index, Rc<RefCell<Option<T>>>)),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this has no docstring while other two inputs do?


/// Remove the element at the given index. If the index is invalid, nothing will happen.
remove(Index),
Expand All @@ -283,9 +279,9 @@ ensogl_core::define_endpoints_2! { <T: ('static)>
}
Output {
/// Fires whenever a new element was added to the list.
on_item_added(Response<(Index, Weak<T>)>),
on_item_added(Response<Index>),

on_item_removed(Response<(Index, Weak<T>)>),
on_item_removed(Response<(Index, Rc<RefCell<Option<T>>>)>),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same question as above.


/// Request new item to be inserted at the provided index. In most cases, this happens after
/// clicking a "plus" icon to add new element to the list. As a response, you should use the
Expand All @@ -296,7 +292,7 @@ ensogl_core::define_endpoints_2! { <T: ('static)>

#[derive(Derivative, CloneRef, Debug, Deref)]
#[derivative(Clone(bound = ""))]
pub struct ListEditor<T: 'static> {
pub struct ListEditor<T: 'static + Debug> {
#[deref]
pub frp: Frp<T>,
root: display::object::Instance,
Expand Down Expand Up @@ -339,7 +335,7 @@ impl<T> From<Model<T>> for SharedModel<T> {
}


impl<T: display::Object + CloneRef> ListEditor<T> {
impl<T: display::Object + CloneRef + Debug> ListEditor<T> {
pub fn new(cursor: &Cursor) -> Self {
let frp = Frp::new();
let model = Model::new(cursor);
Expand All @@ -359,10 +355,6 @@ impl<T: display::Object + CloneRef> ListEditor<T> {
let on_move = scene.on_event::<mouse::Move>();

frp::extend! { network

// Do not pass events to children, as we don't know whether we are about to drag
// them yet.
eval on_down ([] (event) event.stop_propagation());
target <= on_down.map(|event| event.target());

on_up <- on_up_source.identity();
Expand All @@ -385,13 +377,21 @@ impl<T: display::Object + CloneRef> ListEditor<T> {
}

self.init_add_and_remove();
let (is_dragging, drag_diff) = self.init_dragging(&on_up, &on_down, &target, &pos_diff);
let (is_dragging, drag_diff, no_drag) =
self.init_dragging(&on_up, &on_down, &target, &pos_diff);
let (is_trashing, trash_pointer_style) = self.init_trashing(&on_up, &drag_diff);
self.init_dropping(&on_up, &pos_on_move_down, &is_trashing);
let insert_pointer_style = self.init_insertion_points(&on_up, &pos_on_move, &is_dragging);

frp::extend! { network
cursor.frp.set_style <+ all [insert_pointer_style, trash_pointer_style].fold();
on_down_drag <- on_down.gate_not(&no_drag);
// Do not pass events to children, as we don't know whether we are about to drag
// them yet.
eval on_down_drag ([] (event) event.stop_propagation());
_eval <- no_drag.on_true().map3(&on_down, &target, |_, event, target| {
target.emit_event(event.payload.clone());
});
}
self
}
Expand Down Expand Up @@ -451,18 +451,18 @@ impl<T: display::Object + CloneRef> ListEditor<T> {
let network = self.frp.network();

frp::extend! { network
push_ix <= frp.push.map(f!((item) model.push_weak(item)));
on_pushed <- frp.push.map2(&push_ix, |t, ix| Response::api((*ix, t.clone())));
push_ix <= frp.push.map(f!((item) model.push_cell(item)));
on_pushed <- push_ix.map(|ix| Response::api(*ix));
frp.private.output.on_item_added <+ on_pushed;

insert_ix <= frp.insert.map(f!(((index, item)) model.insert_weak(*index, item)));
on_inserted <- frp.insert.map2(&insert_ix, |t, ix| Response::api((*ix, t.1.clone())));
insert_ix <= frp.insert.map(f!(((index, item)) model.insert_cell(*index, item)));
on_inserted <- insert_ix.map(|ix| Response::api(*ix));
frp.private.output.on_item_added <+ on_inserted;

let on_item_removed = &frp.private.output.on_item_removed;
eval frp.remove([model, on_item_removed] (index) {
if let Some(item) = model.borrow_mut().trash_item_at(*index) {
on_item_removed.emit(Response::api((*index, Rc::new(item).downgrade())));
on_item_removed.emit(Response::api((*index, Rc::new(RefCell::new(Some(item))))));
}
});
}
Expand All @@ -475,7 +475,7 @@ impl<T: display::Object + CloneRef> ListEditor<T> {
on_down: &frp::Stream<Event<mouse::Down>>,
target: &frp::Stream<display::object::Instance>,
pos_diff: &frp::Stream<Vector2>,
) -> (frp::Stream<bool>, frp::Stream<Vector2>) {
) -> (frp::Stream<bool>, frp::Stream<Vector2>, frp::Stream<bool>) {
let model = &self.model;
let on_up = on_up.clone_ref();
let on_down = on_down.clone_ref();
Expand All @@ -499,18 +499,19 @@ impl<T: display::Object + CloneRef> ListEditor<T> {
init_drag_not_disabled <- init_drag.gate_not(&drag_disabled);
is_dragging <- bool(&on_up, &init_drag_not_disabled).on_change();
drag_diff <- pos_diff.gate(&is_dragging);
no_drag <- drag_disabled.gate_not(&is_dragging).on_change();

status <- bool(&on_up, &drag_diff).on_change();
start <- status.on_true();
target_on_start <- target.sample(&start);
let on_item_removed = &frp.private.output.on_item_removed;
eval target_on_start([model, on_item_removed] (t) {
if let Some((index, item)) = model.borrow_mut().start_item_drag(t) {
on_item_removed.emit(Response::gui((index, Rc::new(item).downgrade())));
on_item_removed.emit(Response::gui((index, Rc::new(RefCell::new(Some(item))))));
}
});
}
(status, drag_diff)
(status, drag_diff, no_drag)
}

/// Implementation of item trashing logic. See docs of this crate to learn more.
Expand Down Expand Up @@ -571,8 +572,8 @@ impl<T: display::Object + CloneRef> ListEditor<T> {

let on_item_added = &frp.private.output.on_item_added;
eval insert_index_on_drop ([model, on_item_added] (index)
if let Some((index, item)) = model.borrow_mut().place_dragged_item(*index) {
on_item_added.emit(Response::gui((index, Rc::new(item).downgrade())));
if let Some(index) = model.borrow_mut().place_dragged_item(*index) {
on_item_added.emit(Response::gui(index));
}
);
}
Expand All @@ -591,7 +592,7 @@ impl<T: display::Object + CloneRef> ListEditor<T> {
}

pub fn push(&self, item: T) {
self.frp.push(Rc::new(item).downgrade());
self.frp.push(Rc::new(RefCell::new(Some(item))));
}

pub fn items(&self) -> Vec<T> {
Expand All @@ -612,16 +613,18 @@ impl<T: display::Object + CloneRef + 'static> SharedModel<T> {
self.borrow_mut().push(item)
}

fn push_weak(&self, item: &Weak<T>) -> Option<Index> {
item.upgrade().map(|item| self.push((*item).clone_ref()))
fn push_cell(&self, item: &Rc<RefCell<Option<T>>>) -> Option<Index> {
let item = mem::take(&mut *item.borrow_mut());
item.map(|item| self.push(item))
}

fn insert(&self, index: Index, item: T) -> Index {
self.borrow_mut().insert(index, item)
}

fn insert_weak(&self, index: Index, item: &Weak<T>) -> Option<Index> {
item.upgrade().map(|item| self.insert(index, (*item).clone_ref()))
fn insert_cell(&self, index: Index, item: &Rc<RefCell<Option<T>>>) -> Option<Index> {
let item = mem::take(&mut *item.borrow_mut());
item.map(|item| self.insert(index, item))
}

fn insert_index(&self, x: f32, center_points: &[f32]) -> ItemOrPlaceholderIndex {
Expand Down Expand Up @@ -681,7 +684,7 @@ impl<T: display::Object + CloneRef + 'static> Model<T> {

/// Find an element by the provided display object reference.
fn item_index_of(
&mut self,
&self,
obj: &display::object::Instance,
) -> Option<(Index, ItemOrPlaceholderIndex)> {
self.items
Expand Down Expand Up @@ -841,11 +844,12 @@ impl<T: display::Object + CloneRef + 'static> Model<T> {
///
/// See docs of [`Self::start_item_drag_at`] for more information.
fn start_item_drag(&mut self, target: &display::object::Instance) -> Option<(Index, T)> {
let index = self.item_index_of(target);
if let Some((index, index_or_placeholder_index)) = index {
let objs = target.rev_parent_chain();
let tarrget_index = objs.into_iter().find_map(|t| self.item_index_of(&t));
if let Some((index, index_or_placeholder_index)) = tarrget_index {
self.start_item_drag_at(index_or_placeholder_index).map(|item| (index, item))
} else {
warn!("Requested to drag a non-existent item.");
warn!("Could not find the item to drag.");
None
}
}
Expand Down Expand Up @@ -941,7 +945,7 @@ impl<T: display::Object + CloneRef + 'static> Model<T> {
/// Place the currently dragged element in the given index. The item will be enclosed in the
/// [`Item`] object, will handles its animation. See the documentation of
/// [`ItemOrPlaceholder`] to learn more.
fn place_dragged_item(&mut self, index: ItemOrPlaceholderIndex) -> Option<(Index, T)> {
fn place_dragged_item(&mut self, index: ItemOrPlaceholderIndex) -> Option<Index> {
if let Some(item) = self.cursor.stop_drag_if_is::<T>() {
self.collapse_all_placeholders_no_margin_update();
if let Some((index, placeholder)) = self.get_indexed_merged_placeholder_at(index) {
Expand All @@ -958,7 +962,7 @@ impl<T: display::Object + CloneRef + 'static> Model<T> {
warn!("An element was inserted without a placeholder. This should not happen.");
}
self.reposition_items();
self.item_or_placeholder_index_to_index(index).map(|index| (index, item))
self.item_or_placeholder_index_to_index(index)
} else {
warn!("Called function to insert dragged element, but no element is being dragged.");
None
Expand Down Expand Up @@ -1036,7 +1040,7 @@ impl<T: display::Object + CloneRef + 'static> Model<T> {
}
}

impl<T: 'static> display::Object for ListEditor<T> {
impl<T: 'static + Debug> display::Object for ListEditor<T> {
fn display_object(&self) -> &display::object::Instance {
&self.root
}
Expand Down Expand Up @@ -1092,92 +1096,3 @@ mod trash {
}
use crate::placeholder::WeakPlaceholder;
use trash::Trash;


// ===================
// === Entry Point ===
// ===================

pub mod glob {
use super::*;
ensogl_core::define_endpoints_2! {
Input {
}
Output {
}
}
}

/// The example entry point.
#[entry_point]
#[allow(dead_code)]
pub fn main() {
let app = Application::new("root");
let world = app.display.clone();
let scene = &world.default_scene;

let camera = scene.camera().clone_ref();
let navigator = Navigator::new(scene, &camera);

let vector_editor = ListEditor::<Rectangle>::new(&app.cursor);


let shape1 = Circle().build(|t| {
t.set_size(Vector2(60.0, 100.0))
.set_color(color::Rgba::new(0.0, 0.0, 0.0, 0.1))
.set_inset_border(2.0)
.set_border_color(color::Rgba::new(0.0, 0.0, 0.0, 0.5))
.keep_bottom_left_quarter();
});
let shape2 = RoundedRectangle(10.0).build(|t| {
t.set_size(Vector2(120.0, 100.0))
.set_color(color::Rgba::new(0.0, 0.0, 0.0, 0.1))
.set_inset_border(2.0)
.set_border_color(color::Rgba::new(0.0, 0.0, 0.0, 0.5));
});
let shape3 = RoundedRectangle(10.0).build(|t| {
t.set_size(Vector2(240.0, 100.0))
.set_color(color::Rgba::new(0.0, 0.0, 0.0, 0.1))
.set_inset_border(2.0)
.set_border_color(color::Rgba::new(0.0, 0.0, 0.0, 0.5));
});


let glob_frp = glob::Frp::new();
let glob_frp_network = glob_frp.network();

let shape1_down = shape1.on_event::<mouse::Down>();
frp::extend! { glob_frp_network
eval_ shape1_down ([] {
warn!("Shape 1 down");
});
new_item <- vector_editor.request_new_item.map(|_| {
let shape = RoundedRectangle(10.0).build(|t| {
t.set_size(Vector2(100.0, 100.0))
.set_color(color::Rgba::new(0.0, 0.0, 0.0, 0.1))
.set_inset_border(2.0)
.set_border_color(color::Rgba::new(0.0, 0.0, 0.0, 0.5));
});
Rc::new(shape)
});
vector_editor.insert <+ vector_editor.request_new_item.map2(&new_item, |index, item|
(**index, item.downgrade())
);
}

vector_editor.push(shape1);
vector_editor.push(shape2);
vector_editor.push(shape3);

let root = display::object::Instance::new();
root.set_size(Vector2(300.0, 100.0));
root.add_child(&vector_editor);
world.add_child(&root);

world.keep_alive_forever();
mem::forget(app);
mem::forget(glob_frp);
mem::forget(navigator);
mem::forget(root);
mem::forget(vector_editor);
}
Loading