choices options UI

This commit is contained in:
aOK 2024-05-14 09:40:13 +03:00
parent 0adbfdf84c
commit a817794709
35 changed files with 5459 additions and 92 deletions

BIN
nimanyatta_v001/.DS_Store vendored Normal file

Binary file not shown.

View file

@ -1,3 +1,4 @@
/dist/ /dist/
/target/ /target/
/Cargo.lock /Cargo.lock
extra.txt

View file

@ -3,5 +3,23 @@ name = "nimanyatta_v001"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
[lib]
name = "nimanyatta_v001"
crate-type = ["staticlib", "cdylib", "rlib"]
[dependencies] [dependencies]
makepad-widgets = { git = "https://github.com/makepad/makepad", branch = "rik" } # makepad-widgets = { git = "https://github.com/makepad/makepad", branch = "rik", version = "0.6.0"}
makepad-widgets = { path = "../makepad/widgets", version = "0.6.0" }
makepad-draw = { path = "../makepad/draw", version = "0.6.0" }
# makepad-derive-widget = { path = "../makepad/widgets/derive_widget", version = "0.4.0" }
# makepad-derive-widget = { version = "0.4.0" }
makepad-derive-widget = { path = "../makepad/widgets/derive_widget", version = "0.4.0" }
makepad-wasm-bridge = { path = "../makepad/libs/wasm_bridge", version = "0.4.0" }
# makepad-widgets = { path = "../makepad/widgets" }
# makepad-widgets = { git = "https://github.com/makepad/makepad", branch = "rik" }
# [dependencies.nimanyatta_v001]
# version = "0.1.0"
# default-features = false
# features = ["wasm32"]

View file

@ -0,0 +1,31 @@
# Desktop in Debug Mode
cargo run
MAKEPAD=lines sudo cargo +nightly run nimanyatta_v001
# Desktop in Release Mode
cargo run --release
# Desktop in small size
cargo run --profile=small
# Android
cargo makepad android run --release
sudo cargo makepad android run -p nimanyatta_v001 --release
You can also customize the package name and application label
```
cargo run -p cargo-makepad --release -- android --package-name=com.yourcompany.myapp --app-label="My Example App" run -p makepad-example-ironfish
```
# wasm
sudo cargo makepad wasm run -p nimanyatta_v001 --release
RUSTFLAGS="-C linker-plugin-lto -C embed-bitcode=yes -C
codegen-units=1 -C opt-level=z" sudo cargo +nightly build -p nimanyatta_v001 --target=wasm32-unknown-unknown --release
cargo +stable run -p wasm_strip --release -- target/wasm32-unknown-unknown/release/nimanyatta_v001.wasm

BIN
nimanyatta_v001/resources/.DS_Store vendored Normal file

Binary file not shown.

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<path fill="#000" d="M11.025 13.25a.748.748 0 0 1-1.281.53l-5.25-5.264a.75.75 0 0 1 0-1.06L9.717 2.22a.75.75 0 1 1 1.062 1.06L6.084 7.986l4.722 4.734a.748.748 0 0 1 .219.53z"></path>
</svg>

After

Width:  |  Height:  |  Size: 290 B

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path fill="#000" d="M18.53 17.47a.75.75 0 1 1-1.06 1.06L12 13.06l-5.47 5.47a.748.748 0 0 1-1.06 0 .75.75 0 0 1 0-1.06L10.94 12 5.47 6.53a.75.75 0 1 1 1.06-1.06L12 10.94l5.47-5.47a.75.75 0 1 1 1.06 1.06L13.06 12l5.47 5.47z"></path>
</svg>

After

Width:  |  Height:  |  Size: 339 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path fill="#000" d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2zM6.858 18.752c.605-1.868 2.722-3.24 5.142-3.24 2.42 0 4.537 1.372 5.142 3.24C15.712 19.844 13.933 20.5 12 20.5s-3.712-.656-5.142-1.748zm11.469-1.095c-1.02-2.165-3.483-3.645-6.327-3.645s-5.307 1.48-6.327 3.645A8.456 8.456 0 0 1 3.5 12c0-4.687 3.813-8.5 8.5-8.5 4.687 0 8.5 3.813 8.5 8.5a8.456 8.456 0 0 1-2.173 5.657zM12 6a3.5 3.5 0 1 0 0 7 3.5 3.5 0 0 0 0-7zm0 5.5c-1.103 0-2-.897-2-2s.897-2 2-2 2 .897 2 2-.897 2-2 2z"></path>
</svg>

After

Width:  |  Height:  |  Size: 627 B

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="6" fill="#fff"></circle>
<path fill="#3E6AE1" d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2zm.75 14.25a.75.75 0 0 1-1.5 0v-4.5a.75.75 0 0 1 1.5 0v4.5zM12 10a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"></path>
</svg>

After

Width:  |  Height:  |  Size: 357 B

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path fill="#000" d="M18.53 17.47a.75.75 0 1 1-1.06 1.06L12 13.06l-5.47 5.47a.748.748 0 0 1-1.06 0 .75.75 0 0 1 0-1.06L10.94 12 5.47 6.53a.75.75 0 1 1 1.06-1.06L12 10.94l5.47-5.47a.75.75 0 1 1 1.06 1.06L13.06 12l5.47 5.47z"></path>
</svg>

After

Width:  |  Height:  |  Size: 339 B

View file

@ -1,4 +1,6 @@
// use crate::shared::stack_view_action::StackViewAction;
use makepad_widgets::*; use makepad_widgets::*;
// use std::collections::HashMap;
live_design! { live_design! {
@ -7,12 +9,20 @@ live_design! {
import makepad_widgets::theme_desktop_dark::*; import makepad_widgets::theme_desktop_dark::*;
import crate::home::home_screen::HomeScreen; import crate::home::home_screen::HomeScreen;
import crate::default_choice::default_choice_screen::DefaultChoiceScreen; import crate::default_choice::default_choice_screen::DefaultChoice;
// import crate::default_choice::choose::Choose;
import crate::two::two_screen::TwoScreen; import crate::two::two_screen::TwoScreen;
import crate::three::three_screen::ThreeScreen; import crate::three::three_screen::ThreeScreen;
import crate::four::four_screen::FourScreen; import crate::four::four_screen::FourScreen;
import crate::five::five_screen::FiveScreen; import crate::five::five_screen::FiveScreen;
import crate::shared::styles::*; import crate::shared::styles::*;
import crate::shared::steps::Choices;
import crate::shared::steps::ChoicesComponent;
import crate::shared::popup_menu::MenuItem;
// import crate::shared::header::DropDown;
// import crate::shared::stack_navigation::*;ChoicesComponent
REGULAR_TEXT = { REGULAR_TEXT = {
font_size: (12), font_size: (12),
@ -21,7 +31,7 @@ live_design! {
ICON_CHAT = dep("crate://self/resources/icons/chat.svg") ICON_CHAT = dep("crate://self/resources/icons/chat.svg")
PHONE_CHAT = dep("crate://self/resources/icons/chat.svg") PHONE_CHAT = dep("crate://self/resources/icons/chat.svg")
AppTab = <RadioButton> { AppTab2 = <RadioButton> {
width: Fit, width: Fit,
height: Fill, height: Fill,
flow: Right, flow: Right,
@ -31,8 +41,43 @@ live_design! {
// color: #000 // color: #000
draw_radio: { draw_radio: {
radio_type: Tab, radio_type: Tab,
color_active: #fff,
color_inactive: #fff, instance border_width: 0.0
instance border_color: #0000
instance inset: vec4(0.0, 0.0, 0.0, 0.0)
instance radius: 2.5
fn get_color(self) -> vec4 {
return mix(
mix(
(SIDEBAR_BG_COLOR),
(SIDEBAR_BG_COLOR_HOVER),
self.hover
),
(SIDEBAR_BG_COLOR_SELECTED),
self.selected
)
}
fn get_border_color(self) -> vec4 {
return self.border_color
}
fn pixel(self) -> vec4 {
let sdf = Sdf2d::viewport(self.pos * self.rect_size)
sdf.box(
self.inset.x + self.border_width,
self.inset.y + self.border_width,
self.rect_size.x - (self.inset.x + self.inset.z + self.border_width * 2.0),
self.rect_size.y - (self.inset.y + self.inset.w + self.border_width * 2.0),
max(1.0, self.radius)
)
sdf.fill_keep(self.get_color())
if self.border_width > 0.0 {
sdf.stroke(self.get_border_color(), self.border_width)
}
return sdf.result;
}
} }
draw_text: { draw_text: {
color_selected: #000, //#0b0, color_selected: #000, //#0b0,
@ -41,9 +86,114 @@ live_design! {
text_style: <H3_TEXT_REGULAR> {} text_style: <H3_TEXT_REGULAR> {}
} }
} }
SIDEBAR_FONT_COLOR = #344054
SIDEBAR_FONT_COLOR_HOVER = #344054
SIDEBAR_FONT_COLOR_SELECTED = #127487
// SIDEBAR_BG_COLOR = #F8F8F8
SIDEBAR_BG_COLOR = #f
SIDEBAR_BG_COLOR_HOVER = #E2F1F199
SIDEBAR_BG_COLOR_SELECTED = #E2F1F199
AppTab = <RadioButton> {
width: 80,
height: 70,
padding: 0, margin: 0,
flow: Down, spacing: 8.0, align: {x: 0.5, y: 0.5}
icon_walk: {margin: 0, width: 30, height: 30}
label_walk: {margin: 0}
draw_radio: {
radio_type: Tab,
instance border_width: 0.0
instance border_color: #0000
instance inset: vec4(0.0, 0.0, 0.0, 0.0)
instance radius: 2.5
fn get_color(self) -> vec4 {
return mix(
mix(
(SIDEBAR_BG_COLOR),
(SIDEBAR_BG_COLOR_HOVER),
self.hover
),
(SIDEBAR_BG_COLOR_SELECTED),
self.selected
)
}
fn get_border_color(self) -> vec4 {
return self.border_color
}
fn pixel(self) -> vec4 {
let sdf = Sdf2d::viewport(self.pos * self.rect_size)
sdf.box(
self.inset.x + self.border_width,
self.inset.y + self.border_width,
self.rect_size.x - (self.inset.x + self.inset.z + self.border_width * 2.0),
self.rect_size.y - (self.inset.y + self.inset.w + self.border_width * 2.0),
max(1.0, self.radius)
)
sdf.fill_keep(self.get_color())
if self.border_width > 0.0 {
sdf.stroke(self.get_border_color(), self.border_width)
}
return sdf.result;
}
}
draw_text: {
color_unselected: (SIDEBAR_FONT_COLOR)
color_unselected_hover: (SIDEBAR_FONT_COLOR_HOVER)
color_selected: (SIDEBAR_FONT_COLOR_SELECTED)
fn get_color(self) -> vec4 {
return mix(
mix(
self.color_unselected,
self.color_unselected_hover,
self.hover
),
self.color_selected,
self.selected
)
}
}
draw_icon: {
instance color_unselected: (SIDEBAR_FONT_COLOR)
instance color_unselected_hover: (SIDEBAR_FONT_COLOR_HOVER)
instance color_selected: (SIDEBAR_FONT_COLOR_SELECTED)
fn get_color(self) -> vec4 {
return mix(
mix(
self.color_unselected,
self.color_unselected_hover,
self.hover
),
self.color_selected,
self.selected
)
}
}
}
App = {{App}} { App = {{App}} {
ui: <Window> { ui: <Window> {
caption_bar = { margin: {left: -100}, visible: true, caption_label = {label = {text: "NiManyatta"}} }, caption_bar = {
margin: {
left: -100
},
visible: true,
caption_label = {
label = {
text: "NiManyatta"
}
}
},
window: {inner_size: vec2(1280, 1000)},
body = { body = {
show_bg: true show_bg: true
@ -80,12 +230,14 @@ live_design! {
} }
mobile_modes = <View> { mobile_modes = <View> {
// spacing: 20
<View> {
// padding: { right: 60 }
home_tab = <AppTab> { home_tab = <AppTab> {
flow: Right, flow: Right,
align: {x: 0.0, y: 0.5} align: {x: 0.0, y: 0.5}
animator: {selected = {default: on}} animator: {selected = {default: on}}
label: "" text: ""
draw_icon: { draw_icon: {
svg_file: (ICON_CHAT), svg_file: (ICON_CHAT),
fn get_color(self) -> vec4 { fn get_color(self) -> vec4 {
@ -96,15 +248,18 @@ live_design! {
) )
} }
} }
width: Fill, width: 80.0,
icon_walk: {width: 20, height: 20} icon_walk: {width: 20, height: 20}
flow: Right, spacing: 5.0, align: {x: 0.2, y: 0.5} flow: Right, spacing: 5.0, align: {x: 0.5, y: 0.5}
} }
}
<View> {
align: {x: 0.5}
two_tab = <AppTab> { two_tab = <AppTab> {
flow: Right, flow: Right,
align: {x: 0.0, y: 0.5} align: {x: 0.0, y: 0.5}
animator: {selected = {default: on}} // animator: {selected = {default: on}}
label: "2" text: "2"
draw_icon: { draw_icon: {
svg_file: (ICON_CHAT), svg_file: (ICON_CHAT),
fn get_color(self) -> vec4 { fn get_color(self) -> vec4 {
@ -115,15 +270,18 @@ live_design! {
) )
} }
} }
width: Fill, width: 80.0,
icon_walk: {width: 20, height: 20} icon_walk: {width: 20, height: 20}
flow: Down, spacing: 5.0, align: {x: 0.5, y: 0.5} flow: Down, spacing: 5.0, align: {x: 0.5, y: 0.5}
} }
}
<View> {
align: {x: 0.5}
three_tab = <AppTab> { three_tab = <AppTab> {
flow: Right, flow: Right,
align: {x: 0.0, y: 0.5} align: {x: 0.0, y: 0.5}
animator: {selected = {default: on}} // animator: {selected = {default: on}}
label: "3" text: "3"
draw_icon: { draw_icon: {
svg_file: (ICON_CHAT), svg_file: (ICON_CHAT),
fn get_color(self) -> vec4 { fn get_color(self) -> vec4 {
@ -134,15 +292,18 @@ live_design! {
) )
} }
} }
width: Fill, width: 80.0,
icon_walk: {width: 20, height: 20} icon_walk: {width: 20, height: 20}
flow: Down, spacing: 5.0, align: {x: 0.5, y: 0.5} flow: Down, spacing: 5.0, align: {x: 0.5, y: 0.5}
} }
}
<View> {
align: {x: 0.5}
four_tab = <AppTab> { four_tab = <AppTab> {
flow: Right, flow: Right,
align: {x: 0.0, y: 0.5} align: {x: 0.0, y: 0.5}
animator: {selected = {default: on}} // animator: {selected = {default: on}}
label: "4" text: "4"
draw_icon: { draw_icon: {
svg_file: (ICON_CHAT), svg_file: (ICON_CHAT),
fn get_color(self) -> vec4 { fn get_color(self) -> vec4 {
@ -153,15 +314,18 @@ live_design! {
) )
} }
} }
width: Fill, width: 80.0,
icon_walk: {width: 20, height: 20} icon_walk: {width: 20, height: 20}
flow: Down, spacing: 5.0, align: {x: 0.5, y: 0.5} flow: Down, spacing: 5.0, align: {x: 0.5, y: 0.5}
} }
}
<View> {
align: {x: 1.0}
five_tab = <AppTab> { five_tab = <AppTab> {
flow: Right, flow: Right,
align: {x: 0.0, y: 0.5} align: {x: 0.0, y: 0.5}
animator: {selected = {default: on}} // animator: {selected = {default: on}}
label: "" text: ""
draw_icon: { draw_icon: {
svg_file: (PHONE_CHAT), svg_file: (PHONE_CHAT),
fn get_color(self) -> vec4 { fn get_color(self) -> vec4 {
@ -172,9 +336,10 @@ live_design! {
) )
} }
} }
width: Fill, width: 80.0,
icon_walk: {width: 20, height: 20} icon_walk: {width: 20, height: 20}
flow: Right, spacing: 5.0, align: {x: 0.8, y: 0.5} flow: Right, spacing: 5.0, align: {x: 0.5, y: 0.5}
}
} }
} }
@ -184,10 +349,10 @@ live_design! {
padding: 0.0 padding: 0.0
home_frame = <HomeScreen> { visible: true } home_frame = <HomeScreen> { visible: true }
kimanyatta_frame = <TwoScreen> {visible: false} two_frame = <TwoScreen> {visible: false}
insurance_frame = <ThreeScreen> {visible: false} three_frame = <ThreeScreen> {visible: false}
matatu_frame = <FourScreen> {visible: false} four_frame = <FourScreen> {visible: false}
contactus_frame = <FiveScreen> {visible: false} five_frame = <FiveScreen> {visible: false}
} }
} }
@ -197,12 +362,15 @@ live_design! {
title_container = { title_container = {
title = { title = {
text: "Help Me Choose" text: "Help Me Choose"
draw_text: {
color: #000
}
} }
} }
} }
} }
body = { body = {
<DefaultChoiceScreen> {} <DefaultChoice> {}
} }
} }
} }
@ -217,6 +385,8 @@ app_main!(App);
pub struct App { pub struct App {
#[live] #[live]
ui: WidgetRef, ui: WidgetRef,
// #[rust]
// navigation_destinations: HashMap<StackViewAction, LiveId>,
} }
impl LiveRegister for App { impl LiveRegister for App {
@ -224,6 +394,8 @@ impl LiveRegister for App {
crate::makepad_widgets::live_design(cx); crate::makepad_widgets::live_design(cx);
crate::home::home_screen::live_design(cx); crate::home::home_screen::live_design(cx);
crate::default_choice::default_choice_screen::live_design(cx); crate::default_choice::default_choice_screen::live_design(cx);
crate::default_choice::choice::live_design(cx);
// crate::default_choice::choose::live_design(cx);
crate::two::two_screen::live_design(cx); crate::two::two_screen::live_design(cx);
crate::three::three_screen::live_design(cx); crate::three::three_screen::live_design(cx);
crate::four::four_screen::live_design(cx); crate::four::four_screen::live_design(cx);
@ -231,12 +403,20 @@ impl LiveRegister for App {
crate::shared::styles::live_design(cx); crate::shared::styles::live_design(cx);
crate::shared::custom_button::live_design(cx); crate::shared::custom_button::live_design(cx);
crate::shared::round_slider::live_design(cx);
crate::shared::steps::live_design(cx);
crate::shared::cho::live_design(cx);
crate::shared::popup_menu::live_design(cx);
crate::shared::header::live_design(cx);
crate::shared::helpers::live_design(cx);
crate::shared::dropdown_menu::live_design(cx);
} }
} }
impl LiveHook for App { impl LiveHook for App {
// fn after_new_from_doc(&mut self, _cx: &mut Cx) { // fn after_new_from_doc(&mut self, _cx: &mut Cx) {
// // self.init_navigation_destinations(); // self.init_navigation_destinations();
// } // }
fn after_new_from_doc(&mut self, _cx: &mut Cx) { fn after_new_from_doc(&mut self, _cx: &mut Cx) {
println!("after_new_from_doc(): starting some kind of a loop"); println!("after_new_from_doc(): starting some kind of a loop");
@ -287,6 +467,16 @@ impl MatchEvent for App {
let mut navigation = self.ui.stack_navigation(id!(navigation)); let mut navigation = self.ui.stack_navigation(id!(navigation));
navigation.handle_stack_view_actions(cx, &actions); navigation.handle_stack_view_actions(cx, &actions);
for action in actions {
if let WindowAction::WindowGeomChange(ce) = action.as_widget_action().cast() {
let screen_width = ce.new_geom.inner_size.x * ce.new_geom.dpi_factor;
log!("x ===> {}", ce.new_geom.inner_size.x);
log!("dpi_factor ===> {}", ce.new_geom.dpi_factor);
log!("SCREEN_WIDTH ===> {}", screen_width);
}
}
} }
} }
@ -296,3 +486,26 @@ impl AppMain for App {
self.ui.handle_event(cx, event, &mut Scope::empty()); self.ui.handle_event(cx, event, &mut Scope::empty());
} }
} }
// impl Widget for App {
// fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
// // let label = self.view.label(id!(footer));
// // let cap = self.ui.widget(id!(caption_bar));
// // cap.apply_value_instance(cx, apply, index, nodes)
// DrawStep::done()
// }
// }
impl App {
async fn _do_network_request(_cx: CxRef, _ui: WidgetRef, _url: &str) -> String {
"".to_string()
}
// fn handle_event(&mut self, cx: &mut Cx, event: &Event) {
// if let Event::Draw(event) = event {
// return self.ui.draw_widget_all(&mut Cx2d::new(cx, event));
// }
// let actions = self.ui.handle_widget_event(cx, event);
// }
}

