|
@@ -75,6 +75,12 @@ struct MouseClickInfo {
|
|
|
start_instant: std::time::Instant,
|
|
start_instant: std::time::Instant,
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+#[derive(Clone)]
|
|
|
|
|
+struct DragInfo {
|
|
|
|
|
+ item_idx: usize,
|
|
|
|
|
+ insert_idx: usize,
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
impl TouchInfo {
|
|
impl TouchInfo {
|
|
|
fn new(start_scroll: f32, pos: Point) -> Self {
|
|
fn new(start_scroll: f32, pos: Point) -> Self {
|
|
|
Self {
|
|
Self {
|
|
@@ -133,6 +139,7 @@ pub struct Menu {
|
|
|
mouse_pos: SyncMutex<Point>,
|
|
mouse_pos: SyncMutex<Point>,
|
|
|
touch_info: SyncMutex<Option<TouchInfo>>,
|
|
touch_info: SyncMutex<Option<TouchInfo>>,
|
|
|
mouse_click_info: SyncMutex<Option<MouseClickInfo>>,
|
|
mouse_click_info: SyncMutex<Option<MouseClickInfo>>,
|
|
|
|
|
+ drag_info: SyncMutex<Option<DragInfo>>,
|
|
|
long_press_task: SyncMutex<Option<smol::Task<()>>>,
|
|
long_press_task: SyncMutex<Option<smol::Task<()>>>,
|
|
|
weak_self: SyncMutex<Option<Weak<Self>>>,
|
|
weak_self: SyncMutex<Option<Weak<Self>>>,
|
|
|
ex: SyncMutex<Option<ExecutorPtr>>,
|
|
ex: SyncMutex<Option<ExecutorPtr>>,
|
|
@@ -202,6 +209,7 @@ impl Menu {
|
|
|
mouse_pos: SyncMutex::new(Point::new(0., 0.)),
|
|
mouse_pos: SyncMutex::new(Point::new(0., 0.)),
|
|
|
touch_info: SyncMutex::new(None),
|
|
touch_info: SyncMutex::new(None),
|
|
|
mouse_click_info: SyncMutex::new(None),
|
|
mouse_click_info: SyncMutex::new(None),
|
|
|
|
|
+ drag_info: SyncMutex::new(None),
|
|
|
long_press_task: SyncMutex::new(None),
|
|
long_press_task: SyncMutex::new(None),
|
|
|
weak_self: SyncMutex::new(None),
|
|
weak_self: SyncMutex::new(None),
|
|
|
ex: SyncMutex::new(None),
|
|
ex: SyncMutex::new(None),
|
|
@@ -282,6 +290,9 @@ impl Menu {
|
|
|
|
|
|
|
|
if pos.x >= x_min && pos.x <= x_max {
|
|
if pos.x >= x_min && pos.x <= x_max {
|
|
|
info!(target: "app::menu", "X clicked for item: {item_name}");
|
|
info!(target: "app::menu", "X clicked for item: {item_name}");
|
|
|
|
|
+ let atom = &mut self.renderer.make_guard(gfxtag!("Menu::delete_item"));
|
|
|
|
|
+ self.items.remove_str(atom, Role::App, item_idx).unwrap();
|
|
|
|
|
+ self.redraw(atom);
|
|
|
} else {
|
|
} else {
|
|
|
self.handle_selection(item_idx).await;
|
|
self.handle_selection(item_idx).await;
|
|
|
}
|
|
}
|
|
@@ -318,6 +329,22 @@ impl Menu {
|
|
|
|
|
|
|
|
let num_items = self.items.get_len();
|
|
let num_items = self.items.get_len();
|
|
|
|
|
|
|
|
|
|
+ // Get items and reorder if dragging
|
|
|
|
|
+ let mut items_list = {
|
|
|
|
|
+ let mut items = vec![];
|
|
|
|
|
+ for idx in 0..num_items {
|
|
|
|
|
+ items.push(self.items.get_str(idx).unwrap());
|
|
|
|
|
+ }
|
|
|
|
|
+ items
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ if let Some(ref drag_info) = self.drag_info.lock().as_ref() {
|
|
|
|
|
+ if drag_info.item_idx != drag_info.insert_idx {
|
|
|
|
|
+ let item = items_list.remove(drag_info.item_idx);
|
|
|
|
|
+ items_list.insert(drag_info.insert_idx, item);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// Draw single background mesh for the entire menu
|
|
// Draw single background mesh for the entire menu
|
|
|
let content_height = num_items as f32 * item_height;
|
|
let content_height = num_items as f32 * item_height;
|
|
|
|
|
|
|
@@ -355,7 +382,7 @@ impl Menu {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
for idx in 0..num_items {
|
|
for idx in 0..num_items {
|
|
|
- let item_text = self.items.get_str(idx).unwrap();
|
|
|
|
|
|
|
+ let item_text = items_list[idx].clone();
|
|
|
|
|
|
|
|
let color = match item_states.get(&item_text) {
|
|
let color = match item_states.get(&item_text) {
|
|
|
Some(ItemStatus::Active) => active_color,
|
|
Some(ItemStatus::Active) => active_color,
|
|
@@ -650,6 +677,25 @@ impl UIObject for Menu {
|
|
|
return false
|
|
return false
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ let is_edit_mode = self.is_edit_mode.load(Ordering::Relaxed);
|
|
|
|
|
+
|
|
|
|
|
+ if is_edit_mode {
|
|
|
|
|
+ let font_size = self.font_size.get();
|
|
|
|
|
+ let handle_padding = self.handle_padding.get();
|
|
|
|
|
+ let hammy_half_size = font_size * 0.7;
|
|
|
|
|
+ let hammy_center = rect.w - handle_padding / 2.;
|
|
|
|
|
+
|
|
|
|
|
+ let hammy_min = hammy_center - hammy_half_size;
|
|
|
|
|
+ let hammy_max = hammy_center + hammy_half_size;
|
|
|
|
|
+
|
|
|
|
|
+ if mouse_pos.x >= hammy_min && mouse_pos.x <= hammy_max {
|
|
|
|
|
+ if let Some(item_idx) = self.get_selected_item_index(mouse_pos.y) {
|
|
|
|
|
+ *self.drag_info.lock() = Some(DragInfo { item_idx, insert_idx: item_idx });
|
|
|
|
|
+ info!(target: "app::menu", "Dragging item: {}", item_idx);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
*self.mouse_click_info.lock() =
|
|
*self.mouse_click_info.lock() =
|
|
|
Some(MouseClickInfo { start_pos: mouse_pos, start_instant: std::time::Instant::now() });
|
|
Some(MouseClickInfo { start_pos: mouse_pos, start_instant: std::time::Instant::now() });
|
|
|
|
|
|
|
@@ -691,6 +737,20 @@ impl UIObject for Menu {
|
|
|
return false
|
|
return false
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // Apply drag reorder if we were dragging
|
|
|
|
|
+ let drag = self.drag_info.lock().take();
|
|
|
|
|
+ if let Some(drag_info) = drag {
|
|
|
|
|
+ if drag_info.item_idx != drag_info.insert_idx {
|
|
|
|
|
+ let item = self.items.get_str(drag_info.item_idx).unwrap();
|
|
|
|
|
+ let atom = &mut self.renderer.make_guard(gfxtag!("Menu::reorder_item"));
|
|
|
|
|
+ self.items.remove_str(atom, Role::App, drag_info.item_idx).unwrap();
|
|
|
|
|
+ let insert_idx = drag_info.insert_idx;
|
|
|
|
|
+ self.items.insert_str(atom, Role::App, insert_idx, &item).unwrap();
|
|
|
|
|
+ info!(target: "app::menu", "Reordered item {} to {}", drag_info.item_idx, insert_idx);
|
|
|
|
|
+ }
|
|
|
|
|
+ return true
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// Cancel the long press detection task
|
|
// Cancel the long press detection task
|
|
|
let task = self.long_press_task.lock().take();
|
|
let task = self.long_press_task.lock().take();
|
|
|
if let Some(task) = task {
|
|
if let Some(task) = task {
|
|
@@ -726,6 +786,27 @@ impl UIObject for Menu {
|
|
|
|
|
|
|
|
async fn handle_mouse_move(&self, mouse_pos: Point) -> bool {
|
|
async fn handle_mouse_move(&self, mouse_pos: Point) -> bool {
|
|
|
*self.mouse_pos.lock() = mouse_pos;
|
|
*self.mouse_pos.lock() = mouse_pos;
|
|
|
|
|
+
|
|
|
|
|
+ let mut should_redraw = false;
|
|
|
|
|
+
|
|
|
|
|
+ if self.drag_info.lock().is_some() {
|
|
|
|
|
+ if let Some(insert_idx) = self.get_selected_item_index(mouse_pos.y) {
|
|
|
|
|
+ let mut drag = self.drag_info.lock();
|
|
|
|
|
+ if let Some(d) = drag.as_mut() {
|
|
|
|
|
+ if d.insert_idx != insert_idx {
|
|
|
|
|
+ d.insert_idx = insert_idx;
|
|
|
|
|
+ info!(target: "app::menu", "insert_idx changed to: {}", insert_idx);
|
|
|
|
|
+ should_redraw = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if should_redraw {
|
|
|
|
|
+ let atom = &mut self.renderer.make_guard(gfxtag!("Menu::drag_update"));
|
|
|
|
|
+ self.redraw(atom);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
false
|
|
false
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -748,12 +829,52 @@ impl UIObject for Menu {
|
|
|
return false
|
|
return false
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ let is_edit_mode = self.is_edit_mode.load(Ordering::Relaxed);
|
|
|
|
|
+
|
|
|
|
|
+ if is_edit_mode {
|
|
|
|
|
+ let font_size = self.font_size.get();
|
|
|
|
|
+ let handle_padding = self.handle_padding.get();
|
|
|
|
|
+ let hammy_half_size = font_size * 0.7;
|
|
|
|
|
+ let hammy_center = rect.w - handle_padding / 2.;
|
|
|
|
|
+
|
|
|
|
|
+ let hammy_min = hammy_center - hammy_half_size;
|
|
|
|
|
+ let hammy_max = hammy_center + hammy_half_size;
|
|
|
|
|
+
|
|
|
|
|
+ if touch_pos.x >= hammy_min && touch_pos.x <= hammy_max {
|
|
|
|
|
+ if let Some(item_idx) = self.get_selected_item_index(touch_pos.y) {
|
|
|
|
|
+ *self.drag_info.lock() =
|
|
|
|
|
+ Some(DragInfo { item_idx, insert_idx: item_idx });
|
|
|
|
|
+ info!(target: "app::menu", "Dragging item: {}", item_idx);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
*self.touch_info.lock() =
|
|
*self.touch_info.lock() =
|
|
|
Some(TouchInfo::new(self.scroll.load(Ordering::Relaxed), touch_pos));
|
|
Some(TouchInfo::new(self.scroll.load(Ordering::Relaxed), touch_pos));
|
|
|
true
|
|
true
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
TouchPhase::Moved => {
|
|
TouchPhase::Moved => {
|
|
|
|
|
+ let mut should_redraw = false;
|
|
|
|
|
+
|
|
|
|
|
+ if self.drag_info.lock().is_some() {
|
|
|
|
|
+ if let Some(insert_idx) = self.get_selected_item_index(touch_pos.y) {
|
|
|
|
|
+ let mut drag = self.drag_info.lock();
|
|
|
|
|
+ if let Some(d) = drag.as_mut() {
|
|
|
|
|
+ if d.insert_idx != insert_idx {
|
|
|
|
|
+ d.insert_idx = insert_idx;
|
|
|
|
|
+ info!(target: "app::menu", "insert_idx changed to: {}", insert_idx);
|
|
|
|
|
+ should_redraw = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if should_redraw {
|
|
|
|
|
+ let atom = &mut self.renderer.make_guard(gfxtag!("Menu::drag_update"));
|
|
|
|
|
+ self.redraw(atom);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
let scroll = {
|
|
let scroll = {
|
|
|
let mut touch_info = self.touch_info.lock();
|
|
let mut touch_info = self.touch_info.lock();
|
|
|
let Some(info) = &mut *touch_info else { return false };
|
|
let Some(info) = &mut *touch_info else { return false };
|
|
@@ -795,6 +916,19 @@ impl UIObject for Menu {
|
|
|
TouchPhase::Started | TouchPhase::Moved => false,
|
|
TouchPhase::Started | TouchPhase::Moved => false,
|
|
|
|
|
|
|
|
TouchPhase::Ended | TouchPhase::Cancelled => {
|
|
TouchPhase::Ended | TouchPhase::Cancelled => {
|
|
|
|
|
+ let drag = self.drag_info.lock().take();
|
|
|
|
|
+ if let Some(drag_info) = drag {
|
|
|
|
|
+ if drag_info.item_idx != drag_info.insert_idx {
|
|
|
|
|
+ let item = self.items.get_str(drag_info.item_idx).unwrap();
|
|
|
|
|
+ let atom = &mut self.renderer.make_guard(gfxtag!("Menu::reorder_item"));
|
|
|
|
|
+ self.items.remove_str(atom, Role::App, drag_info.item_idx).unwrap();
|
|
|
|
|
+ let insert_idx = drag_info.insert_idx;
|
|
|
|
|
+ self.items.insert_str(atom, Role::App, insert_idx, &item).unwrap();
|
|
|
|
|
+ info!(target: "app::menu", "Reordered item {} to {}", drag_info.item_idx, insert_idx);
|
|
|
|
|
+ }
|
|
|
|
|
+ return true
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
let (is_tap, is_long_press_tap, elapsed) = {
|
|
let (is_tap, is_long_press_tap, elapsed) = {
|
|
|
let touch_info = self.touch_info.lock();
|
|
let touch_info = self.touch_info.lock();
|
|
|
let Some(info) = &*touch_info else { return true };
|
|
let Some(info) = &*touch_info else { return true };
|