Просмотр исходного кода

app: add unstyled darkfi main menu

darkfi 2 дней назад
Родитель
Сommit
146fad4971

+ 1 - 1
bin/app/src/app/schema/chat.rs

@@ -141,7 +141,7 @@ mod ui_consts {
 mod ui_consts {
     // Chat UI
     pub const CHANNEL_LABEL_X: f32 = 100.;
-    pub const CHANNEL_LABEL_Y: f32 = 12.;
+    pub const CHANNEL_LABEL_Y: f32 = 18.;
     pub const BACKARROW_SCALE: f32 = 15.;
     pub const BACKARROW_X: f32 = 38.;
     pub const BACKARROW_Y: f32 = 30.;

+ 64 - 5
bin/app/src/app/schema/menu/mod.rs

@@ -30,7 +30,7 @@ use crate::{
     db::AppDbPtr,
     expr,
     gfx::gfxtag,
-    mesh::COLOR_CYAN,
+    mesh::{COLOR_BLUE, COLOR_CYAN},
     prop::{PropertyAtomicGuard, PropertyBool, PropertyFloat32, Role},
     scene::{SceneNodePtr, Slot},
     sfx, shape,
@@ -45,10 +45,12 @@ use crate::{
 #[cfg(any(target_os = "android", feature = "emulate-android"))]
 mod android_ui_consts {
     pub const CHANNEL_LABEL_X: f32 = 40.;
-    pub const CHANNEL_LABEL_Y: f32 = 35.;
     pub const CHANNEL_HEADER_HEIGHT: f32 = 140.;
     pub const CHANNEL_ITEM_HEIGHT: f32 = 115.;
     pub const CHANNEL_LABEL_FONTSIZE: f32 = 46.;
+    pub const BACKARROW_BG_W: f32 = 140.;
+    pub const CHANNELS_TITLE_X: f32 = BACKARROW_BG_W + CHANNEL_LABEL_X;
+    pub const CHANNELS_TITLE_Y: f32 = 35.;
     pub const MENU_SEP_SIZE: f32 = 3.;
     pub const MENU_HANDLE_PAD: f32 = 200.;
     pub const MENU_FADE: f32 = 1200.;
@@ -83,10 +85,12 @@ mod ui_consts {
 ))]
 mod ui_consts {
     pub const CHANNEL_LABEL_X: f32 = 20.;
-    pub const CHANNEL_LABEL_Y: f32 = 14.;
     pub const CHANNEL_HEADER_HEIGHT: f32 = 60.;
     pub const CHANNEL_ITEM_HEIGHT: f32 = 40.;
     pub const CHANNEL_LABEL_FONTSIZE: f32 = 18.;
+    pub const BACKARROW_BG_W: f32 = 80.;
+    pub const CHANNELS_TITLE_X: f32 = BACKARROW_BG_W + CHANNEL_LABEL_X;
+    pub const CHANNELS_TITLE_Y: f32 = 14.;
     pub const MENU_SEP_SIZE: f32 = 1.;
     pub const MENU_HANDLE_PAD: f32 = 100.;
     pub const MENU_FADE: f32 = 600.;
@@ -264,8 +268,8 @@ pub async fn make(
     // Create some text
     let node = create_text("channels_label");
     let prop = node.get_property("rect").unwrap();
-    prop.set_default_f32(0, CHANNEL_LABEL_X).unwrap();
-    prop.set_default_f32(1, CHANNEL_LABEL_Y).unwrap();
+    prop.set_default_f32(0, CHANNELS_TITLE_X).unwrap();
+    prop.set_default_f32(1, CHANNELS_TITLE_Y).unwrap();
     prop.set_default_f32(2, 1000.).unwrap();
     prop.set_default_f32(3, 200.).unwrap();
     node.set_property_u32(atom, Role::App, "z_index", 1).unwrap();
@@ -291,6 +295,61 @@ pub async fn make(
         .await;
     layer_node.link(node);
 
+    // Main menu button bg (blue box placeholder). Same spot the back
+    // button takes on the chat subscreens. It lives on the menu layer,
+    // so it is only shown on the main chat screen.
+    let node = create_vector_art("main_menu_btn_bg");
+    let prop = node.get_property("rect").unwrap();
+    prop.set_default_f32(0, 0.).unwrap();
+    prop.set_default_f32(1, 0.).unwrap();
+    prop.set_default_f32(2, BACKARROW_BG_W).unwrap();
+    prop.set_default_f32(3, CHANNEL_HEADER_HEIGHT).unwrap();
+    node.set_property_u32(atom, Role::App, "z_index", 3).unwrap();
+    node.set_property_bool(atom, Role::App, "is_visible", true).unwrap();
+    let mut shape = VectorShape::new();
+    shape.add_filled_box(
+        expr::const_f32(0.),
+        expr::const_f32(0.),
+        expr::load_var("w"),
+        expr::load_var("h"),
+        COLOR_BLUE,
+    );
+    node.set_property_shape(atom, Role::App, "shape", shape).unwrap();
+    let node =
+        node.setup(|me| VectorArt::new(me, app.renderer.clone(), app.redraw_trigger.clone())).await;
+    layer_node.link(node);
+
+    // Main menu button
+    let node = create_button("main_menu_btn");
+    node.set_property_bool(atom, Role::App, "is_active", true).unwrap();
+    node.set_property_u32(atom, Role::App, "z_index", 10).unwrap();
+    node.set_property_u32(atom, Role::App, "priority", 10).unwrap();
+    let prop = node.get_property("rect").unwrap();
+    prop.set_default_f32(0, 0.).unwrap();
+    prop.set_default_f32(1, 0.).unwrap();
+    prop.set_default_f32(2, BACKARROW_BG_W).unwrap();
+    prop.set_default_f32(3, CHANNEL_HEADER_HEIGHT).unwrap();
+
+    let sg_root = app.sg_root.clone();
+    let redraw = app.redraw_trigger.clone();
+    let (slot, recvr) = Slot::new("main_menu_clicked");
+    node.register("click", slot).unwrap();
+    let listen_click = app.ex.spawn(async move {
+        while recvr.recv().await.is_ok() {
+            info!(target: "app::menu", "clicked main menu");
+            let main_menu_layer = sg_root.lookup_node("/window/content/main_menu_layer").unwrap();
+            let main_menu_is_visible =
+                PropertyBool::wrap(&main_menu_layer, Role::App, "is_visible", 0).unwrap();
+            let atom = &mut redraw.make_guard(gfxtag!("main menu toggle"));
+            main_menu_is_visible.set(atom, !main_menu_is_visible.get());
+        }
+    });
+    app.tasks.lock().push(listen_click);
+
+    let node =
+        node.setup(|me| Button::new(me, app.renderer.clone(), app.redraw_trigger.clone())).await;
+    layer_node.link(node);
+
     // Main button layer
     let node = create_layer("mainbtn_layer");
     let prop = node.get_property("rect").unwrap();

+ 106 - 1
bin/app/src/app/schema/mod.rs

@@ -30,6 +30,7 @@ use crate::{
     db::AppDbPtr,
     expr::{self, Compiler},
     gfx::gfxtag,
+    mesh::COLOR_GREEN,
     prop::{PropertyAtomicGuard, PropertyEnum, PropertyFloat32, PropertyStr, Role},
     scene::{SceneNodePtr, Slot},
     sfx, shape,
@@ -58,6 +59,9 @@ macro_rules! e { ($($arg:tt)*) => { error!(target: "app::schema", $($arg)*); } }
 
 #[cfg(any(target_os = "android", feature = "emulate-android"))]
 mod android_ui_consts {
+    pub const MAIN_MENU_HEADER_HEIGHT: f32 = 140.;
+    pub const MAIN_MENU_PADDING: f32 = 20.;
+    pub const SWITCH_THEME_BTN_H: f32 = 100.;
     pub const NETSTATUS_ICON_SIZE: f32 = 140.;
     pub const SETTINGS_ICON_SIZE: f32 = 140.;
     pub const NETLOGO_SCALE: f32 = 50.;
@@ -147,6 +151,9 @@ mod ui_consts {
     not(feature = "emulate-android")
 ))]
 mod ui_consts {
+    pub const MAIN_MENU_HEADER_HEIGHT: f32 = 60.;
+    pub const MAIN_MENU_PADDING: f32 = 10.;
+    pub const SWITCH_THEME_BTN_H: f32 = 50.;
     pub const NETSTATUS_ICON_SIZE: f32 = 60.;
     pub const SETTINGS_ICON_SIZE: f32 = 60.;
     pub const NETLOGO_SCALE: f32 = 25.;
@@ -255,6 +262,8 @@ pub async fn make(
     app_db: AppDbPtr,
 ) {
     let mut cc = Compiler::new();
+    cc.add_const_f32("MAIN_MENU_PADDING", MAIN_MENU_PADDING);
+    cc.add_const_f32("MAIN_MENU_HEADER_HEIGHT", MAIN_MENU_HEADER_HEIGHT);
     cc.add_const_f32("NETSTATUS_ICON_SIZE", NETSTATUS_ICON_SIZE);
     cc.add_const_f32("SETTINGS_ICON_SIZE", SETTINGS_ICON_SIZE);
     cc.add_const_f32("NETSTAT_OVERLAY_MARGIN", NETSTAT_OVERLAY_MARGIN);
@@ -290,6 +299,102 @@ pub async fn make(
         content.setup(|me| Layer::new(me, app.renderer.clone(), app.redraw_trigger.clone())).await;
     window.link(content.clone());
 
+    // Main menu overlay, toggled by the main_menu_btn shown on the main
+    // wallet and main chat screens. Sits above both sections.
+    let main_menu_layer = create_layer("main_menu_layer");
+    let prop = main_menu_layer.get_property("rect").unwrap();
+    prop.set_default_f32(0, 0.).unwrap();
+    prop.set_default_f32(1, MAIN_MENU_HEADER_HEIGHT).unwrap();
+    prop.set_default_expr(2, expr::load_var("w")).unwrap();
+    let code = cc.compile("h - MAIN_MENU_HEADER_HEIGHT").unwrap();
+    prop.set_default_expr(3, code).unwrap();
+    main_menu_layer.set_property_bool(atom, Role::App, "is_visible", false).unwrap();
+    main_menu_layer.set_property_u32(atom, Role::App, "z_index", 10).unwrap();
+    main_menu_layer.set_property_u32(atom, Role::App, "priority", 10).unwrap();
+    let main_menu_layer = main_menu_layer
+        .setup(|me| Layer::new(me, app.renderer.clone(), app.redraw_trigger.clone()))
+        .await;
+    content.link(main_menu_layer.clone());
+
+    // Menu box: starts below the title bar, fills half the screen width
+    // and the total screen height. Blue placeholder fill.
+    let node = create_vector_art("menu_box");
+    let prop = node.get_property("rect").unwrap();
+    prop.set_default_f32(0, 0.).unwrap();
+    prop.set_default_f32(1, 0.).unwrap();
+    prop.set_default_expr(2, expr::load_var("w")).unwrap();
+    prop.set_default_expr(3, expr::load_var("h")).unwrap();
+    node.set_property_bool(atom, Role::App, "is_visible", true).unwrap();
+    node.set_property_u32(atom, Role::App, "z_index", 0).unwrap();
+    let mut shape = VectorShape::new();
+    shape.add_filled_box(
+        expr::const_f32(0.),
+        expr::const_f32(0.),
+        expr::load_var("w"),
+        expr::load_var("h"),
+        [0.2, 0.2, 0.2, 0.6],
+    );
+    node.set_property_shape(atom, Role::App, "shape", shape).unwrap();
+    let node =
+        node.setup(|me| VectorArt::new(me, app.renderer.clone(), app.redraw_trigger.clone())).await;
+    main_menu_layer.link(node);
+
+    // Switch theme button (green bg). Toggles /setting/theme between
+    // the scifi and minimal themes; the engine-owned watcher applies
+    // the live switch.
+    let node = create_vector_art("switch_theme_btn_bg");
+    let prop = node.get_property("rect").unwrap();
+    prop.set_default_f32(0, MAIN_MENU_PADDING).unwrap();
+    prop.set_default_f32(1, MAIN_MENU_HEADER_HEIGHT + MAIN_MENU_PADDING).unwrap();
+    let code = cc.compile("w / 2 - 2 * MAIN_MENU_PADDING").unwrap();
+    prop.set_default_expr(2, code).unwrap();
+    prop.set_default_f32(3, SWITCH_THEME_BTN_H).unwrap();
+    node.set_property_bool(atom, Role::App, "is_visible", true).unwrap();
+    node.set_property_u32(atom, Role::App, "z_index", 1).unwrap();
+    let mut shape = VectorShape::new();
+    shape.add_filled_box(
+        expr::const_f32(0.),
+        expr::const_f32(0.),
+        expr::load_var("w"),
+        expr::load_var("h"),
+        COLOR_GREEN,
+    );
+    node.set_property_shape(atom, Role::App, "shape", shape).unwrap();
+    let node =
+        node.setup(|me| VectorArt::new(me, app.renderer.clone(), app.redraw_trigger.clone())).await;
+    main_menu_layer.link(node);
+
+    let node = create_button("switch_theme_btn");
+    node.set_property_bool(atom, Role::App, "is_active", true).unwrap();
+    node.set_property_u32(atom, Role::App, "z_index", 2).unwrap();
+    let prop = node.get_property("rect").unwrap();
+    prop.set_default_f32(0, MAIN_MENU_PADDING).unwrap();
+    prop.set_default_f32(1, MAIN_MENU_HEADER_HEIGHT + MAIN_MENU_PADDING).unwrap();
+    let code = cc.compile("w / 2 - 2 * MAIN_MENU_PADDING").unwrap();
+    prop.set_default_expr(2, code).unwrap();
+    prop.set_default_f32(3, SWITCH_THEME_BTN_H).unwrap();
+
+    let sg_root = app.sg_root.clone();
+    let redraw = app.redraw_trigger.clone();
+    let ex = app.ex.clone();
+    let (slot, recvr) = Slot::new("switch_theme_clicked");
+    node.register("click", slot).unwrap();
+    let listen_click = ex.spawn(async move {
+        while recvr.recv().await.is_ok() {
+            i!("clicked switch theme");
+            let setting_node = sg_root.lookup_node("/setting").unwrap();
+            let theme_prop = PropertyEnum::wrap(&setting_node, Role::User, "theme", 0).unwrap();
+            let next = if theme_prop.get() == "scifi" { "minimal" } else { "scifi" };
+            let atom = &mut redraw.make_guard(gfxtag!("switch theme"));
+            theme_prop.set(atom, next);
+        }
+    });
+    app.tasks.lock().push(listen_click);
+
+    let node =
+        node.setup(|me| Button::new(me, app.renderer.clone(), app.redraw_trigger.clone())).await;
+    main_menu_layer.link(node);
+
     // The first-run splash and the king video background are theme
     // content, not baseline structure: the scifi theme owns both (see
     // `theme::scifi`, design D12). The minimal baseline ships
@@ -890,7 +995,7 @@ pub async fn make(
         atom,
         Role::App,
         "text",
-        indoc! {"
+        indoc::indoc! {"
             0  tcp+tls://dasman.xyz:9600
             1  tcp+tls://dasman.xyz:9600
             2  tcp+tls://dasman.xyz:9600

+ 61 - 2
bin/app/src/app/schema/wallet/main.rs

@@ -25,7 +25,7 @@ use crate::{
     },
     expr,
     gfx::{gfxtag, Point},
-    mesh::{COLOR_CYAN, COLOR_TEAL},
+    mesh::{COLOR_BLUE, COLOR_CYAN, COLOR_TEAL},
     prop::{PropertyAtomicGuard, PropertyBool, PropertyFloat32, Role},
     scene::{SceneNodePtr, Slot},
     shape,
@@ -221,7 +221,64 @@ pub async fn make(
     wallet_layer.link(node.clone());
     let back_btn_is_active = PropertyBool::wrap(&node, Role::App, "is_active", 0).unwrap();
 
-    // Show the back button whenever the main wallet screen is hidden
+    // Main menu button bg (blue box placeholder). Same spot the back
+    // button takes on the wallet subscreens.
+    let node = create_vector_art("main_menu_btn_bg");
+    let prop = node.get_property("rect").unwrap();
+    prop.set_default_f32(0, 0.).unwrap();
+    prop.set_default_f32(1, 0.).unwrap();
+    prop.set_default_f32(2, BACKARROW_BG_W).unwrap();
+    prop.set_default_f32(3, HEADER_HEIGHT).unwrap();
+    node.set_property_u32(atom, Role::App, "z_index", 3).unwrap();
+    node.set_property_bool(atom, Role::App, "is_visible", true).unwrap();
+    let mut shape = VectorShape::new();
+    shape.add_filled_box(
+        expr::const_f32(0.),
+        expr::const_f32(0.),
+        expr::load_var("w"),
+        expr::load_var("h"),
+        COLOR_BLUE,
+    );
+    node.set_property_shape(atom, Role::App, "shape", shape).unwrap();
+    let node =
+        node.setup(|me| VectorArt::new(me, app.renderer.clone(), app.redraw_trigger.clone())).await;
+    wallet_layer.link(node.clone());
+    let main_menu_bg_is_visible = PropertyBool::wrap(&node, Role::App, "is_visible", 0).unwrap();
+
+    // Main menu button, only active on the main wallet screen
+    let node = create_button("main_menu_btn");
+    node.set_property_bool(atom, Role::App, "is_active", true).unwrap();
+    node.set_property_u32(atom, Role::App, "z_index", 10).unwrap();
+    node.set_property_u32(atom, Role::App, "priority", 10).unwrap();
+    let prop = node.get_property("rect").unwrap();
+    prop.set_default_f32(0, 0.).unwrap();
+    prop.set_default_f32(1, 0.).unwrap();
+    prop.set_default_f32(2, BACKARROW_BG_W).unwrap();
+    prop.set_default_f32(3, HEADER_HEIGHT).unwrap();
+
+    let sg_root = app.sg_root.clone();
+    let redraw = app.redraw_trigger.clone();
+    let (slot, recvr) = Slot::new("main_menu_clicked");
+    node.register("click", slot).unwrap();
+    let listen_click = app.ex.spawn(async move {
+        while recvr.recv().await.is_ok() {
+            info!(target: "app::wallet", "clicked main menu");
+            let main_menu_layer = sg_root.lookup_node("/window/content/main_menu_layer").unwrap();
+            let main_menu_is_visible =
+                PropertyBool::wrap(&main_menu_layer, Role::App, "is_visible", 0).unwrap();
+            let atom = &mut redraw.make_guard(gfxtag!("main menu toggle"));
+            main_menu_is_visible.set(atom, !main_menu_is_visible.get());
+        }
+    });
+    app.tasks.lock().push(listen_click);
+
+    let node =
+        node.setup(|me| Button::new(me, app.renderer.clone(), app.redraw_trigger.clone())).await;
+    wallet_layer.link(node.clone());
+    let main_menu_btn_is_active = PropertyBool::wrap(&node, Role::App, "is_active", 0).unwrap();
+
+    // Show the back button whenever the main wallet screen is hidden,
+    // and the main menu button whenever it is shown
     let redraw = app.redraw_trigger.clone();
     let main_is_visible2 = main_is_visible.clone();
     let main_is_visible_sub = main_is_visible.prop().subscribe_modify();
@@ -231,6 +288,8 @@ pub async fn make(
             let visible = !main_is_visible2.get();
             back_bg_is_visible.set(atom, visible);
             back_btn_is_active.set(atom, visible);
+            main_menu_bg_is_visible.set(atom, !visible);
+            main_menu_btn_is_active.set(atom, !visible);
         }
     });
     app.tasks.lock().push(listen_main_visible);