View file

@ -0,0 +1,682 @@
// use makepad_widgets::*;
use crate::makepad_widgets::*;
const CHOICE_MAX_OFFSET: f64 = 700.0;
const SELECT_MAX_OFFSET: f64 = 500.0;
// use crate::shared::custom_button::CustomButton;
live_design! {
import makepad_draw::shader::std::*;
import makepad_widgets::base::*;
import makepad_widgets::theme_desktop_dark::*;
import crate::shared::styles::*;
import crate::shared::steps::StepNav;
import crate::shared::custom_button::CustomButton;
Indicator = <RoundedView> {
width: 100.0
height: 8.0
draw_bg: {
color: #f60,
radius: 2.5
}
}
ButChoices = <View>{
flow: Down,
show_bg: false
draw_bg: {
color: #000
}
align: {x: 0.5, y: 0.5}
but = <CustomButton> {
width: 300, height: 45,
text: "Option 1"
draw_bg: {
border_radius: 2.
}
}
}
SelectOptions = {{SelectOptions}} {
// page_titles: []
width: Fit, height: Fit,
// indicator_titles: []
flow: Down,
spacing: 10,
align: {x: 0.5, y: 0.5},
select_choices_values: []
selects_template: <ButChoices> {
flow: Down,
}
choice_page_offset: 0.0
animator: {
selectopt = {
default: restart,
restart = {
from: {all: Snap}
apply: {choice_page_offset: 500.0}
}
show = {
redraw: false,
from: {all: Forward {duration: 0}}
apply: {choice_page_offset: 0.0}
}
}
}
}
OptionC = {{Indicators}} {
width: Fit,
height: Fit,
flow: Down,
spacing: 5,
align: {x: 0.5, y: 0.5},
indicator_titles: []
template: <CustomButton> {
width: 100, height: Fit,
// margin: 0, padding: 0
text: "Option 1"
draw_bg: {
border_radius: 2.
}
}
}
StepsScreen = <View> {
// width: 300,
height: Fit,
flow: Down,
padding: 10.0
align: {x: 0.5, y: 0.5}
padding: 20,
spacing: 20,
step_title = <Label> {
// margin: {top: 1}
draw_text: {
text_style: <H2_TEXT_BOLD> {},
color: (COLOR_DOWN_6)
}
text: "Step One"
}
// <ButChoices> {}
<SelectOptions> {
// select_choices_values: [
// ["STP1-Option 1","STP1-Option 2","STP1-Option 3"],
// ["STP2-Option 1", "STP2-Option 2", "STP2-Option 3", "STP2-Option 4"],
// ["STP3-Option 1", "STP3-Option 2", "STP3-Option 3", "STP3-Option 4", "STP3-Option 5"]
// ]
select_choices_values: []
}
next = <CustomButton> {
width: 300, height: 50,
// margin: 0, padding: 0
text: "Next"
draw_bg: {
border_radius: 2.
fn pixel(self) -> vec4 {
let border_color = #0157c0;
// let border_color = #016def; //#0157c0
let border_width = 0.5;
let sdf = Sdf2d::viewport(self.pos * self.rect_size);
let body = mix(mix(#0157c0, #f, self.hover), #0157c0, self.pressed);
sdf.box(
1.,
1.,
self.rect_size.x - 2.0,
self.rect_size.y - 2.0,
self.border_radius
)
sdf.fill_keep(body)
sdf.stroke(
border_color,
border_width
)
return sdf.result
}
}
}
}
IndicatorCheckBox = <CheckBox> {
width: Fill,
height: 35,
margin: {left: 1},
label_walk: {margin: {top: 15}}
draw_check: {
uniform size: 3.5;
instance open: 0.0
uniform length: 3.0
uniform width: 1.0
fn pixel(self) -> vec4 {
let sdf = Sdf2d::viewport(self.pos * self.rect_size)
match self.check_type {
CheckType::Check => {
let sz = self.size;
let left = sz + 1.;
let up = sz + 7;
let c = self.rect_size * vec2(0.5, 0.5);
sdf.box(
left,
c.y - up,
25. * sz + 5,
1.8 * sz,
1.8
);
sdf.fill(#232323);
sdf.stroke(#000, 0.5 + 0.5 * self.dpi_dilate);
let isz = sz * 0.5;
let ileft = isz + 3;
let iup = sz + 7;
sdf.box(
ileft,
c.y - iup,
25. * sz + 5,
1.8 * sz,
1.8
);
sdf.fill(mix(#fff0, #016def, self.selected));
}
}
return sdf.result
}
}
draw_text: {text_style: <THEME_FONT_LABEL> {}}
}
Indicators = {{Indicators}} {
width: Fit,
height: Fit,
flow: Right,
// flow: Down,
spacing: 5,
// align: {x: 0.5, y: 0.5},
indicator_titles: []
template: <IndicatorCheckBox> {
width: 100, height: 20,
margin: 0, padding: 0
// flow: Right,
// flow: Down,
align: {x: 0.5, y: 0.5},
text: "Here",
draw_text: {
fn get_color(self) -> vec4 {
return #f60;
}
}
draw_check: {
check_type: Check,
}
}
}
Steps = {{Steps}} {
page_titles: []
choices_values: []
page_template: <StepsScreen> { }
// custom_button: <CustomButton>{
// width: Fill, height: Fit,
// text: "Next"
// draw_bg: {
// border_radius: 2.
// }
// }
choice_page_offset: 0.0
animator: {
choice = {
default: restart,
restart = {
from: {all: Snap}
apply: {choice_page_offset: 400.0}
}
show = {
redraw: true,
from: {all: Forward {duration: 0.5}}
apply: {choice_page_offset: 0.0}
}
}
}
}
Choose = {{Choose}}{
// debug: A
width: 400,
height: 480,
flow: Down,
padding: 20, spacing: 10
align: {x: 0.5, y: 0.5}
<Indicators>{
indicator_titles: [
"Step One",
"Step Two",
"Step Three"
]
}
<Steps>{
page_template: <StepsScreen> {}
page_titles: ["Step One", "Step Two", "Step Three" ]
choices_values: [
["STP1-Option 1"],
["STP2-Option 1", "STP2-Option 2", "STP2-Option 3"],
["STP3-Option 1", "STP3-Option 2"]
]
}
}
}
#[derive(Live, LiveHook, Widget)]
pub struct Choose {
#[deref]
view: View,
}
impl Widget for Choose {
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
self.view.draw_walk_all(cx, scope, walk);
// log!("Choose: draw_walk");
DrawStep::done()
}
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
self.view.handle_event(cx, event, scope);
}
}
#[derive(Live, Widget)]
pub struct Indicators {
#[redraw]
#[rust]
area: Area,
#[walk]
walk: Walk,
#[layout]
layout: Layout,
#[live]
indicator_titles: Vec<String>,
#[live]
template: Option<LivePtr>,
#[rust]
items: ComponentMap<LiveId, WidgetRef>,
}
impl LiveHook for Indicators {
fn after_apply(&mut self, cx: &mut Cx, _apply: &mut Apply, _index: usize, _nodes: &[LiveNode]) {
// let tags = ["test1", "test2", "test3"];
// log!("Indicators: after_apply");
self.items.clear();
for (i, title_text) in self.indicator_titles.iter().enumerate() {
// for (i, title_text) in tags.iter().enumerate() {
let item_id = LiveId::from_str(&format!("items{}", i));
let item_widget = WidgetRef::new_from_ptr(cx, self.template);
// item_widget.apply_over(cx, live! {label = { text: (title_text) }});
item_widget.apply_over(cx, live! {text: (title_text) });
self.items.insert(item_id, item_widget);
}
}
}
impl Widget for Indicators {
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
for (_id, item) in self.items.iter_mut() {
item.handle_event(cx, event, scope);
}
}
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
cx.begin_turtle(walk, self.layout);
//Indicators
// log!("Indicators: draw_walk");
for (_id, item) in self.items.iter_mut() {
// let _ = item.draw(cx, scope);
let _ = item.draw_all(cx, scope);
}
cx.end_turtle_with_area(&mut self.area);
DrawStep::done()
}
}
#[derive(Live, Widget)]
pub struct Steps {
#[redraw]
#[rust]
area: Area,
#[walk]
walk: Walk,
// #[live]
// draw_bg: DrawQuad,
// #[live]
// custom_button: CustomButton,
#[live]
page_titles: Vec<String>,
#[live]
choices_values: Vec<Vec<String>>,
#[rust]
items: ComponentMap<LiveId, WidgetRef>,
// #[live]
// choices_values: Vec<LiveValue>,
#[live]
page_template: Option<LivePtr>,
#[live]
options_template: Option<LivePtr>,
#[rust(0)]
current_page: u8,
#[rust]
pages: ComponentMap<LiveId, WidgetRef>,
#[live]
choice_page_offset: f64,
#[animator]
animator: Animator,
}
impl LiveHook for Steps {
fn after_new_from_doc(&mut self, cx: &mut Cx) {
log!("Steps::after_new_from_doc");
// fn after_apply(&mut self, cx: &mut Cx, _apply: &mut Apply, _index: usize, _nodes: &[LiveNode]) {
// log!("Steps: after_apply");
for (idx, title_text) in self.page_titles.iter().enumerate() {
let widget_id = LiveId::from_str(&format!("page{}", idx));
let page = self.pages.get_or_insert(cx, widget_id, |cx| {
WidgetRef::new_from_ptr(cx, self.page_template)
});
page.label(id!(step_title))
.set_text_and_redraw(cx, &format!("{}", &title_text.as_str()))
}
// for (idx, title_text_vec) in self.choices_values.iter().enumerate() {
// log!("Options: {:?}", &title_text_vec);
// // for (_idx, title_text) in title_text_vec.iter().enumerate() {
// // log!("Options: {:?}", &title_text);
// let widget_id = LiveId::from_str(&format!("page{}", idx));
// let page = self.pages.get_or_insert(cx, widget_id, |cx| {
// WidgetRef::new_from_ptr(cx, self.page_template)
// });
// for (_idx, title_text) in title_text_vec.iter().enumerate() {
// log!("Options: {:?}", &title_text);
// page.button(id!(but))
// .set_text_and_redraw(cx, &format!("{}", &title_text.as_str()))
// }
// }
}
}
impl Widget for Steps {
fn handle_event(&mut self, cx: &mut Cx, event: &Event, _scope: &mut Scope) {
// //Options
// for (_id, item) in self.items.iter_mut() {
// item.handle_event(cx, event, scope);
// }
// log!("Steps: handle_event");
if self.animator_handle_event(cx, event).must_redraw() {
self.redraw(cx);
}
// Fire the "show" animation when the "restart" animation is done
if self.animator.animator_in_state(cx, id!(choice.restart)) {
self.animator_play(cx, id!(choice.show));
}
// match event.hits(cx, self.custom_button.area()) {
match event.hits(cx, self.area) {
Hit::FingerUp(fe) => {
if fe.is_over {
// Do not fire a new animation if the choice is already animating
if !self.animator.is_track_animating(cx, id!(choice)) {
self.update_current_page();
self.animator_play(cx, id!(choice.restart));
//self.redraw(cx);
}
}
}
_ => (),
};
}
// fn redraw(&mut self, cx: &mut Cx) {
// self.area.redraw(cx);
// }
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
// let walk = self.walk(cx);
self.draw_walkd(cx, scope, walk);
DrawStep::done()
}
}
impl Steps {
fn draw_walkd(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) {
cx.begin_turtle(walk, Layout::default());
self.draw_page_with_offset(cx, scope, self.current_page, self.choice_page_offset, walk);
let prev_page_idx =
(self.current_page + self.page_titles.len() as u8 - 1) % self.page_titles.len() as u8;
self.draw_page_with_offset(
cx,
scope,
prev_page_idx,
self.choice_page_offset - CHOICE_MAX_OFFSET,
walk,
);
cx.end_turtle_with_area(&mut self.area);
// cx.end_turtle_with_area(&mut self.custom_button.area());
}
fn draw_page_with_offset(
&mut self,
cx: &mut Cx2d,
scope: &mut Scope,
index: u8,
offset: f64,
walk: Walk,
) {
let widget_id = LiveId::from_str(&format!("page{}", index));
let page = self.pages.get_or_insert(cx, widget_id, |cx| {
WidgetRef::new_from_ptr(cx, self.page_template)
});
let _ = page.draw_walk(cx, scope, walk.with_margin_left(offset));
}
fn update_current_page(&mut self) {
self.current_page = (self.current_page + 1) % self.page_titles.len() as u8;
log!("Steps::update_current_page: {:?}", &self.current_page);
// self.current_page = (self.current_page + 1) % self.choices_values.len() as u8;
}
}
#[derive(Live, Widget)]
pub struct SelectOptions {
#[redraw]
#[rust]
area: Area,
#[walk]
walk: Walk,
#[layout]
layout: Layout,
#[live]
page_titles: Vec<String>,
#[live]
select_choices_values: Vec<Vec<String>>,
#[live]
selects_template: Option<LivePtr>,
#[live]
options_template: Option<LivePtr>,
#[rust(0)]
current_page: u8,
#[rust]
pages: ComponentMap<LiveId, WidgetRef>,
#[live]
choice_page_offset: f64,
#[animator]
animator: Animator,
}
impl LiveHook for SelectOptions {
fn after_new_from_doc(&mut self, cx: &mut Cx) {
log!("SelectOptions::after_new_from_doc");
// fn after_apply(&mut self, cx: &mut Cx, _apply: &mut Apply, _index: usize, _nodes: &[LiveNode]) {
// log!("SelectOptions: after_apply");
self.select_choices_values = vec![
vec![
"STP1-Option 1".to_string(),
"STP1-Option 2".to_string(),
"STP1-Option 3".to_string(),
],
vec![
"STP2-Option 1".to_string(),
"STP2-Option 2".to_string(),
"STP2-Option 3".to_string(),
"STP2-Option 4".to_string(),
],
vec![
"STP3-Option 1".to_string(),
"STP3-Option 2".to_string(),
"STP3-Option 3".to_string(),
"STP3-Option 4".to_string(),
"STP3-Option 5".to_string(),
],
];
for (idx, title_text_vec) in self.select_choices_values.iter().enumerate() {
// log!("Options: {:?}", &title_text_vec);
let widget_id = LiveId::from_str(&format!("page{}", idx));
let page = self.pages.get_or_insert(cx, widget_id, |cx| {
WidgetRef::new_from_ptr(cx, self.selects_template)
});
for (_idx, title_text) in title_text_vec.iter().enumerate() {
// log!("Options: {:?}", &title_text);
page.button(id!(but))
.set_text_and_redraw(cx, &format!("{}", &title_text.as_str()))
}
}
}
}
impl Widget for SelectOptions {
fn handle_event(&mut self, cx: &mut Cx, event: &Event, _scope: &mut Scope) {
if self.animator_handle_event(cx, event).must_redraw() {
self.redraw(cx);
}
// Fire the "show" animation when the "restart" animation is done
if self.animator.animator_in_state(cx, id!(selectopt.restart)) {
self.animator_play(cx, id!(selectopt.show));
}
// match event.hits(cx, self.custom_button.area()) {
match event.hits(cx, self.area) {
Hit::FingerUp(fe) => {
if fe.is_over {
// Do not fire a new animation if the selectopt is already animating
if !self.animator.is_track_animating(cx, id!(selectopt)) {
self.update_current_page();
self.animator_play(cx, id!(selectopt.restart));
// self.redraw(cx);
}
}
}
_ => (),
};
}
// fn redraw(&mut self, cx: &mut Cx) {
// self.area.redraw(cx);
// }
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
// let walk = self.walk(cx);
self.draw_walkd(cx, scope, walk);
DrawStep::done()
}
}
impl SelectOptions {
fn draw_walkd(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) {
// cx.begin_turtle(walk, Layout::default());
cx.begin_turtle(walk, self.layout);
let mut arr: Vec<Vec<String>> = Vec::new();
for inner_vec in &self.select_choices_values {
// Append the inner vector to the new variable
arr.push(inner_vec.clone());
}
// let arr = self.add_to();
for (_idx, choices_arr) in arr.iter().enumerate() {
for (inneridx, _innerj) in choices_arr.iter().enumerate() {
// let prev_page_idx = (idx as u8 + choices_arr.len() as u8 - inneridx as u8)
// % choices_arr.len() as u8;
// log!(
// "prev_page_idx/index: {:?}===offset: {:?}",
// inneridx,
// self.choice_page_offset - SELECT_MAX_OFFSET
// );
// if prev_page_idx == 1 {
self.draw_page_with_offset(
cx,
scope,
inneridx as u8,
self.choice_page_offset - SELECT_MAX_OFFSET,
walk,
);
// }
}
}
// self.draw_page_with_offset(cx, scope, self.current_page, self.choice_page_offset, walk);
// let prev_page_idx = (self.current_page + self.select_choices_values.len() as u8 - 0)
// % self.select_choices_values.len() as u8;
// self.draw_page_with_offset(
// cx,
// scope,
// prev_page_idx,
// self.choice_page_offset - SELECT_MAX_OFFSET,
// walk,
// );
cx.end_turtle_with_area(&mut self.area);
}
fn draw_page_with_offset(
&mut self,
cx: &mut Cx2d,
scope: &mut Scope,
index: u8,
offset: f64,
walk: Walk,
) {
let widget_id = LiveId::from_str(&format!("page{}", index));
let page = self.pages.get_or_insert(cx, widget_id, |cx| {
WidgetRef::new_from_ptr(cx, self.selects_template)
});
// let _ = page.draw_all(cx, scope);
let _ = page.draw_walk(cx, scope, walk.with_margin_left(offset));
}
fn update_current_page(&mut self) {
self.current_page = (self.current_page + 1) % self.select_choices_values.len() as u8;
log!(
"SelectOptions::update_current_page: {:?}",
&self.current_page
);
}
}

View file

