浏览代码

app/menu: add color active/alert status for channels

jkds 6 月之前
父节点
当前提交
f8c9e963ce
共有 4 个文件被更改,包括 162 次插入20 次删除
  1. 1 1
      bin/app/Makefile
  2. 20 0
      bin/app/src/app/node.rs
  3. 12 1
      bin/app/src/app/schema/menu.rs
  4. 129 18
      bin/app/src/ui/menu.rs

+ 1 - 1
bin/app/Makefile

@@ -22,7 +22,7 @@ DEBUG_FEATURES = --features=enable-filelog,enable-plugins
 
 #DEV_FEATURES = --features=enable-filelog,enable-netdebug,emulate-android
 #DEV_FEATURES = --features=enable-filelog,enable-netdebug,enable-plugins
-DEV_FEATURES = --features=schema-app
+DEV_FEATURES = --features=schema-app,enable-netdebug
 
 default: build-release
 	./darkfi-app

+ 20 - 0
bin/app/src/app/node.rs

@@ -649,6 +649,20 @@ pub fn create_menu(name: &str) -> SceneNode {
     prop.set_range_f32(0., 1.);
     node.add_property(prop).unwrap();
 
+    let mut prop = Property::new("active_color", PropertyType::Float32, PropertySubType::Color);
+    prop.set_ui_text("Active Color", "Active item text color");
+    prop.set_array_len(4);
+    prop.set_defaults_f32(vec![1., 1., 1., 1.]).unwrap();
+    prop.set_range_f32(0., 1.);
+    node.add_property(prop).unwrap();
+
+    let mut prop = Property::new("alert_color", PropertyType::Float32, PropertySubType::Color);
+    prop.set_ui_text("Alert Color", "Alert item text color");
+    prop.set_array_len(4);
+    prop.set_defaults_f32(vec![1., 0.3, 0.3, 1.]).unwrap();
+    prop.set_range_f32(0., 1.);
+    node.add_property(prop).unwrap();
+
     let mut prop =
         Property::new("scroll_start_accel", PropertyType::Float32, PropertySubType::Null);
     prop.set_ui_text("Scroll Start Acceleration", "Multiplier for initial scroll velocity");
@@ -673,5 +687,11 @@ pub fn create_menu(name: &str) -> SceneNode {
     )
     .unwrap();
 
+    node.add_method("mark_active", vec![("item_name", "Item name", CallArgType::Str)], None)
+        .unwrap();
+
+    node.add_method("mark_alert", vec![("item_name", "Item name", CallArgType::Str)], None)
+        .unwrap();
+
     node
 }

+ 12 - 1
bin/app/src/app/schema/menu.rs

@@ -199,6 +199,18 @@ pub async fn make(app: &App, content: SceneNodePtr, i18n_fish: &I18nBabelFish) {
     prop.set_f32(atom, Role::App, 2, 1.).unwrap();
     prop.set_f32(atom, Role::App, 3, 1.).unwrap();
 
+    let prop = node.get_property("active_color").unwrap();
+    prop.set_f32(atom, Role::App, 0, 0.36).unwrap();
+    prop.set_f32(atom, Role::App, 1, 1.).unwrap();
+    prop.set_f32(atom, Role::App, 2, 0.51).unwrap();
+    prop.set_f32(atom, Role::App, 3, 1.).unwrap();
+
+    let prop = node.get_property("alert_color").unwrap();
+    prop.set_f32(atom, Role::App, 0, 0.56).unwrap();
+    prop.set_f32(atom, Role::App, 1, 0.61).unwrap();
+    prop.set_f32(atom, Role::App, 2, 1.).unwrap();
+    prop.set_f32(atom, Role::App, 3, 1.).unwrap();
+
     let prop = node.get_property("sep_color").unwrap();
     prop.set_f32(atom, Role::App, 0, 0.4).unwrap();
     prop.set_f32(atom, Role::App, 1, 0.4).unwrap();
@@ -237,7 +249,6 @@ pub async fn make(app: &App, content: SceneNodePtr, i18n_fish: &I18nBabelFish) {
             info!(target: "app::menu", "clicked: {channel}!");
             chatview_node.set_property_bool(atom, Role::App, "is_visible", true).unwrap();
             menu_is_visible.set(atom, false);
-            //set_normal_color(atom);
         }
     });
     app.tasks.lock().unwrap().push(listen_click);

+ 129 - 18
bin/app/src/ui/menu.rs

@@ -19,15 +19,16 @@
 use async_trait::async_trait;
 use atomic_float::AtomicF32;
 use darkfi::system::CondVar;
-use darkfi_serial::serialize;
+use darkfi_serial::{serialize, Decodable};
 use miniquad::{MouseButton, TouchPhase};
 use parking_lot::Mutex as SyncMutex;
 use rand::{rngs::OsRng, Rng};
 use std::{
-    collections::VecDeque,
+    collections::{HashMap, VecDeque},
+    io::Read,
     sync::{
         atomic::{AtomicBool, Ordering},
-        Arc,
+        Arc, Weak,
     },
 };
 