@ -0,0 +1,938 @@
// use makepad_widgets::*;
use crate::makepad_widgets::*;
const CHOICE_MAX_OFFSET: f64 = 700.0;
const SELECT_MAX_OFFSET: f64 = 500.0;
// use crate::shared::custom_button::CustomButton;
live_design! {
import makepad_draw::shader::std::*;
import makepad_widgets::base::*;
import makepad_widgets::theme_desktop_dark::*;
import crate::shared::styles::*;
import crate::shared::steps::StepNav;
import crate::shared::custom_button::CustomButton;
Indicator = <RoundedView> {
width: 100.0
height: 8.0
draw_bg: {
color: #f60,
radius: 2.5
}
}
ButChoices1 = <View>{
flow: Down,
show_bg: false
draw_bg: {
color: #000
}
align: {x: 0.5, y: 0.5}
but = <CustomButton> {
width: 300, height: 45,
text: "Option 1"
draw_bg: {
border_radius: 2.
}
}
}
ButChoices2 = <View>{
flow: Down,
show_bg: false
draw_bg: {
color: #000
}
align: {x: 0.5, y: 0.5}
but = <RadioButton> {
width: 300, height: 45,
text: "Option 1"
// draw_bg: {
// border_radius: 2.
// }
}
}
SIDEBAR_FONT_COLOR = #344054
SIDEBAR_FONT_COLOR_HOVER = #344054
SIDEBAR_FONT_COLOR_SELECTED = #127487
SIDEBAR_BG_COLOR = #f
SIDEBAR_BG_COLOR_HOVER = #E2F1F199
SIDEBAR_BG_COLOR_SELECTED = #E2F1F199
ButChoices = <ButtonGroup>{
// debug: A
flow: Down,
show_bg: false
draw_bg: {
color: #000
}
align: {x: 0.5, y: 0.5}
but = <View> {
// debug: A
width: 300, height: 45,
butchoice = <RadioButtonTextual> {
width: Fill,
height: 70,
align: {x: 0.5, y: 0.5}
text: "Option 1"
label_walk: { width: Fit, height: 34.0 }
icon_walk: {width: 80, height: 80}
draw_radio: {
radio_type: Tab,
instance border_width: 0.0
instance border_color: #344054
instance inset: vec4(0.0, 0.0, 0.0, 0.0)
instance radius: 2.5
fn get_color(self) -> vec4 {
return mix(
mix(
(SIDEBAR_BG_COLOR),
(SIDEBAR_BG_COLOR_HOVER),
self.hover
),
(SIDEBAR_BG_COLOR_SELECTED),
self.selected
)
}
fn get_border_color(self) -> vec4 {
return self.border_color
}
fn pixel(self) -> vec4 {
let border_color = #0157c0;
let border_width = 0.5;
let sdf = Sdf2d::viewport(self.pos * self.rect_size)
sdf.box(
self.inset.x + self.border_width,
self.inset.y + self.border_width,
self.rect_size.x - (self.inset.x + self.inset.z + self.border_width * 2.0),
self.rect_size.y - (self.inset.y + self.inset.w + self.border_width * 2.0),
max(1.0, self.radius)
)
sdf.fill_keep(self.get_color())
if self.border_width > 0.0 {
sdf.stroke(self.get_border_color(), self.border_width)
}
sdf.stroke(
border_color,
border_width
)
return sdf.result;
}
}
draw_text: {
color_unselected: (SIDEBAR_FONT_COLOR)
color_unselected_hover: (SIDEBAR_FONT_COLOR_HOVER)
color_selected: (SIDEBAR_FONT_COLOR_SELECTED)
fn get_color(self) -> vec4 {
return mix(
mix(
self.color_unselected,
self.color_unselected_hover,
self.hover
),
self.color_selected,
self.selected
)
}
}
}
}
}
// <ButtonGroup> {
// height: Fit
// flow: Right
// align: { x: 0.0, y: 0.5 }
// radiotabs_demo = <View> {
// width: Fit, height: Fit,
// radio1 = <RadioButtonTab> { label: "Option 1" }
// radio2 = <RadioButtonTab> { label: "Option 2" }
// radio3 = <RadioButtonTab> { label: "Option 3" }
// radio4 = <RadioButtonTab> { label: "Option 4" }
// }
// }
SelectOptions = {{SelectOptions}} {
// page_titles: []
width: Fit, height: Fit,
// indicator_titles: []
flow: Down,
spacing: 10,
align: {x: 0.5, y: 0.5},
select_choices_values: []
selects_template: <ButChoices> {
flow: Down,
}
choice_page_offset: 0.0
animator: {
selectopt = {
default: restart,
restart = {
ease: ExpDecay {d1: 0.80, d2: 0.97}
from: {all: Snap}
apply: {choice_page_offset: 500.0}
}
show = {
redraw: true,
ease: ExpDecay {d1: 0.80, d2: 0.97}
// from: {all: Forward {duration: 0}}
from: {all: Snap}
apply: {choice_page_offset: 0.0}
}
}
}
}
OptionC = {{Indicators}} {
width: Fit,
height: Fit,
flow: Down,
spacing: 5,
align: {x: 0.5, y: 0.5},
indicator_titles: []
template: <CustomButton> {
width: 100, height: Fit,
// margin: 0, padding: 0
text: "Option 1"
draw_bg: {
border_radius: 2.
}
}
}
StepsScreen = <View> {
// debug: A
// width: 300,
height: Fit,
flow: Down,
// padding: 10.0
// align: {y: 0.5}
align: {y: 0.5}
// padding: 20,
spacing: (THEME_SPACE_2),
title = <View> {
// debug: A
// align: {x: 0.1}
// padding: {left: 10.0},
height: Fit
spacing: 0,
step_title = <Label> {
// margin: {top: 10.0, left: 10.0}
draw_text: {
// text_style: <H2_TEXT_BOLD> {},
text_style: <REGULAR_TEXT>{font_size: 12.},
color: (COLOR_DOWN_6)
}
text: "Step One"
}
}
// <ButChoices> {}
avail_options = <View> {
// debug: A
align: {x: 0.5, y: 0.5}
height: Fit
<SelectOptions> {
// select_choices_values: [
// ["STP1-Option 1","STP1-Option 2","STP1-Option 3"],
// ["STP2-Option 1", "STP2-Option 2", "STP2-Option 3", "STP2-Option 4"],
// ["STP3-Option 1", "STP3-Option 2", "STP3-Option 3", "STP3-Option 4", "STP3-Option 5"]
// ]
select_choices_values: []
}
}
}
IndicatorCheckBox = <CheckBox> {
width: Fill,
height: 35,
margin: {left: 1},
label_walk: {margin: {top: 15, bottom: 10}}
draw_check: {
uniform size: 3.5;
instance open: 0.0
uniform length: 3.0
uniform width: 1.0
fn pixel(self) -> vec4 {
let sdf = Sdf2d::viewport(self.pos * self.rect_size)
match self.check_type {
CheckType::Check => {
let sz = self.size;
let left = sz + 1.;
let up = sz + 7;
let c = self.rect_size * vec2(0.5, 0.5);
sdf.box(
left,
c.y - up,
25. * sz + 5,
1.8 * sz,
1.8
);
sdf.fill(#232323);
sdf.stroke(#000, 0.5 + 0.5 * self.dpi_dilate);
let isz = sz * 0.5;
let ileft = isz + 3;
let iup = sz + 7;
sdf.box(
ileft,
c.y - iup,
25. * sz + 5,
1.8 * sz,
1.8
);
sdf.fill(mix(#fff0, #016def, self.selected));
}
}
return sdf.result
}
}
draw_text: {text_style: <THEME_FONT_LABEL> {}}
}
Indicators = {{Indicators}} {
width: Fit,
height: Fit,
flow: Right,
// flow: Down,
spacing: 5,
// align: {x: 0.5, y: 0.5},
indicator_titles: []
template: <IndicatorCheckBox> {
width: 100, height: 20,
margin: 0, padding: 0
// flow: Right,
// flow: Down,
align: {x: 0.5, y: 0.5},
text: "Here",
draw_text: {
fn get_color(self) -> vec4 {
return #f60;
}
}
draw_check: {
check_type: Check,
}
}
}
Steps = {{Steps}} {
// debug: A
page_titles: []
choices_values: []
page_template: <StepsScreen> { }
// custom_button: <CustomButton>{
// width: Fill, height: Fit,
// text: "Next"
// draw_bg: {
// border_radius: 2.
// }
// }
choice_page_offset: 0.0
animator: {
choice = {
default: restart,
restart = {
from: {all: Snap}
apply: {choice_page_offset: 400.0}
}
show = {
redraw: true,
from: {all: Forward {duration: 0.5}}
apply: {choice_page_offset: 0.0}
}
}
}
}
Choose = {{Choose}}{
// debug: A
width: 400,
// height: 500,
height: 488,
// height: Fit,
padding: 20, spacing: 20
align: {x: 0.5, y: 0.5}
<View>{
// debug: A
flow: Down,
width: Fit,
// height: Fill,
// spacing: 20
align: {y: 0.5}
<View>{
// debug: A
height: Fit,
width: Fit,
margin: {bottom: 20.0}
<Indicators>{
indicator_titles: [
"Step One",
"Step Two",
"Step Three"
]
}
}
back = <View> {
// debug: A
width: Fit, height: Fit,
back_left_button = <Button> {
width: Fit, height: Fit,
icon_walk: {width: 10, height: Fit}
text: "Back"
draw_text: {
text_style: <REGULAR_TEXT>{font_size: 12.},
fn get_color(self) -> vec4 {
// return #016def
return mix(mix(#000, #000, self.hover), #000, self.pressed)
}
}
draw_bg: {
fn pixel(self) -> vec4 {
let sdf = Sdf2d::viewport(self.pos * self.rect_size);
return sdf.result
}
}
draw_icon: {
svg_file: dep("crate://self/resources/back.svg"),
// color: (THEME_COLOR_TEXT_DEFAULT);
color: #000;
brightness: 0.8;
}
}
}
<Steps>{
page_template: <StepsScreen> {}
page_titles: ["Step One", "Step Two", "Step Three" ]
choices_values: [
["STP1-Option 1"],
["STP2-Option 1", "STP2-Option 2", "STP2-Option 3"],
["STP3-Option 1", "STP3-Option 2"]
]
}
next_button = <View> {
// debug: A
height: Fit
align: {x: 0.5, y: 0.5}
next = <CustomButton> {
// debug: A
width: 300, height: 50,
// margin: 0, padding: 0
text: "Next"
draw_text: {
fn get_color(self) -> vec4 {
// return #016def // 0160C0
return mix(mix(#f0, #016def, self.hover), #f0, self.pressed)
}
}
draw_bg: {
border_radius: 2.
fn pixel(self) -> vec4 {
let border_color = #0157c0;
// let border_color = #016def; //#0157c0
let border_width = 0.5;
let sdf = Sdf2d::viewport(self.pos * self.rect_size);
let body = mix(mix(#0157c0, #f, self.hover), #0157c0, self.pressed);
sdf.box(
1.,
1.,
self.rect_size.x - 2.0,
self.rect_size.y - 2.0,
self.border_radius
)
sdf.fill_keep(body)
sdf.stroke(
border_color,
border_width
)
return sdf.result
}
}
}
}
}
}
}
#[derive(Live, LiveHook, Widget)]
pub struct Choose {
#[deref]
view: View,
#[animator]
animator: Animator,
#[rust]
screen_width: f64,
}
impl Widget for Choose {
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
self.view.draw_walk_all(cx, scope, walk);
// log!("Choose: draw_walk");
DrawStep::done()
}
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
self.view.handle_event(cx, event, scope);
}
}
impl WidgetMatchEvent for Choose {
fn handle_actions(&mut self, cx: &mut Cx, actions: &Actions, _scope: &mut Scope) {
let back_left_button_clicked = self.button(id!(back_left_button)).clicked(&actions);
if back_left_button_clicked {
self.next_page_view(cx);
}
let next_button_clicked = self.button(id!(next)).clicked(&actions);
if next_button_clicked {
self.next_page_view(cx);
}
for action in actions {
if let WindowAction::WindowGeomChange(ce) = action.as_widget_action().cast() {
self.screen_width = ce.new_geom.inner_size.x * ce.new_geom.dpi_factor;
log!("SCREEN_WIDTH ===> {}", self.screen_width);
}
}
}
}
impl Choose {
fn back_left_view(&mut self, cx: &mut Cx) {
// // Fire the "show" animation when the "restart" animation is done
// if self.animator.animator_in_state(cx, id!(choice.restart)) {
// self.animator_play(cx, id!(choice.show));
// }
self.animator_play(cx, id!(choice.show));
// cx.widget_action(
// self.widget_uid(),
// &HeapLiveIdPath::default(),
// StackNavigationTransitionAction::HideBegin,
// );
}
fn next_page_view(&mut self, cx: &mut Cx) {
// // Fire the "show" animation when the "restart" animation is done
// if self.animator.animator_in_state(cx, id!(choice.restart)) {
// self.animator_play(cx, id!(choice.show));
// }
self.animator_play(cx, id!(choice.show));
// cx.widget_action(
// self.widget_uid(),
// &HeapLiveIdPath::default(),
// StackNavigationTransitionAction::HideBegin,
// );
}
}
#[derive(Live, Widget)]
pub struct Indicators {
#[redraw]
#[rust]
area: Area,
#[walk]
walk: Walk,
#[layout]
layout: Layout,
#[live]
indicator_titles: Vec<String>,
#[live]
template: Option<LivePtr>,
#[rust]
items: ComponentMap<LiveId, WidgetRef>,
}
impl LiveHook for Indicators {
fn after_apply(&mut self, cx: &mut Cx, _apply: &mut Apply, _index: usize, _nodes: &[LiveNode]) {
// let tags = ["test1", "test2", "test3"];
// log!("Indicators: after_apply");
self.items.clear();
for (i, title_text) in self.indicator_titles.iter().enumerate() {
// for (i, title_text) in tags.iter().enumerate() {
let item_id = LiveId::from_str(&format!("items{}", i));
let item_widget = WidgetRef::new_from_ptr(cx, self.template);
// item_widget.apply_over(cx, live! {label = { text: (title_text) }});
item_widget.apply_over(cx, live! {text: (title_text) });
self.items.insert(item_id, item_widget);
}
}
}
impl Widget for Indicators {
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
for (_id, item) in self.items.iter_mut() {
item.handle_event(cx, event, scope);
}
}
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
cx.begin_turtle(walk, self.layout);
//Indicators
let mut arr: Vec<WidgetRef> = Vec::new();
for (_idx, inner_vec) in self.items.iter_mut() {
// Append the inner vector to the new variable
arr.push(inner_vec.clone());
}
for (inneridx, indicator_arr) in arr.iter().enumerate() {
let index: u8 = inneridx as u8;
let widget_id = LiveId::from_str(&format!("items{}", index));
let item = self.items.get_or_insert(cx, widget_id, |cx| {
WidgetRef::new_from_ptr(cx, self.template)
});
let _ = item.draw_all(cx, scope);
}
// for (_id, item) in self.items.iter_mut() {
// // let _ = item.draw(cx, scope);
// let _ = item.draw_all(cx, scope);
// }
cx.end_turtle_with_area(&mut self.area);
DrawStep::done()
}
}
#[derive(Live, Widget)]
pub struct Steps {
#[redraw]
#[rust]
area: Area,
#[walk]
walk: Walk,
#[live]
page_titles: Vec<String>,
#[live]
choices_values: Vec<Vec<String>>,
#[rust]
items: ComponentMap<LiveId, WidgetRef>,
#[live]
page_template: Option<LivePtr>,
#[live]
options_template: Option<LivePtr>,
#[rust(0)]
current_page: u8,
#[rust]
pages: ComponentMap<LiveId, WidgetRef>,
#[live]
choice_page_offset: f64,
#[animator]
animator: Animator,
}
impl LiveHook for Steps {
fn after_new_from_doc(&mut self, cx: &mut Cx) {
log!("Steps::after_new_from_doc");
for (idx, title_text) in self.page_titles.iter().enumerate() {
let widget_id = LiveId::from_str(&format!("page{}", idx));
let page = self.pages.get_or_insert(cx, widget_id, |cx| {
WidgetRef::new_from_ptr(cx, self.page_template)
});
page.label(id!(step_title))
.set_text_and_redraw(cx, &format!("{}", &title_text.as_str()))
}
}
}
impl Widget for Steps {
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
for (_id, page) in self.pages.iter_mut() {
page.handle_event(cx, event, scope);
}
if self.animator_handle_event(cx, event).must_redraw() {
self.redraw(cx);
}
// Fire the "show" animation when the "restart" animation is done
if self.animator.animator_in_state(cx, id!(choice.restart)) {
self.animator_play(cx, id!(choice.show));
}
// // match event.hits(cx, self.custom_button.area()) {
// match event.hits(cx, self.area) {
// Hit::FingerUp(fe) => {
// if fe.is_over {
// // Do not fire a new animation if the choice is already animating
// if !self.animator.is_track_animating(cx, id!(choice)) {
// self.update_current_page();
// self.animator_play(cx, id!(choice.restart));
// //self.redraw(cx);
// }
// }
// }
// _ => (),
// };
}
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
self.draw_walkd(cx, scope, walk);
DrawStep::done()
}
}
impl WidgetMatchEvent for Steps {
fn handle_actions(&mut self, cx: &mut Cx, actions: &Actions, _scope: &mut Scope) {
for action in actions {
if let WindowAction::WindowGeomChange(ce) = action.as_widget_action().cast() {
let screen_width = ce.new_geom.inner_size.x * ce.new_geom.dpi_factor;
log!("SCREEN_WIDTH ===> {}", screen_width);
}
}
}
}
impl Steps {
fn draw_walkd(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) {
cx.begin_turtle(walk, Layout::default());
self.draw_page_with_offset(cx, scope, self.current_page, self.choice_page_offset, walk);
let prev_page_idx =
(self.current_page + self.page_titles.len() as u8 - 1) % self.page_titles.len() as u8;
self.draw_page_with_offset(
cx,
scope,
prev_page_idx,
self.choice_page_offset - CHOICE_MAX_OFFSET,
walk,
);
cx.end_turtle_with_area(&mut self.area);
// cx.end_turtle_with_area(&mut self.custom_button.area());
}
fn draw_page_with_offset(
&mut self,
cx: &mut Cx2d,
scope: &mut Scope,
index: u8,
offset: f64,
walk: Walk,
) {
let widget_id = LiveId::from_str(&format!("page{}", index));
let page = self.pages.get_or_insert(cx, widget_id, |cx| {
WidgetRef::new_from_ptr(cx, self.page_template)
});
let _ = page.draw_walk(cx, scope, walk.with_margin_left(offset));
}
fn update_current_page(&mut self) {
self.current_page = (self.current_page + 1) % self.page_titles.len() as u8;
log!("Steps::update_current_page: {:?}", &self.current_page);
// self.current_page = (self.current_page + 1) % self.choices_values.len() as u8;
}
}
#[derive(Live, Widget)]
pub struct SelectOptions {
#[redraw]
#[rust]
area: Area,
#[walk]
walk: Walk,
#[layout]
layout: Layout,
#[live]
page_titles: Vec<String>,
#[live]
select_choices_values: Vec<Vec<String>>,
#[live]
selects_template: Option<LivePtr>,
#[live]
options_template: Option<LivePtr>,
#[rust(0)]
current_page: u8,
#[rust]
pages: ComponentMap<LiveId, WidgetRef>,
#[live]
choice_page_offset: f64,
#[animator]
animator: Animator,
}
impl LiveHook for SelectOptions {
fn after_new_from_doc(&mut self, cx: &mut Cx) {
log!("SelectOptions::after_new_from_doc");
// fn after_apply(&mut self, cx: &mut Cx, _apply: &mut Apply, _index: usize, _nodes: &[LiveNode]) {
// log!("SelectOptions: after_apply");
self.select_choices_values = vec![
vec![
"STP1-Option 1".to_string(),
"STP1-Option 2".to_string(),
"STP1-Option 3".to_string(),
],
vec![
"STP2-Option 1".to_string(),
"STP2-Option 2".to_string(),
"STP2-Option 3".to_string(),
"STP2-Option 4".to_string(),
],
vec![
"STP3-Option 1".to_string(),
"STP3-Option 2".to_string(),
"STP3-Option 3".to_string(),
"STP3-Option 4".to_string(),
"STP3-Option 5".to_string(),
],
];
// for (idx, title_text_vec) in self.select_choices_values[2].iter().enumerate() {
// let widget_id = LiveId::from_str(&format!("page{}", idx));
// let page = WidgetRef::new_from_ptr(cx, self.selects_template);
// // page.apply_over(cx, live! { label = {text:(title_text_vec)}});
// page.apply_over(cx, live! {label: (title_text_vec) });
// self.pages.insert(widget_id, page);
// }
for (idx, title_text_vec) in self.select_choices_values[0].iter().enumerate() {
let widget_id = LiveId::from_str(&format!("page{}", idx));
let page = self.pages.get_or_insert(cx, widget_id, |cx| {
WidgetRef::new_from_ptr(cx, self.selects_template)
});
// page.button(id!(but))
page.radio_button(id!(butchoice))
// page.label(id!(but))
.set_text_and_redraw(cx, &format!("{}", &title_text_vec.as_str()))
}
}
}
impl Widget for SelectOptions {
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
for (_id, page) in self.pages.iter_mut() {
page.handle_event(cx, event, scope);
}
// if self.animator_handle_event(cx, event).must_redraw() {
// self.redraw(cx);
// }
// // Fire the "show" animation when the "restart" animation is done
// if self.animator.animator_in_state(cx, id!(selectopt.restart)) {
// self.animator_play(cx, id!(selectopt.show));
// }
// // match event.hits(cx, self.custom_button.area()) {
// match event.hits(cx, self.area) {
// Hit::FingerUp(fe) => {
// if fe.is_over {
// // Do not fire a new animation if the selectopt is already animating
// if !self.animator.is_track_animating(cx, id!(selectopt)) {
// self.update_current_page();
// self.animator_play(cx, id!(selectopt.restart));
// // self.redraw(cx);
// }
// }
// }
// _ => (),
// };
}
// fn redraw(&mut self, cx: &mut Cx) {
// self.area.redraw(cx);
// }
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
// let walk = self.walk(cx);
self.draw_walkd(cx, scope, walk);
// println!("=>{:?}", self.select_choices_values[0].clone());
DrawStep::done()
}
}
impl WidgetMatchEvent for SelectOptions {
fn handle_actions(&mut self, cx: &mut Cx, actions: &Actions, _scope: &mut Scope) {
for action in actions {
if let WindowAction::WindowGeomChange(ce) = action.as_widget_action().cast() {
let screen_width = ce.new_geom.inner_size.x * ce.new_geom.dpi_factor;
log!("SCREEN_WIDTH ===> {}", screen_width);
}
}
}
}
impl SelectOptions {
fn draw_walkd(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) {
// cx.begin_turtle(walk, Layout::default());
cx.begin_turtle(walk, self.layout);
let mut arr: Vec<Vec<String>> = Vec::new();
for inner_vec in &self.select_choices_values {
// Append the inner vector to the new variable
arr.push(inner_vec.clone());
}
for (inneridx, choices_arr) in arr[0].iter().enumerate() {
// println!("=>{:?}", choices_arr);
// for (inneridx, _innerj) in choices_arr.iter().enumerate() {
// self.draw_page_with_offset(
// cx,
// scope,
// inneridx as u8,
// self.choice_page_offset - SELECT_MAX_OFFSET,
// walk,
// );
let offset: f64 = self.choice_page_offset - SELECT_MAX_OFFSET;
let index: u8 = inneridx as u8;
// println!("=>{:?}", index);
let widget_id = LiveId::from_str(&format!("page{}", index));
let page = self.pages.get_or_insert(cx, widget_id, |cx| {
WidgetRef::new_from_ptr(cx, self.selects_template)
});
// let _ = page.draw_walk_all(cx, scope, walk.with_margin_left(offset));
//.draw_all(cx, scope);
let _ = page.draw_walk(cx, scope, walk.with_margin_left(offset));
// }
}
cx.end_turtle_with_area(&mut self.area);
}
fn draw_page_with_offset(
&mut self,
cx: &mut Cx2d,
scope: &mut Scope,
index: u8,
offset: f64,
walk: Walk,
) {
let widget_id = LiveId::from_str(&format!("page{}", index));
let page = self.pages.get_or_insert(cx, widget_id, |cx| {
WidgetRef::new_from_ptr(cx, self.selects_template)
});
// let _ = page.draw_all(cx, scope);
let _ = page.draw_walk(cx, scope, walk.with_margin_left(offset));
}
fn update_current_page(&mut self) {
self.current_page = (self.current_page + 1) % self.select_choices_values[0].len() as u8;
log!(
"SelectOptions::update_current_page: {:?}",
&self.current_page
);
}
}

View file

@ -0,0 +1,297 @@
use makepad_widgets::*;
const CHOICE_MAX_OFFSET: f64 = 400.0;
live_design! {
import makepad_draw::shader::std::*;
import makepad_widgets::base::*;
import makepad_widgets::theme_desktop_dark::*;
import crate::shared::styles::*;
import crate::shared::custom_button::CustomButton;
IndicatorCheckBox = <CheckBox> {
width: Fill,
height: 35,
margin: {left: 1},
label_walk: {margin: {top: 15}}
draw_check: {
uniform size: 3.5;
instance open: 0.0
uniform length: 3.0
uniform width: 1.0
fn pixel(self) -> vec4 {
let sdf = Sdf2d::viewport(self.pos * self.rect_size)
match self.check_type {
CheckType::Check => {
let sz = self.size;
let left = sz + 1.;
let up = sz + 7;
let c = self.rect_size * vec2(0.5, 0.5);
sdf.box(
left,
c.y - up,
25. * sz + 5,
1.8 * sz,
1.8
);
sdf.fill(#232323);
sdf.stroke(#000, 0.5 + 0.5 * self.dpi_dilate);
let isz = sz * 0.5;
let ileft = isz + 3;
let iup = sz + 7;
sdf.box(
ileft,
c.y - iup,
25. * sz + 5,
1.8 * sz,
1.8
);
sdf.fill(mix(#fff0, #016def, self.selected));
}
}
return sdf.result
}
}
draw_text: {text_style: <THEME_FONT_LABEL> {}}
}
// Indicators = {{Indicators}}{
// Fit,
// flow: Right,
// spacing: 5,
// align: {x: 0.5, y: 0.5},
// indicator_titles: []
// template: <IndicatorCheckBox> {
// width: 100, height: 20,
// margin: 0, padding: 0
// flow: Right,
// align: {x: 0.5, y: 0.5},
// text: "Here",
// draw_text: {
// fn get_color(self) -> vec4 {
// return #f60;
// }
// }
// draw_check: {
// check_type: Check,
// }
// }
// }
Choose = {{Choose}}{
height: Fill, width: Fill
// flow: Down
<Indicators>{
// indicator_titles: [
// "Step One",
// "Step Two",
// "Step Three"
// ]
}
<Steps>{}
}
}
#[derive(Live, LiveHook, Widget)]
pub struct Choose {
#[deref]
view: View,
}
impl Widget for Choose {
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
self.view.draw_walk_all(cx, scope, walk);
DrawStep::done()
}
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
self.view.handle_event(cx, event, scope);
}
}
#[derive(Live, Widget)]
pub struct Indicators {
#[walk]
walk: Walk,
#[redraw]
#[live]
draw_bg: DrawQuad,
#[layout]
layout: Layout,
#[live]
indicator_titles: Vec<String>,
#[live]
template: Option<LivePtr>,
#[rust]
items: ComponentMap<LiveId, WidgetRef>,
}
#[derive(Live, Widget)]
pub struct Steps {
#[walk]
walk: Walk,
#[redraw]
#[live]
draw_bg: DrawQuad,
// #[redraw] #[rust] area:Area,
// #[walk] walk:Walk,
#[live]
page_titles: Vec<String>,
#[live]
choices_values: Vec<Vec<String>>,
#[rust]
items: ComponentMap<LiveId, WidgetRef>,
#[live]
page_template: Option<LivePtr>,
#[live]
options_template: Option<LivePtr>,
#[rust(0)]
current_page: u8,
#[rust]
pages: ComponentMap<LiveId, WidgetRef>,
#[live]
choice_page_offset: f64,
#[animator]
animator: Animator,
}
impl LiveHook for Indicators {
fn after_apply(&mut self, cx: &mut Cx, _apply: &mut Apply, _index: usize, _nodes: &[LiveNode]) {
// let tags = ["test1", "test2", "test3"];
self.items.clear();
for (i, title_text) in self.indicator_titles.iter().enumerate() {
// for (i, title_text) in tags.iter().enumerate() {
let item_id = LiveId::from_str(&format!("items{}", i));
let item_widget = WidgetRef::new_from_ptr(cx, self.template);
// item_widget.apply_over(cx, live! {label = { text: (title_text) }});
item_widget.apply_over(cx, live! {text: (title_text) });
self.items.insert(item_id, item_widget);
}
}
}
impl Widget for Indicators {
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
for (_id, item) in self.items.iter_mut() {
item.handle_event(cx, event, scope);
}
}
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
cx.begin_turtle(walk, self.layout);
//Indicators
for (_id, item) in self.items.iter_mut() {
let _ = item.draw_all(cx, scope);
}
cx.end_turtle_with_area(&mut self.draw_bg.draw_vars.area);
DrawStep::done()
}
}
impl LiveHook for Steps {
// fn after_new_from_doc(&mut self, cx: &mut Cx) {
fn after_apply(&mut self, cx: &mut Cx, _apply: &mut Apply, _index: usize, _nodes: &[LiveNode]) {
for (idx, title_text) in self.page_titles.iter().enumerate() {
let widget_id = LiveId::from_str(&format!("page{}", idx));
let page = self.pages.get_or_insert(cx, widget_id, |cx| {
WidgetRef::new_from_ptr(cx, self.page_template)
});
page.label(id!(step_title))
.set_text_and_redraw(cx, &format!("{}", &title_text.as_str()))
}
for (idx, title_text_vec) in self.choices_values.iter().enumerate() {
log!("Options: {:?}", &title_text_vec);
for (_idx, title_text) in title_text_vec.iter().enumerate() {
log!("Options: {:?}", &title_text);
let widget_id = LiveId::from_str(&format!("page{}", idx));
let page = self.pages.get_or_insert(cx, widget_id, |cx| {
WidgetRef::new_from_ptr(cx, self.page_template)
});
page.button(id!(but))
.set_text_and_redraw(cx, &format!("{}", &title_text.as_str()))
}
}
}
}
impl Widget for Steps {
fn handle_event(&mut self, cx: &mut Cx, event: &Event, _scope: &mut Scope) {
// //Options
// for (_id, item) in self.items.iter_mut() {
// item.handle_event(cx, event, scope);
// }
if self.animator_handle_event(cx, event).must_redraw() {
self.redraw(cx);
}
// Fire the "show" animation when the "restart" animation is done
if self.animator.animator_in_state(cx, id!(choice.restart)) {
self.animator_play(cx, id!(choice.show));
}
match event.hits(cx, self.draw_bg.draw_vars.area) {
Hit::FingerUp(fe) => {
if fe.is_over {
// Do not fire a new animation if the choice is already animating
if !self.animator.is_track_animating(cx, id!(choice)) {
self.update_current_page();
self.animator_play(cx, id!(choice.restart));
//self.redraw(cx);
}
}
}
_ => (),
};
}
// fn redraw(&mut self, cx: &mut Cx) {
// self.area.redraw(cx);
// }
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
// let walk = self.walk(cx);
self.draw_walkd(cx, scope, walk);
DrawStep::done()
}
}
impl Steps {
fn draw_walkd(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) {
cx.begin_turtle(walk, Layout::default());
self.draw_page_with_offset(cx, scope, self.current_page, self.choice_page_offset, walk);
let prev_page_idx =
(self.current_page + self.page_titles.len() as u8 - 1) % self.page_titles.len() as u8;
self.draw_page_with_offset(
cx,
scope,
prev_page_idx,
self.choice_page_offset - CHOICE_MAX_OFFSET,
walk,
);
cx.end_turtle_with_area(&mut self.draw_bg.draw_vars.area);
}
fn draw_page_with_offset(
&mut self,
cx: &mut Cx2d,
scope: &mut Scope,
index: u8,
offset: f64,
walk: Walk,
) {
let widget_id = LiveId::from_str(&format!("page{}", index));
let page = self.pages.get_or_insert(cx, widget_id, |cx| {
WidgetRef::new_from_ptr(cx, self.page_template)
});
let _ = page.draw_walk(cx, scope, walk.with_margin_left(offset));
}
fn update_current_page(&mut self) {
self.current_page = (self.current_page + 1) % self.page_titles.len() as u8;
// self.current_page = (self.current_page + 1) % self.choices_values.len() as u8;
}
}

View file

@ -1,8 +1,5 @@
use makepad_widgets::widget::WidgetCache; use makepad_widgets::widget::WidgetCache;
use makepad_widgets::*; use makepad_widgets::*;
use std::collections::HashMap;
// use crate::shared::steps::StepsNavSetWidgetExt;
live_design! { live_design! {
import makepad_draw::shader::std::*; import makepad_draw::shader::std::*;
@ -11,13 +8,12 @@ live_design! {
import crate::shared::styles::*; import crate::shared::styles::*;
import crate::shared::custom_button::CustomButton; import crate::shared::custom_button::CustomButton;
import crate::shared::round_slider::RoundSlider;
// import crate::default_choice::choose::Choose;Choose
import crate::default_choice::choice::Choose;
DefaultChoiceScreen = <View> {
width: Fill, height: Fill flow: Down StepOneScreen = <View> {
<View> {
flow: Down,
align: {x: 0.5, y: 0.3}
<View> {
flow: Down, flow: Down,
spacing: (SSPACING_2), spacing: (SSPACING_2),
align: {x: 0.5, y: 0.5} align: {x: 0.5, y: 0.5}
@ -46,14 +42,173 @@ live_design! {
} }
} }
} }
StepTwoScreen = <View> {
flow: Down, spacing: (SSPACING_2),
align: {x: 0.5, y: 0.5}
label = <Label> {
margin: {top: 1}
draw_text: {
text_style: <H2_TEXT_BOLD> {},
color: (COLOR_DOWN_6)
}
text: "Step Two"
}
getstarted = <CustomButton> {
width: 250
height: Fit
text: "Submit"
draw_bg: {
border_radius: 2.
}
}
}
StepThreeScreen = <View> {
flow: Down, spacing: (SSPACING_2),
align: {x: 0.5, y: 0.5}
label = <Label> {
margin: {top: 1}
draw_text: {
text_style: <H2_TEXT_BOLD> {},
color: (COLOR_DOWN_6)
}
text: "Step Three"
}
getstarted = <CustomButton> {
width: 250
height: Fit
text: "Submit"
draw_bg: {
border_radius: 2.
}
}
}
FishHeader = <RoundedView> {
flow: Right
height: Fit,
width: Fill,
margin: {bottom: (SSPACING_2)}
align: {x: 1.0, y: 0.5},
// flow: Right,
// spacing: 5.,
padding: {right: 5.}
// title = <FishTitle> {
// height: Fit,
// width: Fill,
// margin: <SPACING_0> {}
// padding: <SPACING_2> {}
// }
menu = <DropDown> {//DropDown NavBar
}
}
Indicator = <RoundedView> {
width: 100.0
height: 8.0
draw_bg: {
color: #f60,
radius: 2.5
}
}
DefaultChoiceScreen = {{DefaultChoiceScreen}} {
width: Fit,
height: Fit,
flow: Right,
spacing: 5,
template: <CheckBox> {}
// template: <Indicator> {}
}
DefaultChoice = <View> {
width: Fill, height: Fill flow: Down
<View> {
flow: Down,
align: {x: 0.5, y: 0.3}
// <ModelFilesTags>{}
// // <DefaultChoiceScreen>{}
<View> {
flow: Down,
spacing: (SSPACING_2),
align: {x: 0.5, y: 0.5}
// <DefaultChoiceScreen>{}
Choose = <Choose> {}
// <View> {
// debug: M
// width: 300, height: 150
// align: {x: 0.5, y: 0.5}
// <RoundSlider> {}
// }
// label = <Label> {
// margin: {top: 1}
// draw_text: {
// text_style: <H2_TEXT_BOLD> {},
// color: (COLOR_DOWN_6)
// }
// text: "Step One"
// }
// getstarted = <CustomButton> {
// width: 250
// height: Fit
// text: "Submit"
// draw_bg: {
// border_radius: 2.
// }
// }
// getstartedw = <CustomButton> {
// width: 250
// height: Fit
// text: "Submit"
// draw_bg: {
// border_radius: 2.
// }
// }
}
} }
} }
} }
#[derive(Live, LiveHook, Widget)] #[derive(Live, Widget)]
pub struct DefaultChoiceScreen { pub struct DefaultChoiceScreen {
#[deref] #[deref]
view: View, view: View,
#[redraw]
#[rust]
area: Area,
#[walk]
walk: Walk,
#[layout]
layout: Layout,
#[live]
template: Option<LivePtr>,
#[rust]
items: ComponentMap<LiveId, WidgetRef>,
}
impl LiveHook for DefaultChoiceScreen {
fn after_apply(&mut self, cx: &mut Cx, _apply: &mut Apply, _index: usize, _nodes: &[LiveNode]) {
let tags = ["test1", "test2", "test3"];
self.items.clear();
for (i, tag) in tags.iter().enumerate() {
let item_id = LiveId(i as u64).into();
let item_widget = WidgetRef::new_from_ptr(cx, self.template);
item_widget.apply_over(cx, live! {label = { text: (tag) }});
self.items.insert(item_id, item_widget);
}
}
} }
impl Widget for DefaultChoiceScreen { impl Widget for DefaultChoiceScreen {
@ -64,9 +219,17 @@ impl Widget for DefaultChoiceScreen {
if self.view.button(id!(getstartedw)).clicked(&actions) { if self.view.button(id!(getstartedw)).clicked(&actions) {
log!("Option Selected: {}", 0); log!("Option Selected: {}", 0);
} }
for (_id, item) in self.items.iter_mut() {
item.handle_event(cx, event, scope);
}
} }
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep { fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
self.view.draw_walk(cx, scope, walk) cx.begin_turtle(walk, self.layout);
for (_id, item) in self.items.iter_mut() {
let _ = item.draw_all(cx, scope);
}
cx.end_turtle_with_area(&mut self.area);
DrawStep::done()
} }
} }

View file

@ -1 +1,3 @@
pub mod choice;
// pub mod choose;
pub mod default_choice_screen; pub mod default_choice_screen;

View file

@ -1,5 +1,9 @@
#[cfg(not(target_arch = "wasm32"))]
use crate::utils::get_current_year; use crate::utils::get_current_year;
// #[cfg(target_arch = "wasm32")]
// use crate::utils::get_current_year;
use makepad_widgets::*; use makepad_widgets::*;
live_design! { live_design! {
@ -146,9 +150,13 @@ impl Widget for Home {
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep { fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
let label = self.view.label(id!(footer)); let label = self.view.label(id!(footer));
// let check_box = self.view.check_box(id!(footer));
// NiManyatta © 2024 // NiManyatta © 2024
#[cfg(not(target_arch = "wasm32"))]
let year = get_current_year(); let year = get_current_year();
#[cfg(not(target_arch = "wasm32"))]
label.set_text_and_redraw(cx, &format!("NiManyatta © {}", year)); label.set_text_and_redraw(cx, &format!("NiManyatta © {}", year));
// check_box.set_text_and_redraw(cx, &format!("NiManyatta © {}", year));
self.view.draw_walk(cx, scope, walk) self.view.draw_walk(cx, scope, walk)
} }
} }

View file

@ -0,0 +1,43 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, user-scalable=no"
/>
<title>Simple</title>
<script type="module">
import { WasmWebGL } from "../../makepad/platform/src/os/web/web_gl.js";
const wasm = await WasmWebGL.fetch_and_instantiate_wasm(
"/nimanyatta_v001/target/wasm32-unknown-unknown/release/nimanyatta_v001.wasm",
);
class MyWasmApp {
constructor(wasm) {
let canvas =
document.getElementsByClassName("full_canvas")[0];
this.webgl = new WasmWebGL(wasm, this, canvas);
}
}
let app = new MyWasmApp(wasm);
</script>
<script
type="module"
src="../../makepad/platform/src/os/web/auto_reload.js"
></script>
<link
rel="stylesheet"
type="text/css"
href="../../makepad/platform/src/os/web/full_canvas.css"
/>
</head>
<body>
<canvas class="full_canvas"></canvas>
<div class="canvas_loader">
<div style="">Loading..</div>
</div>
</body>
</html>

View file

@ -7,4 +7,6 @@ pub mod home;
pub mod shared; pub mod shared;
pub mod three; pub mod three;
pub mod two; pub mod two;
// #[cfg(not(target_arch = "wasm32"))]
pub mod utils; pub mod utils;