@@ -38,7 +39,7 @@ use crate::{
         BatchGuardId, BatchGuardPtr, PropertyAtomicGuard, PropertyBool, PropertyColor,
         PropertyFloat32, PropertyPtr, PropertyRect, PropertyUint32, Role,
     },
-    scene::{Pimpl, SceneNodeWeak},
+    scene::{MethodCallSub, Pimpl, SceneNodeWeak},
     text, ExecutorPtr,
 };
 
@@ -49,6 +50,12 @@ const BIG_EPSILON: f32 = 0.05;
 
 macro_rules! d { ($($arg:tt)*) => { debug!(target: "ui::menu", $($arg)*); } }
 
+#[derive(Clone, Copy, PartialEq, Eq)]
+enum ItemStatus {
+    Active,
+    Alert,
+}
+
 #[derive(Clone)]
 struct TouchInfo {
     start_scroll: f32,
@@ -109,6 +116,8 @@ pub struct Menu {
     bg_color: PropertyColor,
     sep_size: PropertyFloat32,
     sep_color: PropertyColor,
+    active_color: PropertyColor,
+    alert_color: PropertyColor,
     window_scale: PropertyFloat32,
 
     mouse_pos: SyncMutex<Point>,
@@ -119,6 +128,7 @@ pub struct Menu {
     speed: AtomicF32,
 
     parent_rect: SyncMutex<Option<Rectangle>>,
+    item_states: SyncMutex<HashMap<String, ItemStatus>>,
 }
 
 impl Menu {
@@ -140,6 +150,8 @@ impl Menu {
         let bg_color = PropertyColor::wrap(node_ref, Role::Internal, "bg_color").unwrap();
         let sep_size = PropertyFloat32::wrap(node_ref, Role::Internal, "sep_size", 0).unwrap();
         let sep_color = PropertyColor::wrap(node_ref, Role::Internal, "sep_color").unwrap();
+        let active_color = PropertyColor::wrap(node_ref, Role::Internal, "active_color").unwrap();
+        let alert_color = PropertyColor::wrap(node_ref, Role::Internal, "alert_color").unwrap();
 
         let scroll_start_accel =
             PropertyFloat32::wrap(node_ref, Role::Internal, "scroll_start_accel", 0).unwrap();
@@ -166,6 +178,8 @@ impl Menu {
             bg_color,
             sep_size,
             sep_color,
+            active_color,
+            alert_color,
             window_scale,
             mouse_pos: SyncMutex::new(Point::new(0., 0.)),
             touch_info: SyncMutex::new(None),
@@ -174,6 +188,7 @@ impl Menu {
             motion_cv,
             speed: AtomicF32::new(0.),
             parent_rect: SyncMutex::new(None),
+            item_states: SyncMutex::new(HashMap::new()),
         });
 
         Pimpl::Menu(self_)
@@ -205,6 +220,9 @@ impl Menu {
     async fn handle_selection(&self, item_idx: usize) {
         if item_idx < self.items.get_len() {
             let item_name = self.items.get_str(item_idx).unwrap();
+
+            self.item_states.lock().remove(&item_name);
+
             let node = self.node.upgrade().unwrap();
             let data = serialize(&item_name);
             node.trigger("select", data).await.unwrap();
@@ -227,6 +245,8 @@ impl Menu {
         let padding_x = self.padding.get_f32(0).unwrap();
         let padding_y = self.padding.get_f32(1).unwrap();
         let text_color = self.text_color.get();
+        let active_color = self.active_color.get();
+        let alert_color = self.alert_color.get();
         let bg_color = self.bg_color.get();
         let sep_size = self.sep_size.get();
         let sep_color = self.sep_color.get();
@@ -248,13 +268,21 @@ impl Menu {
         sep_mesh.draw_filled_box(&Rectangle::new(0., 0., rect.w, sep_size), sep_color);
         let sep_mesh = sep_mesh.alloc(&self.renderer).draw_untextured();
 
+        let item_states = self.item_states.lock();
+
         for idx in 0..num_items {
             let item_text = self.items.get_str(idx).unwrap();
 
+            let color = match item_states.get(&item_text) {
+                Some(ItemStatus::Active) => active_color,
+                Some(ItemStatus::Alert) => alert_color,
+                _ => text_color,
+            };
+
             // Draw text
             let layout = text::make_layout(
                 &item_text,
-                text_color,
+                color,
                 font_size,
                 1.0,
                 window_scale,
@@ -302,12 +330,9 @@ impl Menu {
         })
     }
 
-    async fn redraw(self: Arc<Self>, batch: BatchGuardPtr) {
+    fn redraw(&self, atom: &mut PropertyAtomicGuard) {
         let Some(parent_rect) = self.parent_rect.lock().clone() else { return };
-
-        let atom = &mut batch.spawn();
         let Some(draw_update) = self.get_draw_calls(atom, parent_rect) else { return };
-
         self.renderer.replace_draw_calls(Some(atom.batch_id), draw_update.draw_calls);
     }
 
@@ -385,6 +410,72 @@ impl Menu {
             }
         }
     }
+
+    async fn process_mark_active_method(me: &Weak<Self>, sub: &MethodCallSub) -> bool {
+        let Ok(method_call) = sub.receive().await else {
+            d!("Event relayer closed");
+            return false
+        };
+
+        d!("method called: mark_active({method_call:?})");
+        assert!(method_call.send_res.is_none());
+
+        fn decode_data(data: &[u8]) -> std::io::Result<String> {
+            use std::io::Cursor;
+            let mut cur = Cursor::new(&data);
+            let item_name = String::decode(&mut cur)?;
+            Ok(item_name)
+        }
+
+        let Ok(item_name) = decode_data(&method_call.data) else {
+            d!("mark_active() method invalid arg data");
+            return true
+        };
+
+        let Some(self_) = me.upgrade() else {
+            d!("Self destroyed");
+            return true
+        };
+
+        self_.item_states.lock().insert(item_name, ItemStatus::Active);
+        let atom = &mut self_.renderer.make_guard(gfxtag!("Menu::mark_active"));
+        self_.redraw(atom);
+
+        true
+    }
+
+    async fn process_mark_alert_method(me: &Weak<Self>, sub: &MethodCallSub) -> bool {
+        let Ok(method_call) = sub.receive().await else {
+            d!("Event relayer closed");
+            return false
+        };
+
+        d!("method called: mark_alert({method_call:?})");
+        assert!(method_call.send_res.is_none());
+
+        fn decode_data(data: &[u8]) -> std::io::Result<String> {
+            use std::io::Cursor;
+            let mut cur = Cursor::new(&data);
+            let item_name = String::decode(&mut cur)?;
+            Ok(item_name)
+        }
+
+        let Ok(item_name) = decode_data(&method_call.data) else {
+            d!("mark_alert() method invalid arg data");
+            return true
+        };
+
+        let Some(self_) = me.upgrade() else {
+            d!("Self destroyed");
+            return true
+        };
+
+        self_.item_states.lock().insert(item_name, ItemStatus::Alert);
+        let atom = &mut self_.renderer.make_guard(gfxtag!("Menu::mark_alert"));
+        self_.redraw(atom);
+
+        true
+    }
 }
 
 #[async_trait]
@@ -395,6 +486,7 @@ impl UIObject for Menu {
 
     async fn start(self: Arc<Self>, ex: ExecutorPtr) {
         let me = Arc::downgrade(&self);
+        let node_ref = &self.node.upgrade().unwrap();
 
         let me2 = me.clone();
         let cv = self.motion_cv.clone();
@@ -409,18 +501,37 @@ impl UIObject for Menu {
             }
         });
 
+        let method_sub = node_ref.subscribe_method_call("mark_active").unwrap();
+        let me2 = me.clone();
+        let mark_active_task =
+            ex.spawn(
+                async move { while Self::process_mark_active_method(&me2, &method_sub).await {} },
+            );
+
+        let method_sub = node_ref.subscribe_method_call("mark_alert").unwrap();
+        let me2 = me.clone();
+        let mark_alert_task =
+            ex.spawn(
+                async move { while Self::process_mark_alert_method(&me2, &method_sub).await {} },
+            );
+
         let mut on_modify = OnModify::new(ex, self.node.clone(), me.clone());
 
-        on_modify.when_change(self.items.clone(), Self::redraw);
-        on_modify.when_change(self.rect.prop(), Self::redraw);
-        on_modify.when_change(self.font_size.prop(), Self::redraw);
-        on_modify.when_change(self.padding.clone(), Self::redraw);
-        on_modify.when_change(self.text_color.prop(), Self::redraw);
-        on_modify.when_change(self.bg_color.prop(), Self::redraw);
-        on_modify.when_change(self.sep_size.prop(), Self::redraw);
-        on_modify.when_change(self.sep_color.prop(), Self::redraw);
+        async fn redraw(self_: Arc<Menu>, batch: BatchGuardPtr) {
+            let atom = &mut batch.spawn();
+            self_.redraw(atom);
+        }
+
+        on_modify.when_change(self.items.clone(), redraw);
+        on_modify.when_change(self.rect.prop(), redraw);
+        on_modify.when_change(self.font_size.prop(), redraw);
+        on_modify.when_change(self.padding.clone(), redraw);
+        on_modify.when_change(self.text_color.prop(), redraw);
+        on_modify.when_change(self.bg_color.prop(), redraw);
+        on_modify.when_change(self.sep_size.prop(), redraw);
+        on_modify.when_change(self.sep_color.prop(), redraw);
 
-        let mut tasks = vec![motion_task];
+        let mut tasks = vec![motion_task, mark_active_task, mark_alert_task];
         tasks.append(&mut on_modify.tasks);
         *self.tasks.lock() = tasks;
     }