View file

@ -0,0 +1,6 @@
{
"Help Me Choose": {},
"Help Me Build": {
"Help Me Build": {}
}
}

View file

@ -0,0 +1,483 @@
use makepad_widgets::*;
live_design! {
import makepad_draw::shader::std::*;
import makepad_widgets::base::*;
DrawStepsNav = {{DrawStepsNav}} {}
StepsNavBase = {{StepsNav}} {}
StepNav = <StepsNavBase> {
width: Fit,
height: Fit
label_walk: {
margin: {left: 20.0, top: 8, bottom: 8, right: 10}
width: Fit,
height: Fit,
}
label_align: {
y: 0.0
}
draw_check: {
uniform size: 7.0;
fn pixel(self) -> vec4 {
let sdf = Sdf2d::viewport(self.pos * self.rect_size)
match self.check_type {
CheckType::Check => {
let left = 3;
let sz = self.size;
let c = vec2(left + sz, self.rect_size.y * 0.5);
sdf.box(left, c.y - sz, sz * 2.0, sz * 2.0, 3.0); // rounding = 3rd value
sdf.fill_keep(mix(mix(#x00000077, #x00000044, pow(self.pos.y, 1.)), mix(#x000000AA, #x00000066, pow(self.pos.y, 1.0)), self.hover))
sdf.stroke(#x888, 1.0) // outline
let szs = sz * 0.5;
let dx = 1.0;
sdf.move_to(left + 4.0, c.y);
sdf.line_to(c.x, c.y + szs);
sdf.line_to(c.x + szs, c.y - szs);
sdf.stroke(mix(#fff0, #f, self.selected), 1.25);
}
CheckType::Radio => {
let sz = self.size;
let left = sz + 1.;
let c = vec2(left + sz, self.rect_size.y * 0.5);
sdf.circle(left, c.y, sz);
sdf.fill(#2);
let isz = sz * 0.5;
sdf.circle(left, c.y, isz);
sdf.fill(mix(#fff0, #f, self.selected));
}
CheckType::Toggle => {
let sz = self.size;
let left = sz + 1.;
let c = vec2(left + sz, self.rect_size.y * 0.5);
sdf.box(left, c.y - sz, sz * 3.0, sz * 2.0, 0.5 * sz);
sdf.fill(#2);
let isz = sz * 0.5;
sdf.circle(left + sz + self.selected * sz, c.y, isz);
sdf.circle(left + sz + self.selected * sz, c.y, 0.5 * isz);
sdf.subtract();
sdf.circle(left + sz + self.selected * sz, c.y, isz);
sdf.blend(self.selected)
sdf.fill(#f);
}
CheckType::None => {
return #0000
}
CheckType::Bar => {
let sz = self.size;
let left = sz + 1.;
let up = sz + 3.5;
let c = self.rect_size * vec2(0.5, 0.5);
sdf.box(
left,
c.y - up,
12. * sz + 5,
1. * sz,
1.9
);
sdf.fill(#232323);
sdf.stroke(#000, 0.5 + 0.5 * self.dpi_dilate);
let isz = sz * 0.5;
let ileft = isz + 4.5;
let iup = sz + 3.5;
sdf.box(
ileft,
c.y - iup,
12. * sz + 5,
1. * sz,
1.9
);
sdf.fill(mix(#fff0, #016def, self.selected));
}
}
return sdf.result
}
}
draw_text: {
color: #9,
instance focus: 0.0
instance selected: 0.0
instance hover: 0.0
text_style: {
font: {
//path: d"resources/IBMPlexSans-SemiBold.ttf"
}
font_size: 11.0
}
fn get_color(self) -> vec4 {
return mix(
mix(
#fff6,
#fff6,
self.hover
),
#fff6,
self.selected
)
}
}
draw_icon: {
instance focus: 0.0
instance hover: 0.0
instance selected: 0.0
fn get_color(self) -> vec4 {
return mix(
mix(
#9,
#c,
self.hover
),
#f,
self.selected
)
}
}
animator: {
hover = {
default: off
off = {
from: {all: Forward {duration: 0.15}}
apply: {
draw_check: {hover: 0.0}
draw_text: {hover: 0.0}
draw_icon: {hover: 0.0}
}
}
on = {
from: {all: Snap}
apply: {
draw_check: {hover: 1.0}
draw_text: {hover: 1.0}
draw_icon: {hover: 1.0}
}
}
}
focus = {
default: off
off = {
from: {all: Snap}
apply: {
draw_check: {focus: 0.0}
draw_text: {focus: 0.0}
draw_icon: {focus: 0.0}
}
}
on = {
from: {all: Snap}
apply: {
draw_check: {focus: 1.0}
draw_text: {focus: 1.0}
draw_icon: {focus: 1.0}
}
}
}
selected = {
default: off
off = {
from: {all: Forward {duration: 0.1}}
apply: {
draw_check: {selected: 0.0},
draw_text: {selected: 0.0},
draw_icon: {selected: 0.0},
}
}
on = {
from: {all: Forward {duration: 0.0}}
apply: {
draw_check: {selected: 1.0}
draw_text: {selected: 1.0}
draw_icon: {selected: 1.0},
}
}
}
}
}
Cho = <StepNav>{
width: 100, height: 20,
margin: 0, padding: 0
flow: Right,
align: {x: 0.5, y: 0.5},
draw_check: {
check_type: Bar,
}
}
// CheckContainer = <View> {
// width: Fit,
// height: Fit,
// checkbox = <CheckBox> {
// width: 400
// height: 266
// text: "Check me out!"
// }
// }
ModelFilesTags = {{ModelFilesTags}} {
width: Fit,
height: Fit,
flow: Right,
spacing: 5,
template: <Cho> {}
}
}
// #[derive(Live, Widget)]
// pub struct Choices {
// #[redraw]
// #[live]
// steps: ScrollBars,
// }
#[derive(Live, LiveHook, Widget)]
pub struct ModelFilesTags {
#[redraw]
#[rust]
area: Area,
#[walk]
walk: Walk,
#[layout]
layout: Layout,
#[live]
template: Option<LivePtr>,
#[rust]
items: ComponentMap<LiveId, WidgetRef>,
}
// impl LiveHook for ModelFilesTags {
// fn after_apply(&mut self, _cx: &mut Cx, _apply: &mut Apply, _index: usize, _nodes: &[LiveNode]) {
// self.set_tags
// }
// }
impl Widget for ModelFilesTags {
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
for (_id, item) in self.items.iter_mut() {
item.handle_event(cx, event, scope);
}
}
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
cx.begin_turtle(walk, self.layout);
for (_id, item) in self.items.iter_mut() {
let _ = item.draw_walk(cx, scope, walk);
}
cx.end_turtle_with_area(&mut self.area);
DrawStep::done()
}
}
// impl LiveHook for ModelFilesTags {
// fn before_live_design(cx: &mut Cx) {
// register_widget!(cx, ModelFilesTags);
// }
// }
impl ModelFilesTagsRef {
pub fn set_tags(&self, cx: &mut Cx, tags: Vec<String>) {
let Some(mut tags_widget) = self.borrow_mut() else {
return;
};
tags_widget.items.clear();
for (i, tag) in tags.iter().enumerate() {
let item_id = LiveId(i as u64).into();
let item_widget = WidgetRef::new_from_ptr(cx, tags_widget.template);
item_widget.apply_over(cx, live! {label = { text: (tag) }});
tags_widget.items.insert(item_id, item_widget);
}
}
}
#[derive(Live, LiveHook, LiveRegister)]
#[repr(C)]
pub struct DrawStepsNav {
#[deref]
draw_super: DrawQuad,
#[live]
check_type: CheckType,
#[live]
hover: f32,
#[live]
focus: f32,
#[live]
selected: f32,
}
#[derive(Live, LiveHook, Widget)]
pub struct StepsNav {
#[walk]
walk: Walk,
#[layout]
layout: Layout,
#[animator]
animator: Animator,
#[live]
icon_walk: Walk,
#[live]
label_walk: Walk, //
#[live]
#[redraw]
draw_name: DrawText, //
#[live]
label_align: Align, //
#[redraw]
#[live]
draw_check: DrawStepsNav, //
#[live]
draw_text: DrawText,
#[live]
draw_icon: DrawIcon,
#[live]
text: RcStringMut, //
#[live]
bind: String, //
}
#[derive(Live, LiveHook, LiveRegister)]
#[live_ignore]
#[repr(u32)]
pub enum CheckType {
#[pick]
Check = shader_enum(1),
Radio = shader_enum(2),
Toggle = shader_enum(3),
None = shader_enum(4),
Bar = shader_enum(5), //RadioType::Bar
}
#[derive(Default, Clone, Debug)]
pub enum StepsNavAction {
Change(bool),
WasSweeped,
WasSelected,
#[default]
None,
}
impl StepsNav {
pub fn draw_walk(&mut self, cx: &mut Cx2d, walk: Walk) {
self.draw_check.begin(cx, walk, self.layout);
// self.draw_icon.svg_file = icon;
self.draw_text
.draw_walk(cx, self.label_walk, self.label_align, self.text.as_ref());
self.draw_icon.draw_walk(cx, self.icon_walk);
self.draw_check.end(cx);
}
pub fn handle_event_with(
&mut self,
cx: &mut Cx,
event: &Event,
sweep_area: Area,
dispatch_action: &mut dyn FnMut(&mut Cx, StepsNavAction),
) {
if self.animator_handle_event(cx, event).must_redraw() {
// self.draw_bg.area().redraw(cx);
self.draw_check.area().redraw(cx);
}
match event.hits_with_options(
cx,
// self.draw_bg.area(),
self.draw_check.area(),
HitOptions::new().with_sweep_area(sweep_area),
) {
Hit::FingerHoverIn(_) => {
self.animator_play(cx, id!(hover.on));
}
Hit::FingerHoverOut(_) => {
self.animator_play(cx, id!(hover.off));
}
Hit::FingerDown(_) => {
dispatch_action(cx, StepsNavAction::WasSweeped);
self.animator_play(cx, id!(hover.on));
self.animator_play(cx, id!(select.on));
}
Hit::FingerUp(se) => {
if !se.is_sweep {
dispatch_action(cx, StepsNavAction::WasSelected);
} else {
self.animator_play(cx, id!(hover.off));
self.animator_play(cx, id!(select.off));
}
}
_ => {}
}
}
}
impl Widget for StepsNav {
fn widget_to_data(
&self,
_cx: &mut Cx,
actions: &Actions,
nodes: &mut LiveNodeVec,
path: &[LiveId],
) -> bool {
match actions.find_widget_action_cast(self.widget_uid()) {
StepsNavAction::Change(v) => {
nodes.write_field_value(path, LiveValue::Bool(v));
true
}
_ => false,
}
}
fn data_to_widget(&mut self, cx: &mut Cx, nodes: &[LiveNode], path: &[LiveId]) {
if let Some(value) = nodes.read_field_value(path) {
if let Some(value) = value.as_bool() {
self.animator_toggle(cx, value, Animate::Yes, id!(selected.on), id!(selected.off));
}
}
}
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
let uid = self.widget_uid();
self.animator_handle_event(cx, event);
match event.hits(cx, self.draw_check.area()) {
Hit::FingerHoverIn(_) => {
cx.set_cursor(MouseCursor::Hand);
self.animator_play(cx, id!(hover.on));
}
Hit::FingerHoverOut(_) => {
self.animator_play(cx, id!(hover.off));
}
Hit::FingerDown(_fe) => {
if self.animator_in_state(cx, id!(selected.on)) {
self.animator_play(cx, id!(selected.off));
cx.widget_action(uid, &scope.path, StepsNavAction::Change(false));
} else {
self.animator_play(cx, id!(selected.on));
cx.widget_action(uid, &scope.path, StepsNavAction::Change(true));
}
}
Hit::FingerUp(_fe) => {}
Hit::FingerMove(_fe) => {}
_ => (),
}
}
fn draw_walk(&mut self, cx: &mut Cx2d, _scope: &mut Scope, walk: Walk) -> DrawStep {
self.draw_walk(cx, walk);
DrawStep::done()
}
fn text(&self) -> String {
self.text.as_ref().to_string()
}
fn set_text(&mut self, v: &str) {
self.text.as_mut_empty().push_str(v);
}
}

View file

@ -1,4 +1,4 @@
use makepad_widgets::*; use makepad_widgets::{makepad_derive_widget::*, makepad_draw::*, widget::*};
live_design! { live_design! {
import makepad_draw::shader::std::*; import makepad_draw::shader::std::*;
@ -20,8 +20,8 @@ live_design! {
draw_bg: { draw_bg: {
border_radius: 8. border_radius: 8.
fn pixel(self) -> vec4 { fn pixel(self) -> vec4 {
let border_color = #0157c0;
// let border_color = #016def; //#0157c0 // let border_color = #016def; //#0157c0
let border_color = #0157c0;
let border_width = 0.5; let border_width = 0.5;
let sdf = Sdf2d::viewport(self.pos * self.rect_size); let sdf = Sdf2d::viewport(self.pos * self.rect_size);
let body = mix(mix(#f, #0157c0, self.hover), #d1, self.pressed); let body = mix(mix(#f, #0157c0, self.hover), #d1, self.pressed);
@ -43,4 +43,225 @@ live_design! {
} }
} }
} }
// CustomButtonC = {{CustomButton}} {
// draw_cbutt: {
// <CustomButton>{}
// }
// }
} }
// #[derive(Live, LiveHook, LiveRegister)]
// #[repr(C)]
// pub struct DrawCButton {
// #[deref]
// draw_super: DrawQuad,
// #[live]
// button_type: CButtonType,
// #[live]
// hover: f32,
// #[live]
// focus: f32,
// #[live]
// selected: f32,
// }
// #[derive(Live, LiveHook)]
// #[live_ignore]
// #[repr(u32)]
// pub enum CButtonType {
// #[pick]
// Primary = shader_enum(1),
// Secondary = shader_enum(2),
// }
// #[derive(Live, LiveHook, Widget)]
// pub struct CustomButton {
// #[redraw]
// #[live]
// draw_cbtn: DrawCButton,
// }
// #[derive(Live, LiveHook, Widget)]
// pub struct CustomButton {
// #[walk]
// walk: Walk,
// #[layout]
// layout: Layout,
// #[animator]
// animator: Animator,
// #[live]
// icon_walk: Walk,
// #[live]
// label_walk: Walk,
// #[live]
// label_align: Align,
// // #[redraw]
// // #[live]
// // draw_check: DrawCheckBox,
// #[live]
// #[redraw]
// draw_cbutt: DrawCButton,
// #[live]
// draw_text: DrawText,
// #[live]
// draw_icon: DrawIcon,
// #[live]
// text: RcStringMut,
// #[live]
// bind: String,
// }
// #[derive(Live, LiveHook, LiveRegister)]
// #[repr(C)]
// pub struct DrawCButton {
// #[deref]
// draw_super: DrawQuad,
// #[live]
// check_type: CButtonType,
// #[live]
// hover: f32,
// #[live]
// focus: f32,
// #[live]
// selected: f32,
// }
// #[derive(Live, LiveHook, LiveRegister)]
// #[live_ignore]
// #[repr(u32)]
// pub enum CButtonType {
// #[pick]
// Clickable = shader_enum(1),
// UnClickable = shader_enum(2),
// }
// #[derive(Clone, Debug, DefaultNone)]
// pub enum CButtonBoxAction {
// Change(bool),
// None,
// }
// #[derive(Live, LiveHook, LiveRegister)]
// #[repr(C)]
// struct DrawLabelText {
// #[deref]
// draw_super: DrawText,
// #[live]
// hover: f32,
// #[live]
// pressed: f32,
// }
// impl CustomButton {
// pub fn draw_walk(&mut self, cx: &mut Cx2d, walk: Walk) {
// self.draw_cbutt.begin(cx, walk, self.layout);
// self.draw_text
// .draw_walk(cx, self.label_walk, self.label_align, self.text.as_ref());
// self.draw_icon.draw_walk(cx, self.icon_walk);
// self.draw_cbutt.end(cx);
// }
// }
// impl Widget for CustomButton {
// fn widget_to_data(
// &self,
// _cx: &mut Cx,
// actions: &Actions,
// nodes: &mut LiveNodeVec,
// path: &[LiveId],
// ) -> bool {
// match actions.find_widget_action_cast(self.widget_uid()) {
// CButtonBoxAction::Change(v) => {
// nodes.write_field_value(path, LiveValue::Bool(v));
// true
// }
// _ => false,
// }
// }
// fn data_to_widget(&mut self, cx: &mut Cx, nodes: &[LiveNode], path: &[LiveId]) {
// if let Some(value) = nodes.read_field_value(path) {
// if let Some(value) = value.as_bool() {
// self.animator_toggle(cx, value, Animate::Yes, id!(selected.on), id!(selected.off));
// }
// }
// }
// fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
// let uid = self.widget_uid();
// self.animator_handle_event(cx, event);
// match event.hits(cx, self.draw_cbutt.area()) {
// Hit::FingerHoverIn(_) => {
// cx.set_cursor(MouseCursor::Hand);
// self.animator_play(cx, id!(hover.on));
// }
// Hit::FingerHoverOut(_) => {
// self.animator_play(cx, id!(hover.off));
// }
// Hit::FingerDown(_fe) => {
// if self.animator_in_state(cx, id!(selected.on)) {
// self.animator_play(cx, id!(selected.off));
// cx.widget_action(uid, &scope.path, CButtonBoxAction::Change(false));
// } else {
// self.animator_play(cx, id!(selected.on));
// cx.widget_action(uid, &scope.path, CButtonBoxAction::Change(true));
// }
// }
// Hit::FingerUp(_fe) => {}
// Hit::FingerMove(_fe) => {}
// _ => (),
// }
// }
// fn draw_walk(&mut self, cx: &mut Cx2d, _scope: &mut Scope, walk: Walk) -> DrawStep {
// self.draw_walk(cx, walk);
// DrawStep::done()
// }
// fn text(&self) -> String {
// self.text.as_ref().to_string()
// }
// fn set_text(&mut self, v: &str) {
// self.text.as_mut_empty().push_str(v);
// }
// }
// impl CustomButtonRef {
// pub fn changed(&self, actions: &Actions) -> Option<bool> {
// if let CButtonBoxAction::Change(b) = actions.find_widget_action_cast(self.widget_uid()) {
// return Some(b);
// }
// None
// }
// pub fn set_text(&self, text: &str) {
// if let Some(mut inner) = self.borrow_mut() {
// let s = inner.text.as_mut_empty();
// s.push_str(text);
// }
// }
// pub fn selected(&self, cx: &Cx) -> bool {
// if let Some(inner) = self.borrow() {
// inner.animator_in_state(cx, id!(selected.on))
// } else {
// false
// }
// }
// pub fn set_selected(&self, cx: &mut Cx, value: bool) {
// if let Some(mut inner) = self.borrow_mut() {
// inner.animator_toggle(cx, value, Animate::Yes, id!(selected.on), id!(selected.off));
// }
// }
// }
// // impl CustomButton {
// // pub fn area(&self) -> Area {
// // self.area
// // }
// // }

View file

@ -0,0 +1,362 @@
use std::cell::RefCell;
use std::rc::Rc;
use makepad_widgets::makepad_derive_widget::*;
use makepad_widgets::makepad_draw::*;
use makepad_widgets::widget::*;
use crate::shared::popup_menu::*;
live_design! {
import makepad_draw::shader::std::*;
import makepad_widgets::theme_desktop_dark::*;
import crate::shared::popup_menu::*;
DropDown = {{WechatDropDown}} {
width: Fit,
height: Fit,
margin: {left: 1.0, right: 1.0, top: 1.0, bottom: 1.0},
align: {x: 0., y: 0.},
padding: {left: 5.0, top: 5.0, right: 4.0, bottom: 5.0}
draw_bg: {
fn pixel(self) -> vec4 {
let sdf = Sdf2d::viewport(self.pos * self.rect_size);
return sdf.result
}
}
draw_icon: {
instance hover: 0.0
instance pressed: 0.0
fn get_color(self) -> vec4 {
return mix(
mix(
#9,
#c,
self.hover
),
#9,
self.pressed
)
}
}
icon_walk: { width: 20., height: Fit}
popup_menu: <PopupMenu> {
}
popup_shift: vec2(-6.0,4.0)
selected_item: -1
animator: {
hover = {
default: off,
off = {
from: {all: Forward {duration: 0.1}}
apply: {
draw_icon: {pressed: 0.0, hover: 0.0}
}
}
on = {
from: {
all: Forward {duration: 0.1}
pressed: Forward {duration: 0.01}
}
apply: {
draw_icon: {pressed: 0.0, hover: [{time: 0.0, value: 1.0}],}
}
}
pressed = {
from: {all: Forward {duration: 0.2}}
apply: {
draw_icon: {pressed: [{time: 0.0, value: 1.0}], hover: 1.0,}
}
}
}
}
}
}
#[derive(Live, Widget)]
pub struct WechatDropDown {
#[animator]
animator: Animator,
#[walk]
walk: Walk,
#[layout]
layout: Layout,
#[live]
#[redraw]
draw_bg: DrawQuad,
#[live]
draw_icon: DrawIcon,
#[live]
icon_walk: Walk,
#[live]
bind: String,
#[live]
bind_enum: String,
#[live]
popup_menu: Option<LivePtr>,
#[live]
labels: Vec<String>,
#[live]
values: Vec<LiveValue>,
#[live]
icons: Vec<LiveDependency>,
#[live]
popup_shift: DVec2,
#[rust]
is_open: bool,
#[live]
selected_item: usize,
}
impl LiveHook for WechatDropDown {
fn after_apply_from(&mut self, cx: &mut Cx, apply: &mut Apply) {
if self.popup_menu.is_none() || !apply.from.is_from_doc() {
return;
}
let global = cx.global::<PopupMenuGlobal>().clone();
let mut map = global.map.borrow_mut();
// when live styling clean up old style references
map.retain(|k, _| cx.live_registry.borrow().generation_valid(*k));
let list_box = self.popup_menu.unwrap();
map.get_or_insert(cx, list_box, |cx| {
PopupMenu::new_from_ptr(cx, Some(list_box))
});
}
}
#[derive(Clone, DefaultNone, Debug)]
pub enum WechatDropDownAction {
Select(usize, LiveValue),
None,
}
#[derive(Default, Clone)]
struct PopupMenuGlobal {
map: Rc<RefCell<ComponentMap<LivePtr, PopupMenu>>>,
}
impl WechatDropDown {
pub fn toggle_open(&mut self, cx: &mut Cx) {
if self.is_open {
self.set_closed(cx);
} else {
self.set_open(cx);
}
}
pub fn set_open(&mut self, cx: &mut Cx) {
self.is_open = true;
self.draw_bg.redraw(cx);
let global = cx.global::<PopupMenuGlobal>().clone();
let mut map = global.map.borrow_mut();
let lb = map.get_mut(&self.popup_menu.unwrap()).unwrap();
let node_id = LiveId(self.selected_item as u64).into();
lb.init_select_item(node_id);
cx.sweep_lock(self.draw_bg.area());
}
pub fn set_closed(&mut self, cx: &mut Cx) {
self.is_open = false;
self.draw_bg.redraw(cx);
cx.sweep_unlock(self.draw_bg.area());
}
}
// It is named WechatDropDown because DropDown is already a widget in makepad_widgets
impl Widget for WechatDropDown {
fn widget_to_data(
&self,
_cx: &mut Cx,
actions: &Actions,
nodes: &mut LiveNodeVec,
path: &[LiveId],
) -> bool {
match actions.find_widget_action(self.widget_uid()).cast() {
WechatDropDownAction::Select(_, value) => {
nodes.write_field_value(path, value.clone());
true
}
_ => false,
}
}
fn data_to_widget(&mut self, cx: &mut Cx, nodes: &[LiveNode], path: &[LiveId]) {
if let Some(value) = nodes.read_field_value(path) {
if let Some(index) = self.values.iter().position(|v| v == value) {
if self.selected_item != index {
self.selected_item = index;
self.redraw(cx);
}
} else {
// error!("Value not in values list {:?}", value);
}
}
}
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
self.animator_handle_event(cx, event);
let uid = self.widget_uid();
if self.is_open && self.popup_menu.is_some() {
let global = cx.global::<PopupMenuGlobal>().clone();
let mut map = global.map.borrow_mut();
let menu = map.get_mut(&self.popup_menu.unwrap()).unwrap();
let mut close = false;
menu.handle_event_with(cx, event, self.draw_bg.area(), &mut |cx, action| {
if let PopupMenuAction::WasSelected(node_id) = action {
self.selected_item = node_id.0 .0 as usize;
cx.widget_action(
uid,
&scope.path,
WechatDropDownAction::Select(
self.selected_item,
self.values
.get(self.selected_item)
.cloned()
.unwrap_or(LiveValue::None),
),
);
self.draw_bg.redraw(cx);
close = true;
}
});
if close {
self.set_closed(cx);
}
// check if we touch outside of the popup menu
if let Event::MouseDown(e) = event {
if !menu.menu_contains_pos(cx, e.abs) {
self.set_closed(cx);
self.animator_play(cx, id!(hover.off));
}
}
}
// TODO: close on clicking outside of the popup menu
match event.hits_with_sweep_area(cx, self.draw_bg.area(), self.draw_bg.area()) {
Hit::KeyFocusLost(_) => {
self.set_closed(cx);
self.animator_play(cx, id!(hover.off));
self.draw_bg.redraw(cx);
}
Hit::KeyDown(ke) => match ke.key_code {
KeyCode::ArrowUp => {
if self.selected_item > 0 {
self.selected_item -= 1;
cx.widget_action(
uid,
&scope.path,
WechatDropDownAction::Select(
self.selected_item,
self.values
.get(self.selected_item)
.cloned()
.unwrap_or(LiveValue::None),
),
);
self.set_closed(cx);
self.draw_bg.redraw(cx);
}
}
KeyCode::ArrowDown => {
if !self.values.is_empty() && self.selected_item < self.values.len() - 1 {
self.selected_item += 1;
cx.widget_action(
uid,
&scope.path,
WechatDropDownAction::Select(
self.selected_item,
self.values
.get(self.selected_item)
.cloned()
.unwrap_or(LiveValue::None),
),
);
self.set_closed(cx);
self.draw_bg.redraw(cx);
}
}
_ => (),
},
Hit::FingerDown(_fe) => {
cx.set_key_focus(self.draw_bg.area());
self.toggle_open(cx);
self.animator_play(cx, id!(hover.pressed));
}
Hit::FingerHoverIn(_) => {
cx.set_cursor(MouseCursor::Hand);
self.animator_play(cx, id!(hover.on));
}
Hit::FingerHoverOut(_) => {
self.animator_play(cx, id!(hover.off));
}
Hit::FingerUp(fe) => {
if fe.is_over {
if fe.device.has_hovers() {
self.animator_play(cx, id!(hover.on));
}
} else {
self.animator_play(cx, id!(hover.off));
}
}
_ => (),
};
}
fn draw_walk(&mut self, cx: &mut Cx2d, _scope: &mut Scope, walk: Walk) -> DrawStep {
// cx.clear_sweep_lock(self.draw_bg.area());
self.draw_bg.begin(cx, walk, self.layout);
//let start_pos = cx.turtle().rect().pos;
self.draw_icon.draw_walk(cx, self.icon_walk);
self.draw_bg.end(cx);
cx.add_nav_stop(self.draw_bg.area(), NavRole::DropDown, Margin::default());
if self.is_open && self.popup_menu.is_some() {
// cx.set_sweep_lock(self.draw_bg.area());
let global = cx.global::<PopupMenuGlobal>().clone();
let mut map = global.map.borrow_mut();
let popup_menu = map.get_mut(&self.popup_menu.unwrap()).unwrap();
popup_menu.begin(cx);
for (i, item) in self.labels.iter().enumerate() {
let node_id = LiveId(i as u64).into();
popup_menu.draw_item(cx, node_id, item, self.icons[i].clone());
}
popup_menu.end(cx, self.draw_bg.area());
}
DrawStep::done()
}
}
impl WechatDropDownRef {
pub fn item_clicked(&mut self, item_id: &[LiveId], actions: &Actions) -> bool {
if let Some(item) = actions.find_widget_action(self.widget_uid()) {
if let WechatDropDownAction::Select(_id, value) = item.cast() {
return LiveValue::Bool(true) == value.enum_eq(item_id);
}
}
return false;
}
}

View file

@ -0,0 +1,190 @@
use crate::shared::dropdown_menu::*;
use crate::shared::stack_view_action::StackViewAction;
use makepad_widgets::widget::WidgetCache;
use makepad_widgets::*;
live_design! {
import makepad_draw::shader::std::*;
import makepad_widgets::base::*;
import makepad_widgets::theme_desktop_dark::*;
import crate::shared::styles::*;
import crate::shared::helpers::FillerX;
import crate::shared::dropdown_menu::DropDown;
SimpleHeaderContent = <View> {
width: Fill, height: Fit
flow: Right, align: {x: 0.5, y: 0.5}
<FillerX> {}
title_container = <View> {
width: Fill, height: Fit
align: {x: 0.5, y: 0.5}
title = <Label> {
width: Fit, height: Fit
draw_text: {
color: #000,
text_style: <TITLE_TEXT>{},
},
text: "微信"
}
}
}
SimpleHeader = <View> {
width: Fill , height: Fit, margin: 0
padding: {bottom: 7., top: 50.}, align: {x: 0.5, y: 0.0}, spacing: 0.0, flow: Overlay
show_bg: true
draw_bg: {
color: #EDEDED
}
content = <SimpleHeaderContent> {}
}
HeaderWithLeftActionButton = <SimpleHeader> {
content = {
flow: Overlay
button_container = <View> {
left_button = <Button> {
width: Fit, height: 68
icon_walk: {width: 20, height: 68}
draw_bg: {
fn pixel(self) -> vec4 {
let sdf = Sdf2d::viewport(self.pos * self.rect_size);
return sdf.result
}
}
draw_icon: {
color: #000;
brightness: 0.8;
}
}
divider = <View> {
width: Fill, height: Fit
right_button = <Button> {
width: Fit, height: 68
icon_walk: {width: 20, height: 68}
draw_bg: {
fn pixel(self) -> vec4 {
let sdf = Sdf2d::viewport(self.pos * self.rect_size);
return sdf.result
}
}
draw_icon: {
color: #000;
brightness: 0.8;
}
}
}
}
}
}
HeaderWithRightActionButton = <SimpleHeader> {
content = {
flow: Overlay
button_container = <View> {
spacer = <View> {
width: Fill, height: Fit
right_button = <Button> {
width: Fit, height: 68
icon_walk: {width: 20, height: 68}
draw_bg: {
fn pixel(self) -> vec4 {
let sdf = Sdf2d::viewport(self.pos * self.rect_size);
return sdf.result
}
}
draw_icon: {
color: #000;
brightness: 0.8;
}
}
}
}
}
}
HeaderDropDownMenu = {{HeaderDropDownMenu}} {
width: Fill, height: Fit, margin: 0
padding: {bottom: 7., top: 50.}, align: {x: 0.5, y: 0.0}, spacing: 0.0, flow: Overlay
show_bg: true
draw_bg: {
color: #EDEDED
}
content = <SimpleHeaderContent> {
width: Fill, height: Fit
flow: Right, align: {x: 0.5, y: 0.5}
button_container = <View> {
width: Fill, height: Fit
align: {x: 1.0, y: 0.5}, flow: Right, spacing: 5., padding: {right: 5.}
// TODO: this should be the searchbar, and we need consistent svgs
left_button = <Button> {
width: Fit, height: Fit
padding: 0.
icon_walk: {width: 20, height: Fit}
draw_bg: {
fn pixel(self) -> vec4 {
let sdf = Sdf2d::viewport(self.pos * self.rect_size);
return sdf.result
}
}
draw_icon: {
svg_file: dep("crate://self/resources/icons/search.svg")
color: #000;
brightness: 0.8;
}
}
menu = <DropDown> {
height: Fit, width: Fit
draw_icon: {
svg_file: dep("crate://self/resources/icons/menu.svg")
color: #000;
brightness: 0.8;
}
labels: ["StepOneScreen", "New Chat", "Scan", "Money"]
values: [AddContact, NewChat, Scan, Money]
icons: [
dep("crate://self/resources/icons/add_contact.svg"),
dep("crate://self/resources/icons/chat.svg"),
dep("crate://self/resources/icons/scan.svg"),
dep("crate://self/resources/icons/money.svg")
]
}
}
}
}
}
#[derive(Live, LiveHook, Widget)]
pub struct HeaderDropDownMenu {
#[deref]
view: View,
}
impl Widget for HeaderDropDownMenu {
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
let uid = self.widget_uid();
let actions = cx.capture_actions(|cx| self.view.handle_event(cx, event, scope));
if self
.wechat_drop_down(id!(menu))
.item_clicked(id!(AddContact), &actions)
{
cx.widget_action(uid, &scope.path, StackViewAction::ShowAddContact);
}
}
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
self.view.draw_walk(cx, scope, walk)
}
}

View file

@ -0,0 +1,27 @@
use makepad_widgets::*;
live_design! {
import makepad_widgets::base::*;
import makepad_widgets::theme_desktop_dark::*;
Divider = <View> {
width: Fill, height: Fit
flow: Down
<RoundedView> {
width: Fill,
height: 1.,
draw_bg: {color: (#ddd)}
}
}
LineH = <RoundedView> {
width: Fill,
height: 2,
margin: 0.0,
padding: 0.0, spacing: 0.0
draw_bg: {color: (COLOR_DIVIDER)}
}
FillerX = <View> { width: Fill, height: Fit }
FillerY = <View> { width: Fit, height: Fill }
}

View file

@ -1,2 +1,11 @@
pub mod cho;
pub mod custom_button; pub mod custom_button;
pub mod dropdown_menu;
pub mod header;
pub mod helpers;
pub mod popup_menu;
pub mod stack_navigation;
pub mod stack_view_action;
pub mod steps;
pub mod styles; pub mod styles;
pub mod round_slider;

View file

@ -0,0 +1,415 @@
use makepad_widgets::makepad_derive_widget::*;
use makepad_widgets::makepad_draw::*;
use makepad_widgets::widget::*;
live_design! {
import makepad_draw::shader::std::*;
import makepad_widgets::theme_desktop_dark::*;
import crate::shared::styles::*;
MenuItem = {{MenuItem}} {
align: {y: 0.5},
padding: {left: 5., top: 10., bottom: 10., right: 5.},
spacing: 5.,
width: Fill,
height: Fit
draw_bg: {
instance color: #4
instance color_selected: #5
instance border_radius: 4.0
instance selected: 0.0
instance hover: 0.0
fn pixel(self) -> vec4 {
let sdf = Sdf2d::viewport(self.pos * self.rect_size)
sdf.box(
1.0,
1.0,
self.rect_size.x,
self.rect_size.y,
self.border_radius
)
sdf.fill_keep(mix(self.color, self.color_selected, self.hover))
return sdf.result;
}
}
draw_name: {
text_style: <REGULAR_TEXT>{font_size: 9},
instance selected: 0.0
instance hover: 0.0
fn get_color(self) -> vec4 {
return mix(
mix(
THEME_COLOR_TEXT_DEFAULT,
THEME_COLOR_TEXT_SELECTED,
self.selected
),
THEME_COLOR_TEXT_HOVER,
self.hover
)
}
}
icon_walk: {width: 15., height: Fit, margin: {bottom: 3.}}
draw_icon: {
color: #f2f2f2;
brightness: 0.8;
}
animator: {
hover = {
default: off
off = {
from: {all: Snap}
apply: {
draw_bg: {hover: 0.0}
draw_name: {hover: 0.0}
}
}
on = {
cursor: Hand
from: {all: Snap}
apply: {
draw_bg: {hover: 1.0}
draw_name: {hover: 1.0}
}
}
}
select = {
default: off
off = {
from: {all: Snap}
apply: {
draw_bg: {selected: 0.0,}
draw_name: {selected: 0.0,}
}
}
on = {
from: {all: Snap}
apply: {
draw_bg: {selected: 1.0,}
draw_name: {selected: 1.0,}
}
}
}
}
indent_width: 10.0
}
PopupMenu = {{PopupMenu}} {
menu_item: <MenuItem> {}
flow: Down,
padding: 5.,
width: 140.,
height: Fit,
icon_walk: {width: 20., height: Fit}
draw_icon:{
instance hover: 0.0
instance pressed: 0.0
fn get_color(self) -> vec4 {
return mix(
mix(
#9,
#c,
self.hover
),
#9,
self.pressed
)
}
}
draw_bg: {
instance color: #4
instance border_width: 0.0,
instance border_color: #4,
instance border_radius: 4.0
fn get_color(self) -> vec4 {
return self.color
}
fn get_border_color(self) -> vec4 {
return self.border_color
}
fn pixel(self) -> vec4 {
let sdf = Sdf2d::viewport(self.pos * self.rect_size)
// sdf.blur = 18.0;
sdf.box(
self.border_width,
self.border_width,
self.rect_size.x - (self.border_width * 2.0),
self.rect_size.y - (self.border_width * 2.0),
self.border_radius
)
sdf.fill_keep(self.get_color())
return sdf.result;
}
}
}
}
#[derive(Live, LiveRegister, WidgetWrap)]
pub struct PopupMenu {
#[live]
draw_list: DrawList2d,
#[live]
menu_item: Option<LivePtr>,
#[live]
#[redraw]
draw_bg: DrawQuad,
#[live]
#[redraw]
draw_icon: DrawIcon,
#[live]
icon_walk: Walk,
#[layout]
layout: Layout,
#[walk]
walk: Walk,
#[live]
labels: Vec<String>,
#[live]
values: Vec<LiveValue>,
#[live]
items: Vec<String>,
#[rust]
menu_items: ComponentMap<MenuItemId, MenuItem>,
#[rust]
init_select_item: Option<MenuItemId>,
#[rust]
first_tap: bool,
#[rust]
count: usize,
}
impl LiveHook for PopupMenu {
fn after_apply(&mut self, cx: &mut Cx, apply: &mut Apply, index: usize, nodes: &[LiveNode]) {
if let Some(index) = nodes.child_by_name(index, live_id!(list_node).as_field()) {
for (_, node) in self.menu_items.iter_mut() {
node.apply(cx, apply, index, nodes);
}
}
self.draw_list.redraw(cx);
}
}
impl Widget for PopupMenu {
fn handle_event(&mut self, _cx: &mut Cx, _event: &Event, _scope: &mut Scope) {}
fn draw_walk(&mut self, cx: &mut Cx2d, _scope: &mut Scope, walk: Walk) -> DrawStep {
self.draw_bg.begin(cx, walk, self.layout);
self.draw_icon.draw_walk(cx, self.icon_walk);
self.draw_bg.end(cx);
DrawStep::done()
}
}
impl PopupMenu {
pub fn menu_contains_pos(&self, cx: &mut Cx, pos: DVec2) -> bool {
self.draw_bg.area().clipped_rect(cx).contains(pos)
}
pub fn begin(&mut self, cx: &mut Cx2d) {
self.draw_list.begin_overlay_reuse(cx);
cx.begin_pass_sized_turtle(Layout::flow_down());
self.draw_bg.begin(cx, self.walk, self.layout);
self.count = 0;
}
pub fn end(&mut self, cx: &mut Cx2d, shift_area: Area) {
self.draw_bg.end(cx);
let area = self.draw_bg.area().rect(cx);
let shift = DVec2 {
x: -area.size.x + (shift_area.rect(cx).size.x * 0.7),
y: 30.,
};
cx.end_pass_sized_turtle_with_shift(shift_area, shift);
self.draw_list.end(cx);
self.menu_items.retain_visible();
if let Some(init_select_item) = self.init_select_item.take() {
self.select_item_state(cx, init_select_item);
}
}
pub fn draw_item(
&mut self,
cx: &mut Cx2d,
item_id: MenuItemId,
label: &str,
icon: LiveDependency,
) {
self.count += 1;
let menu_item = self.menu_item;
let menu_item = self
.menu_items
.get_or_insert(cx, item_id, |cx| MenuItem::new_from_ptr(cx, menu_item));
menu_item.draw_item(cx, label, icon);
}
pub fn init_select_item(&mut self, which_id: MenuItemId) {
self.init_select_item = Some(which_id);
self.first_tap = true;
}
fn select_item_state(&mut self, cx: &mut Cx, which_id: MenuItemId) {
for (id, item) in &mut *self.menu_items {
if *id == which_id {
item.animator_cut(cx, id!(select.on));
item.animator_cut(cx, id!(hover.on));
} else {
item.animator_cut(cx, id!(select.off));
item.animator_cut(cx, id!(hover.off));
}
}
}
pub fn handle_event_with(
&mut self,
cx: &mut Cx,
event: &Event,
sweep_area: Area,
dispatch_action: &mut dyn FnMut(&mut Cx, PopupMenuAction),
) {
let mut actions = Vec::new();
for (item_id, node) in self.menu_items.iter_mut() {
node.handle_event_with(cx, event, sweep_area, &mut |_, e| {
actions.push((*item_id, e))
});
}
for (node_id, action) in actions {
match action {
MenuItemAction::WasSweeped => {
self.select_item_state(cx, node_id);
dispatch_action(cx, PopupMenuAction::WasSweeped(node_id));
}
MenuItemAction::WasSelected => {
self.select_item_state(cx, node_id);
dispatch_action(cx, PopupMenuAction::WasSelected(node_id));
}
_ => (),
}
}
}
}
#[derive(Live, LiveHook, LiveRegister, WidgetWrap)]
pub struct MenuItem {
#[layout]
layout: Layout,
#[walk]
walk: Walk,
#[live]
#[redraw]
draw_bg: DrawQuad,
#[live]
#[redraw]
draw_name: DrawText,
#[live]
#[redraw]
draw_icon: DrawIcon,
#[live]
icon_walk: Walk,
#[live]
indent_width: f32,
#[animator]
animator: Animator,
#[live]
opened: f32,
#[live]
hover: f32,
#[live]
selected: f32,
}
#[derive(Default, Clone, Debug)]
pub enum MenuItemAction {
WasSweeped,
WasSelected,
#[default]
None,
}
#[derive(Clone, DefaultNone, Debug)]
pub enum PopupMenuAction {
WasSweeped(MenuItemId),
WasSelected(MenuItemId),
None,
}
#[derive(Clone, Debug, Default, Eq, Hash, Copy, PartialEq, FromLiveId)]
pub struct MenuItemId(pub LiveId);
impl MenuItem {
pub fn draw_item(&mut self, cx: &mut Cx2d, label: &str, icon: LiveDependency) {
self.draw_bg.begin(cx, self.walk, self.layout);
self.draw_icon.svg_file = icon;
self.draw_icon.draw_walk(cx, self.icon_walk);
self.draw_name
.draw_walk(cx, Walk::fit(), Align { x: 0., y: 0.5 }, label);
self.draw_bg.end(cx);
}
pub fn handle_event_with(
&mut self,
cx: &mut Cx,
event: &Event,
sweep_area: Area,
dispatch_action: &mut dyn FnMut(&mut Cx, MenuItemAction),
) {
if self.animator_handle_event(cx, event).must_redraw() {
self.draw_bg.area().redraw(cx);
}
match event.hits_with_options(
cx,
self.draw_bg.area(),
HitOptions::new().with_sweep_area(sweep_area),
) {
Hit::FingerHoverIn(_) => {
self.animator_play(cx, id!(hover.on));
}
Hit::FingerHoverOut(_) => {
self.animator_play(cx, id!(hover.off));
}
Hit::FingerDown(_) => {
dispatch_action(cx, MenuItemAction::WasSweeped);
self.animator_play(cx, id!(hover.on));
self.animator_play(cx, id!(select.on));
}
Hit::FingerUp(se) => {
if !se.is_sweep {
dispatch_action(cx, MenuItemAction::WasSelected);
} else {
self.animator_play(cx, id!(hover.off));
self.animator_play(cx, id!(select.off));
}
}
_ => {}
}
}
}

View file

@ -0,0 +1,99 @@
use makepad_widgets::*;
// const CHOICE_MAX_OFFSET: f64 = 400.0;
live_design! {
import makepad_draw::shader::std::*;
import makepad_widgets::base::*;
import makepad_widgets::theme_desktop_dark::*;
import crate::shared::styles::*;
import crate::shared::steps::StepNav;
import crate::shared::custom_button::CustomButton;
RoundSlider = <Slider> {
// height: 36
height: 300
text: "CutOff1"
draw_text: {text_style: <H2_TEXT_BOLD> {}, color: (COLOR_UP_5)}
text_input: {
cursor_margin_bottom: (SSPACING_1),
cursor_margin_top: (SSPACING_1),
select_pad_edges: (SSPACING_1),
cursor_size: (SSPACING_1),
empty_message: "0",
numeric_only: true,
draw_bg: {
color: (COLOR_DOWN_0)
},
}
draw_slider: {
instance hover: float
instance focus: float
instance drag: float
fn pixel(self) -> vec4 {
let slider_height = 10;
let offset = 150;
let nub_size = mix(3, 4, self.hover);
let nubbg_size = 18
let sdf = Sdf2d::viewport(self.pos * self.rect_size)
let slider_bg_color = mix(#38, #30, self.focus);
let slider_color = mix(mix(#x086975, #x086975, self.hover), #x086975, self.focus);
let nub_color = mix(mix(#8, #f, self.hover), mix(#c, #f, self.drag), self.focus);
let nubbg_color = mix(#eee0, #8, self.drag);
sdf.rect(0, self.rect_size.y - slider_height-offset, self.rect_size.x, slider_height)
sdf.fill(slider_bg_color);
sdf.rect(0, self.rect_size.y - slider_height-offset, self.slide_pos * (self.rect_size.x - nub_size) + nub_size, slider_height)
sdf.fill(slider_color);
let nubbg_x = self.slide_pos * (self.rect_size.x - nub_size) - nubbg_size * 0.5 + 0.5 * nub_size;
sdf.rect(nubbg_x, self.rect_size.y - slider_height-offset, nubbg_size, slider_height)
sdf.fill(nubbg_color);
// the nub
let nub_x = self.slide_pos * (self.rect_size.x - nub_size);
sdf.circle(nub_x, self.rect_size.y - slider_height-(offset-5), slider_height);
// sdf.rect(nub_x, self.rect_size.y - slider_height-100, nub_size, slider_height)
sdf.fill_keep(mix(mix(#xF6FCFD, #xF6FCFD, self.hover), #xF6FCFD, self.pos.y)); // Nub background gradient
sdf.stroke(
mix(
mix(#x00bbd3, #x00bbd3, self.hover),
#x00bbd3,
pow(self.pos.y, 1.5)
),
1.
); // Nub outline gradient
// sdf.fill(nub_color);
// // let nub_x = self.slide_pos * (self.rect_size.x - nub_size - in_side * 2 - 9);
// let cnub_x = self.slide_pos * (self.rect_size.x - nub_size - cin_side * 2 - 9);
// let diameter = 7.0
// // sdf.move_to(mix(in_side + 3.5, self.rect_size.x * 0.5, self.bipolar), top + in_top);
// sdf.move_to(mix(cin_side + 3.5, self.rect_size.x * 0.5, self.bipolar), top + cin_top);
// sdf.line_to(nub_x + cin_side + nub_size * 0.5, top + cin_top);
// sdf.stroke_keep(mix((COLOR_UP_0), self.line_color, self.drag), 1.5)
// sdf.stroke(
// mix(mix(self.line_color * 0.85, self.line_color, self.hover), #xFFFFFF80, self.drag),
// 1
// )
// let nub_x = self.slide_pos * (self.rect_size.x - nub_size - in_side * 2 - 3) - 3;
// sdf.circle(nub_x + in_side, top + 7.0, diameter);
// sdf.fill_keep(mix(mix(#xF6FCFD, #xF6FCFD, self.hover), #xF6FCFD, self.pos.y)); // Nub background gradient
// sdf.stroke(
// mix(
// mix(#x00bbd3, #x00bbd3, self.hover),
// #x00bbd3,
// pow(self.pos.y, 1.5)
// ),
// 1.
// ); // Nub outline gradient
return sdf.result
}
}
}
}

View file

@ -0,0 +1,258 @@
use makepad_widgets::widget::WidgetCache;
use makepad_widgets::*;
use crate::shared::stack_view_action::StackViewAction;
use std::collections::HashMap;
live_design! {
import makepad_widgets::base::*;
import makepad_widgets::theme_desktop_dark::*;
import crate::shared::header::HeaderWithLeftActionButton;
FillerY = <View> {width: Fill}
FillerX = <View> {height: Fill}
Header = <HeaderWithLeftActionButton> {
content = {
title_container = {
title = {
text: "My Stack View"
}
}
button_container = {
left_button = {
width: Fit
icon_walk: {width: 10}
draw_icon: {
svg_file: dep("crate://self/resources/icons/arrow_back.svg")
}
}
}
}
}
StackNavigationView = {{StackNavigationView}} {
visible: false
width: Fill, height: Fill
flow: Down
show_bg: true
draw_bg: {
color: #fff
}
debug: A
header = <Header> {}
<FillerY> {}
// TBD Adjust this based on actual screen size
// offset: 400.0
offset: 2.0
animator: {
slide = {
default: hide,
hide = {
ease: ExpDecay {d1: 0.80, d2: 0.97}
from: {all: Forward {duration: 0.3}}
// Bug: Constants are not working as part of an live state value
// apply: {offset: 400.0}
apply: {offset: 1.0}
}
show = {
ease: ExpDecay {d1: 0.82, d2: 0.95}
from: {all: Forward {duration: 0.3}}
apply: {offset: 0.0}
}
}
}
}
StackNavigation = {{StackNavigation}} {
width: Fill, height: Fill
flow: Overlay
root_view = <View> {}
}
}
#[derive(Live, LiveHook, Widget)]
pub struct StackNavigationView {
#[deref]
view:View,
#[live]
offset: f64,
#[animator]
animator: Animator,
}
impl Widget for StackNavigationView {
fn handle_event(&mut self, cx:&mut Cx, event:&Event, scope:&mut Scope) {
if self.animator_handle_event(cx, event).is_animating() {
self.view.redraw(cx);
}
let actions = cx.capture_actions(|cx| self.view.handle_event(cx, event, scope));
if self.button(id!(left_button)).clicked(&actions) {
self.animator_play(cx, id!(slide.hide));
}
if self.animator.animator_in_state(cx, id!(slide.hide))
&& !self.animator.is_track_animating(cx, id!(slide))
{
self.apply_over(cx, live! {visible: false});
}
}
fn draw_walk(&mut self, cx:&mut Cx2d, scope:&mut Scope, walk:Walk) -> DrawStep{
self.view.draw_walk(
cx,
scope,
walk.with_abs_pos(DVec2 {
x: self.offset,
y: 0.,
}),
)
}
}
impl StackNavigationViewRef {
pub fn show(&mut self, cx: &mut Cx) {
if let Some(mut inner) = self.borrow_mut() {
inner.apply_over(cx, live! {visible: true});
inner.animator_play(cx, id!(slide.show));
}
}
pub fn is_showing(&self, cx: &mut Cx) -> bool {
if let Some(inner) = self.borrow() {
inner.animator.animator_in_state(cx, id!(slide.show))
|| inner.animator.is_track_animating(cx, id!(slide))
} else {
false
}
}
pub fn is_animating(&self, cx: &mut Cx) -> bool {
if let Some(inner) = self.borrow() {
inner.animator.is_track_animating(cx, id!(slide))
} else {
false
}
}
}
#[derive(Default)]
enum ActiveStackView {
#[default]
None,
Active(LiveId),
}
#[derive(Live, LiveRegisterWidget, WidgetRef)]
pub struct StackNavigation {
#[deref]
view: View,
#[rust]
active_stack_view: ActiveStackView,
}
impl LiveHook for StackNavigation {
fn after_new_from_doc(&mut self, _cx: &mut Cx) {
self.active_stack_view = ActiveStackView::None;
}
}
impl Widget for StackNavigation {
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
for widget_ref in self.get_active_views(cx).iter() {
widget_ref.handle_event(cx, event, scope);
}
}
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
for widget_ref in self.get_active_views(cx.cx).iter() {
widget_ref.draw_walk(cx, scope, walk) ?;
}
DrawStep::done()
}
}
impl WidgetNode for StackNavigation {
fn walk(&mut self, cx:&mut Cx) -> Walk{
self.view.walk(cx)
}
fn redraw(&mut self, cx: &mut Cx) {
for widget_ref in self.get_active_views(cx).iter() {
widget_ref.redraw(cx);
}
}
fn find_widgets(&mut self, path: &[LiveId], cached: WidgetCache, results: &mut WidgetSet) {
self.view.find_widgets(path, cached, results);
}
}
impl StackNavigation {
pub fn show_stack_view_by_id(&mut self, stack_view_id: LiveId, cx: &mut Cx) {
if let ActiveStackView::None = self.active_stack_view {
let mut stack_view_ref = self.stack_navigation_view(&[stack_view_id]);
stack_view_ref.show(cx);
self.active_stack_view = ActiveStackView::Active(stack_view_id);
self.redraw(cx);
}
}
fn get_active_views(&mut self, cx: &mut Cx) -> Vec<WidgetRef> {
match self.active_stack_view {
ActiveStackView::None => {
vec![self.view.widget(id!(root_view))]
},
ActiveStackView::Active(stack_view_id) => {
let stack_view_ref = self.stack_navigation_view(&[stack_view_id]);
let mut views = vec![];
if stack_view_ref.is_showing(cx) {
if stack_view_ref.is_animating(cx) {
views.push(self.view.widget(id!(root_view)));
}
views.push(stack_view_ref.0.clone());
views
} else {
self.active_stack_view = ActiveStackView::None;
vec![self.view.widget(id!(root_view))]
}
}
}
}
}
impl StackNavigationRef {
pub fn show_stack_view_by_id(&mut self, stack_view_id: LiveId, cx: &mut Cx) {
if let Some(mut inner) = self.borrow_mut() {
inner.show_stack_view_by_id(stack_view_id, cx);
}
}
pub fn handle_stack_view_actions(&mut self, cx: &mut Cx, actions: &Actions, destinations: &HashMap<StackViewAction, LiveId>) {
for action in actions {
let stack_view_action = action.as_widget_action().cast();
if let Some(stack_view_id) = destinations.get(&stack_view_action) {
self.show_stack_view_by_id(*stack_view_id, cx);
break;
}
}
}
pub fn set_title(&self, stack_view_id: LiveId, title: &str) {
if let Some(mut inner) = self.borrow_mut() {
let stack_view_ref = inner.stack_navigation_view(&[stack_view_id]);
stack_view_ref.label(id!(title)).set_text(title);
}
}
}

View file

@ -0,0 +1,12 @@
use makepad_widgets::*;
#[derive(Clone, DefaultNone, Eq, Hash, PartialEq, Debug)]
pub enum StackViewAction {
None,
ShowAddContact,
ShowMoments,
ShowCounterScreen,
ShowDefaultChoiceScreen,
ShowMyProfile,
ShowChat,
}

View file

@ -0,0 +1,846 @@
use std::{cell::RefCell, rc::Rc};
use makepad_widgets::*;
live_design! {
import makepad_draw::shader::std::*;
import makepad_widgets::base::*;
DrawStepsNav = {{DrawStepsNav}} {}
StepsNavBase = {{StepsNav}} {}
StepNav = <StepsNavBase> {
width: Fit,
height: Fit
label_walk: {
margin: {left: 20.0, top: 8, bottom: 8, right: 10}
width: Fit,
height: Fit,
}
label_align: {
y: 0.0
}
draw_check: {
uniform size: 7.0;
fn pixel(self) -> vec4 {
let sdf = Sdf2d::viewport(self.pos * self.rect_size)
match self.check_type {
CheckType::Check => {
let left = 3;
let sz = self.size;
let c = vec2(left + sz, self.rect_size.y * 0.5);
sdf.box(left, c.y - sz, sz * 2.0, sz * 2.0, 3.0); // rounding = 3rd value
sdf.fill_keep(mix(mix(#x00000077, #x00000044, pow(self.pos.y, 1.)), mix(#x000000AA, #x00000066, pow(self.pos.y, 1.0)), self.hover))
sdf.stroke(#x888, 1.0) // outline
let szs = sz * 0.5;
let dx = 1.0;
sdf.move_to(left + 4.0, c.y);
sdf.line_to(c.x, c.y + szs);
sdf.line_to(c.x + szs, c.y - szs);
sdf.stroke(mix(#fff0, #f, self.selected), 1.25);
}
CheckType::Radio => {
let sz = self.size;
let left = sz + 1.;
let c = vec2(left + sz, self.rect_size.y * 0.5);
sdf.circle(left, c.y, sz);
sdf.fill(#2);
let isz = sz * 0.5;
sdf.circle(left, c.y, isz);
sdf.fill(mix(#fff0, #f, self.selected));
}
CheckType::Toggle => {
let sz = self.size;
let left = sz + 1.;
let c = vec2(left + sz, self.rect_size.y * 0.5);
sdf.box(left, c.y - sz, sz * 3.0, sz * 2.0, 0.5 * sz);
sdf.fill(#2);
let isz = sz * 0.5;
sdf.circle(left + sz + self.selected * sz, c.y, isz);
sdf.circle(left + sz + self.selected * sz, c.y, 0.5 * isz);
sdf.subtract();
sdf.circle(left + sz + self.selected * sz, c.y, isz);
sdf.blend(self.selected)
sdf.fill(#f);
}
CheckType::None => {
return #0000
}
CheckType::Bar => {
let sz = self.size;
let left = sz + 1.;
let up = sz + 3.5;
let c = self.rect_size * vec2(0.5, 0.5);
sdf.box(
left,
c.y - up,
12. * sz + 5,
1. * sz,
1.9
);
sdf.fill(#232323);
sdf.stroke(#000, 0.5 + 0.5 * self.dpi_dilate);
let isz = sz * 0.5;
let ileft = isz + 4.5;
let iup = sz + 3.5;
sdf.box(
ileft,
c.y - iup,
12. * sz + 5,
1. * sz,
1.9
);
sdf.fill(mix(#fff0, #016def, self.selected));
}
}
return sdf.result
}
}
draw_text: {
color: #9,
instance focus: 0.0
instance selected: 0.0
instance hover: 0.0
text_style: {
font: {
//path: d"resources/IBMPlexSans-SemiBold.ttf"
}
font_size: 11.0
}
fn get_color(self) -> vec4 {
return mix(
mix(
#fff6,
#fff6,
self.hover
),
#fff6,
self.selected
)
}
}
draw_icon: {
instance focus: 0.0
instance hover: 0.0
instance selected: 0.0
fn get_color(self) -> vec4 {
return mix(
mix(
#9,
#c,
self.hover
),
#f,
self.selected
)
}
}
animator: {
hover = {
default: off
off = {
from: {all: Forward {duration: 0.15}}
apply: {
draw_check: {hover: 0.0}
draw_text: {hover: 0.0}
draw_icon: {hover: 0.0}
}
}
on = {
from: {all: Snap}
apply: {
draw_check: {hover: 1.0}
draw_text: {hover: 1.0}
draw_icon: {hover: 1.0}
}
}
}
focus = {
default: off
off = {
from: {all: Snap}
apply: {
draw_check: {focus: 0.0}
draw_text: {focus: 0.0}
draw_icon: {focus: 0.0}
}
}
on = {
from: {all: Snap}
apply: {
draw_check: {focus: 1.0}
draw_text: {focus: 1.0}
draw_icon: {focus: 1.0}
}
}
}
selected = {
default: off
off = {
from: {all: Forward {duration: 0.1}}
apply: {
draw_check: {selected: 0.0},
draw_text: {selected: 0.0},
draw_icon: {selected: 0.0},
}
}
on = {
from: {all: Forward {duration: 0.0}}
apply: {
draw_check: {selected: 1.0}
draw_text: {selected: 1.0}
draw_icon: {selected: 1.0},
}
}
}
}
}
Choices = {{Choices}} {
<View> {
flow: Down,
align: {x: 0.5, y: 0.3}
debug: A
choice_item: <StepNav> {
width: 100, height: 20,
margin: 0, padding: 0
flow: Right,
align: {x: 0.5, y: 0.5},
// show_bg: true
draw_check: {
check_type: Bar,
}
}
width: 100, height: 100,
// flow: Down,
}
}
ChoicesComponent = {{ChoicesComponent}} {
choices = <Choices>{}
}
// Choices = <StepNav> {
// width: 100, height: 20,
// margin: 0, padding: 0
// flow: Right,
// align: {x: 0.5, y: 0.5},
// // show_bg: true
// draw_check: {
// check_type: Bar,
// }
// }
}
#[derive(Live, Widget)]
pub struct ChoicesComponent {
#[animator]
animator: Animator,
#[walk]
walk: Walk,
#[layout]
layout: Layout,
#[live]
#[redraw]
draw_bg: DrawQuad,
#[live]
draw_icon: DrawIcon,
#[live]
icon_walk: Walk,
#[live]
bind: String,
#[live]
bind_enum: String,
#[live]
choices: Option<LivePtr>,
#[live]
labels: Vec<String>,
#[live]
values: Vec<LiveValue>,
#[live]
icons: Vec<LiveDependency>,
#[live]
popup_shift: DVec2,
#[rust]
is_open: bool,
#[live]
selected_item: usize,
}
impl LiveHook for ChoicesComponent {
fn after_apply_from(&mut self, cx: &mut Cx, apply: &mut Apply) {
if self.choices.is_none() || !apply.from.is_from_doc() {
return;
}
let global = cx.global::<ChoicesGlobal>().clone();
let mut map = global.map.borrow_mut();
// when live styling clean up old style references
map.retain(|k, _| cx.live_registry.borrow().generation_valid(*k));
let list_box = self.choices.unwrap();
map.get_or_insert(cx, list_box, |cx| Choices::new_from_ptr(cx, Some(list_box)));
}
}
#[derive(Clone, DefaultNone, Debug)]
pub enum ChoicesComponentAction {
Select(usize, LiveValue),
None,
}
impl Widget for ChoicesComponent {
fn widget_to_data(
&self,
_cx: &mut Cx,
actions: &Actions,
nodes: &mut LiveNodeVec,
path: &[LiveId],
) -> bool {
match actions.find_widget_action(self.widget_uid()).cast() {
ChoicesComponentAction::Select(_, value) => {
nodes.write_field_value(path, value.clone());
true
}
_ => false,
}
}
fn data_to_widget(&mut self, cx: &mut Cx, nodes: &[LiveNode], path: &[LiveId]) {
if let Some(value) = nodes.read_field_value(path) {
if let Some(index) = self.values.iter().position(|v| v == value) {
if self.selected_item != index {
self.selected_item = index;
self.redraw(cx);
}
} else {
// error!("Value not in values list {:?}", value);
}
}
}
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
self.animator_handle_event(cx, event);
let uid = self.widget_uid();
if self.choices.is_some() {
let global = cx.global::<ChoicesGlobal>().clone();
let mut map = global.map.borrow_mut();
let menu = map.get_mut(&self.choices.unwrap()).unwrap();
let mut close = false;
menu.handle_event_with(cx, event, self.draw_bg.area(), &mut |cx, action| {
if let ChoicesAction::WasSelected(node_id) = action {
self.selected_item = node_id.0 .0 as usize;
cx.widget_action(
uid,
&scope.path,
ChoicesComponentAction::Select(
self.selected_item,
self.values
.get(self.selected_item)
.cloned()
.unwrap_or(LiveValue::None),
),
);
self.draw_bg.redraw(cx);
close = true;
}
});
// if close {
// self.set_closed(cx);
// }
// check if we touch outside of the popup menu
if let Event::MouseDown(e) = event {
if !menu.menu_contains_pos(cx, e.abs) {
// self.set_closed(cx);
self.animator_play(cx, id!(hover.off));
}
}
}
// TODO: close on clicking outside of the popup menu
match event.hits_with_sweep_area(cx, self.draw_bg.area(), self.draw_bg.area()) {
Hit::KeyFocusLost(_) => {
// self.set_closed(cx);
self.animator_play(cx, id!(hover.off));
self.draw_bg.redraw(cx);
}
Hit::KeyDown(ke) => match ke.key_code {
KeyCode::ArrowUp => {
if self.selected_item > 0 {
self.selected_item -= 1;
cx.widget_action(
uid,
&scope.path,
ChoicesComponentAction::Select(
self.selected_item,
self.values
.get(self.selected_item)
.cloned()
.unwrap_or(LiveValue::None),
),
);
// self.set_closed(cx);
self.draw_bg.redraw(cx);
}
}
KeyCode::ArrowDown => {
if !self.values.is_empty() && self.selected_item < self.values.len() - 1 {
self.selected_item += 1;
cx.widget_action(
uid,
&scope.path,
ChoicesComponentAction::Select(
self.selected_item,
self.values
.get(self.selected_item)
.cloned()
.unwrap_or(LiveValue::None),
),
);
// self.set_closed(cx);
self.draw_bg.redraw(cx);
}
}
_ => (),
},
Hit::FingerDown(_fe) => {
cx.set_key_focus(self.draw_bg.area());
// self.toggle_open(cx);
self.animator_play(cx, id!(hover.pressed));
}
Hit::FingerHoverIn(_) => {
cx.set_cursor(MouseCursor::Hand);
self.animator_play(cx, id!(hover.on));
}
Hit::FingerHoverOut(_) => {
self.animator_play(cx, id!(hover.off));
}
Hit::FingerUp(fe) => {
if fe.is_over {
if fe.device.has_hovers() {
self.animator_play(cx, id!(hover.on));
}
} else {
self.animator_play(cx, id!(hover.off));
}
}
_ => (),
};
}
fn draw_walk(&mut self, cx: &mut Cx2d, _scope: &mut Scope, walk: Walk) -> DrawStep {
// cx.clear_sweep_lock(self.draw_bg.area());
self.draw_bg.begin(cx, walk, self.layout);
//let start_pos = cx.turtle().rect().pos;
self.draw_icon.draw_walk(cx, self.icon_walk);
self.draw_bg.end(cx);
// cx.add_nav_stop(self.draw_bg.area(), NavRole::DropDown, Margin::default());
if self.choices.is_some() {
// cx.set_sweep_lock(self.draw_bg.area());
let global = cx.global::<ChoicesGlobal>().clone();
let mut map = global.map.borrow_mut();
let popup_menu = map.get_mut(&self.choices.unwrap()).unwrap();
popup_menu.begin(cx);
for (i, item) in self.labels.iter().enumerate() {
let node_id = LiveId(i as u64).into();
popup_menu.draw_item(cx, node_id, item, walk);
}
popup_menu.end(cx, self.draw_bg.area());
}
DrawStep::done()
}
}
impl ChoicesComponentRef {
pub fn item_clicked(&mut self, item_id: &[LiveId], actions: &Actions) -> bool {
if let Some(item) = actions.find_widget_action(self.widget_uid()) {
if let ChoicesComponentAction::Select(_id, value) = item.cast() {
return LiveValue::Bool(true) == value.enum_eq(item_id);
}
}
return false;
}
}
#[derive(Live, LiveHook, LiveRegister)]
#[repr(C)]
pub struct DrawStepsNav {
#[deref]
draw_super: DrawQuad,
#[live]
check_type: CheckType,
#[live]
hover: f32,
#[live]
focus: f32,
#[live]
selected: f32,
}
#[derive(Live, LiveHook, Widget)]
pub struct StepsNav {
#[walk]
walk: Walk,
#[layout]
layout: Layout,
#[animator]
animator: Animator,
#[live]
icon_walk: Walk,
#[live]
label_walk: Walk, //
#[live]
#[redraw]
draw_name: DrawText, //
#[live]
label_align: Align, //
#[redraw]
#[live]
draw_check: DrawStepsNav, //
#[live]
draw_text: DrawText,
#[live]
draw_icon: DrawIcon,
#[live]
text: RcStringMut, //
#[live]
bind: String, //
}
#[derive(Clone, Debug, Default, Eq, Hash, Copy, PartialEq, FromLiveId)]
pub struct StepsNavId(pub LiveId);
#[derive(Live, LiveHook, LiveRegister)]
#[live_ignore]
#[repr(u32)]
pub enum CheckType {
#[pick]
Check = shader_enum(1),
Radio = shader_enum(2),
Toggle = shader_enum(3),
None = shader_enum(4),
Bar = shader_enum(5), //RadioType::Bar
}
// #[derive(Clone, Debug, DefaultNone)]
// pub enum StepsNavAction {
// Change(bool),
// None,
// }
#[derive(Default, Clone, Debug)]
pub enum StepsNavAction {
Change(bool),
WasSweeped,
WasSelected,
#[default]
None,
}
#[derive(Clone, DefaultNone, Debug)]
pub enum ChoicesAction {
WasSweeped(StepsNavId),
WasSelected(StepsNavId),
None,
}
impl StepsNav {
pub fn draw_walk(&mut self, cx: &mut Cx2d, walk: Walk) {
self.draw_check.begin(cx, walk, self.layout);
// self.draw_icon.svg_file = icon;
self.draw_text
.draw_walk(cx, self.label_walk, self.label_align, self.text.as_ref());
self.draw_icon.draw_walk(cx, self.icon_walk);
self.draw_check.end(cx);
}
pub fn handle_event_with(
&mut self,
cx: &mut Cx,
event: &Event,
sweep_area: Area,
dispatch_action: &mut dyn FnMut(&mut Cx, StepsNavAction),
) {
if self.animator_handle_event(cx, event).must_redraw() {
// self.draw_bg.area().redraw(cx);
self.draw_check.area().redraw(cx);
}
match event.hits_with_options(
cx,
// self.draw_bg.area(),
self.draw_check.area(),
HitOptions::new().with_sweep_area(sweep_area),
) {
Hit::FingerHoverIn(_) => {
self.animator_play(cx, id!(hover.on));
}
Hit::FingerHoverOut(_) => {
self.animator_play(cx, id!(hover.off));
}
Hit::FingerDown(_) => {
dispatch_action(cx, StepsNavAction::WasSweeped);
self.animator_play(cx, id!(hover.on));
self.animator_play(cx, id!(select.on));
}
Hit::FingerUp(se) => {
if !se.is_sweep {
dispatch_action(cx, StepsNavAction::WasSelected);
} else {
self.animator_play(cx, id!(hover.off));
self.animator_play(cx, id!(select.off));
}
}
_ => {}
}
}
}
impl Widget for StepsNav {
fn widget_to_data(
&self,
_cx: &mut Cx,
actions: &Actions,
nodes: &mut LiveNodeVec,
path: &[LiveId],
) -> bool {
match actions.find_widget_action_cast(self.widget_uid()) {
StepsNavAction::Change(v) => {
nodes.write_field_value(path, LiveValue::Bool(v));
true
}
_ => false,
}
}
fn data_to_widget(&mut self, cx: &mut Cx, nodes: &[LiveNode], path: &[LiveId]) {
if let Some(value) = nodes.read_field_value(path) {
if let Some(value) = value.as_bool() {
self.animator_toggle(cx, value, Animate::Yes, id!(selected.on), id!(selected.off));
}
}
}
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
let uid = self.widget_uid();
self.animator_handle_event(cx, event);
match event.hits(cx, self.draw_check.area()) {
Hit::FingerHoverIn(_) => {
cx.set_cursor(MouseCursor::Hand);
self.animator_play(cx, id!(hover.on));
}
Hit::FingerHoverOut(_) => {
self.animator_play(cx, id!(hover.off));
}
Hit::FingerDown(_fe) => {
if self.animator_in_state(cx, id!(selected.on)) {
self.animator_play(cx, id!(selected.off));
cx.widget_action(uid, &scope.path, StepsNavAction::Change(false));
} else {
self.animator_play(cx, id!(selected.on));
cx.widget_action(uid, &scope.path, StepsNavAction::Change(true));
}
}
Hit::FingerUp(_fe) => {}
Hit::FingerMove(_fe) => {}
_ => (),
}
}
fn draw_walk(&mut self, cx: &mut Cx2d, _scope: &mut Scope, walk: Walk) -> DrawStep {
self.draw_walk(cx, walk);
DrawStep::done()
}
fn text(&self) -> String {
self.text.as_ref().to_string()
}
fn set_text(&mut self, v: &str) {
self.text.as_mut_empty().push_str(v);
}
}
#[derive(Default, Clone)]
struct ChoicesGlobal {
map: Rc<RefCell<ComponentMap<LivePtr, Choices>>>,
}
#[derive(Live, LiveRegister, WidgetWrap)]
pub struct Choices {
#[live]
draw_list: DrawList2d,
#[live]
choice_item: Option<LivePtr>,
#[live]
#[redraw]
draw_bg: DrawQuad,
#[live]
#[redraw]
draw_icon: DrawIcon,
#[live]
icon_walk: Walk,
#[layout]
layout: Layout,
#[walk]
walk: Walk,
#[live]
labels: Vec<String>,
#[live]
values: Vec<LiveValue>,
#[live]
items: Vec<String>,
#[rust]
choice_items: ComponentMap<StepsNavId, StepsNav>,
#[rust]
init_select_item: Option<StepsNavId>,
#[rust]
first_tap: bool,
#[rust]
count: usize,
}
impl LiveHook for Choices {
fn after_apply(&mut self, cx: &mut Cx, apply: &mut Apply, index: usize, nodes: &[LiveNode]) {
if let Some(index) = nodes.child_by_name(index, live_id!(list_node).as_field()) {
for (_, node) in self.choice_items.iter_mut() {
node.apply(cx, apply, index, nodes);
}
}
self.draw_list.redraw(cx);
}
}
impl Widget for Choices {
fn handle_event(&mut self, _cx: &mut Cx, _event: &Event, _scope: &mut Scope) {}
fn draw_walk(&mut self, cx: &mut Cx2d, _scope: &mut Scope, walk: Walk) -> DrawStep {
self.draw_bg.begin(cx, walk, self.layout);
self.draw_icon.draw_walk(cx, self.icon_walk);
self.draw_bg.end(cx);
DrawStep::done()
}
}
impl Choices {
pub fn menu_contains_pos(&self, cx: &mut Cx, pos: DVec2) -> bool {
self.draw_bg.area().clipped_rect(cx).contains(pos)
}
pub fn begin(&mut self, cx: &mut Cx2d) {
self.draw_list.begin_overlay_reuse(cx);
cx.begin_pass_sized_turtle(Layout::flow_down());
self.draw_bg.begin(cx, self.walk, self.layout);
self.count = 0;
}
pub fn end(&mut self, cx: &mut Cx2d, shift_area: Area) {
self.draw_bg.end(cx);
let area = self.draw_bg.area().rect(cx);
let shift = DVec2 {
x: -area.size.x + (shift_area.rect(cx).size.x * 0.7),
y: 30.,
};
cx.end_pass_sized_turtle_with_shift(shift_area, shift);
self.draw_list.end(cx);
self.choice_items.retain_visible();
if let Some(init_select_item) = self.init_select_item.take() {
self.select_item_state(cx, init_select_item);
}
}
pub fn draw_item(
&mut self,
cx: &mut Cx2d,
item_id: StepsNavId,
label: &str,
// icon: LiveDependency,
walk: Walk,
) {
self.count += 1;
let menu_item = self.choice_item;
let menu_item = self
.choice_items
.get_or_insert(cx, item_id, |cx| StepsNav::new_from_ptr(cx, menu_item));
// menu_item.draw_walk(cx, label, icon);
menu_item.draw_walk(cx, walk);
}
pub fn init_select_item(&mut self, which_id: StepsNavId) {
self.init_select_item = Some(which_id);
self.first_tap = true;
}
fn select_item_state(&mut self, cx: &mut Cx, which_id: StepsNavId) {
for (id, item) in &mut *self.choice_items {
if *id == which_id {
item.animator_cut(cx, id!(select.on));
item.animator_cut(cx, id!(hover.on));
} else {
item.animator_cut(cx, id!(select.off));
item.animator_cut(cx, id!(hover.off));
}
}
}
pub fn handle_event_with(
&mut self,
cx: &mut Cx,
event: &Event,
sweep_area: Area,
dispatch_action: &mut dyn FnMut(&mut Cx, ChoicesAction),
) {
let mut actions = Vec::new();
for (item_id, node) in self.choice_items.iter_mut() {
node.handle_event_with(cx, event, sweep_area, &mut |_, e| {
actions.push((*item_id, e))
});
}
for (node_id, action) in actions {
match action {
StepsNavAction::WasSweeped => {
self.select_item_state(cx, node_id);
dispatch_action(cx, ChoicesAction::WasSweeped(node_id));
}
StepsNavAction::WasSelected => {
self.select_item_state(cx, node_id);
dispatch_action(cx, ChoicesAction::WasSelected(node_id));
}
_ => (),
}
}
}
}

View file

@ -55,13 +55,18 @@ live_design! {
COLOR_DOWN_FULL = #000 COLOR_DOWN_FULL = #000
COLOR_DOWN_0 = #x00000000 // COLOR_DOWN_0 = #x00000000
// COLOR_DOWN_0 = #x00bbd3
COLOR_DOWN_0 = #x1c1c1c
COLOR_DOWN_1 = #x00000011 COLOR_DOWN_1 = #x00000011
COLOR_DOWN_2 = #x00000022 COLOR_DOWN_2 = #x00000022
COLOR_DOWN_3 = #x00000044 COLOR_DOWN_3 = #x00000044
COLOR_DOWN_4 = #x00000066 COLOR_DOWN_4 = #x00000066
// COLOR_DOWN_4 = #016def
// COLOR_DOWN_4 = #x086975
COLOR_DOWN_5 = #x000000AA COLOR_DOWN_5 = #x000000AA
COLOR_DOWN_6 = #x000000CC COLOR_DOWN_6 = #x000000CC
COLOR_DOWN_7 = #x00bbd3
COLOR_UP_0 = #xFFFFFF00 COLOR_UP_0 = #xFFFFFF00
COLOR_UP_1 = #xFFFFFF0A COLOR_UP_1 = #xFFFFFF0A

View file

@ -1,5 +1,20 @@
// #![allow(unused)]
// #![cfg(target_arch = "wasm32")]
// use makepad_wasm_bridge::*;
#[cfg(not(target_arch = "wasm32"))]
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
// pub fn get_current_year() -> i32 {
// let now = SystemTime::now();
// let since_the_epoch = now.duration_since(UNIX_EPOCH).expect("Time went backwards");
// let in_seconds = since_the_epoch.as_secs();
// let seconds_per_year = 60 * 60 * 24 * 365;
// let current_year = 1970 + (in_seconds / seconds_per_year as u64) as i32;
// current_year
// }
#[cfg(not(target_arch = "wasm32"))]
pub fn get_current_year() -> i32 { pub fn get_current_year() -> i32 {
let now = SystemTime::now(); let now = SystemTime::now();
let since_the_epoch = now.duration_since(UNIX_EPOCH).expect("Time went backwards"); let since_the_epoch = now.duration_since(UNIX_EPOCH).expect("Time went backwards");