653 lines
17 KiB
Rust
653 lines
17 KiB
Rust
use std::collections::HashMap;
|
|
|
|
use indexmap::IndexSet;
|
|
use makepad_widgets::*;
|
|
|
|
live_design! {
|
|
import makepad_widgets::base::*;
|
|
import makepad_widgets::theme_desktop_dark::*;
|
|
import makepad_draw::shader::std::*;
|
|
|
|
ICO_SEARCH = dep("crate://self/resources/icons/Icon_Search.svg")
|
|
RADAR = dep("crate://self/resources/icons/icon_radar.svg")
|
|
THREED = dep("crate://self/resources/icons/icon_threed.svg")
|
|
|
|
RView = <ViewBase> {
|
|
show_bg: true,
|
|
draw_bg: {
|
|
instance border_width: 0.0
|
|
instance border_color: #0000
|
|
instance inset: vec4(0.0, 0.0, 0.0, 0.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.rect(
|
|
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)
|
|
)
|
|
sdf.fill_keep(self.get_color())
|
|
if self.border_width > 0.0 {
|
|
sdf.stroke(self.get_border_color(), self.border_width)
|
|
}
|
|
return sdf.result
|
|
}
|
|
}
|
|
}
|
|
|
|
TitleBar = <RView>{
|
|
height: 24
|
|
width: Fill
|
|
align: {
|
|
y:0.5
|
|
}
|
|
|
|
padding: {
|
|
left:20
|
|
}
|
|
|
|
draw_bg: {
|
|
color: #202121
|
|
border_color: #9c9c9c
|
|
border_width: 0.5
|
|
}
|
|
|
|
class_title = <Label> {
|
|
|
|
draw_text: {
|
|
color: #9c9c9c
|
|
text_style: <THEME_FONT_BOLD>{
|
|
font_size: 10.0
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
SelectorItem = {{SelectorItem}}{
|
|
flow: Down
|
|
draw_title: {
|
|
color: #fff
|
|
text_style: <THEME_FONT_BOLD>{
|
|
font_size: 8.0
|
|
}
|
|
}
|
|
|
|
draw_icon_bg: {
|
|
|
|
instance color: #3c3c3c
|
|
instance radius: 3.0
|
|
|
|
fn get_color(self) -> vec4 {
|
|
return self.color;
|
|
}
|
|
|
|
fn pixel(self) -> vec4 {
|
|
let sdf = Sdf2d::viewport(self.pos * self.rect_size)
|
|
sdf.box(
|
|
0.0,0.0,
|
|
self.rect_size.x,
|
|
self.rect_size.y,
|
|
max(1.0, self.radius)
|
|
)
|
|
sdf.fill_keep(self.get_color())
|
|
return sdf.result;
|
|
}
|
|
|
|
}
|
|
|
|
draw_title_bg: {
|
|
instance color: #0078D7
|
|
instance radius: 3.0
|
|
|
|
fn get_color(self) -> vec4 {
|
|
return self.color;
|
|
}
|
|
|
|
fn pixel(self) -> vec4 {
|
|
let sdf = Sdf2d::viewport(self.pos * self.rect_size)
|
|
sdf.box(
|
|
0.0,0.0,
|
|
self.rect_size.x,
|
|
self.rect_size.y,
|
|
max(1.0, self.radius)
|
|
)
|
|
sdf.fill_keep(self.get_color())
|
|
return sdf.result;
|
|
}
|
|
}
|
|
|
|
draw_icon: {
|
|
svg_file: (THREED)
|
|
fn get_color(self) -> vec4 {
|
|
return #0A60FE;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
SelectorList = {{SelectorList}} {
|
|
width: 250
|
|
height: Fit
|
|
flow: RightWrap
|
|
padding: {
|
|
top: 15
|
|
bottom: 15
|
|
}
|
|
align: {x:0.0, y:0.0}
|
|
item_ref: <SelectorItem>{}
|
|
}
|
|
|
|
|
|
|
|
Selector = {{Selector}} {
|
|
|
|
<RoundedView> {
|
|
height: Fill
|
|
width: Fill
|
|
flow: Down
|
|
padding: 20.0
|
|
|
|
align: {y:0.0}
|
|
|
|
draw_bg: {
|
|
color: #202121
|
|
radius: 5.0
|
|
}
|
|
<View> {
|
|
height: Fill
|
|
width: Fill
|
|
flow: Down
|
|
|
|
<Label> {
|
|
text: "choose a template for your project"
|
|
|
|
draw_text: {
|
|
color: #ffffff
|
|
}
|
|
|
|
margin: {
|
|
bottom: 5.0
|
|
}
|
|
}
|
|
|
|
<RView> {
|
|
height: 26
|
|
width: Fill
|
|
|
|
draw_bg: {
|
|
color: #303030
|
|
border_color: #9c9c9c
|
|
border_width: 0.5
|
|
}
|
|
|
|
align: {
|
|
y: 0.5
|
|
}
|
|
|
|
padding: {
|
|
left: 20
|
|
}
|
|
|
|
file_base_name = <Label> {
|
|
text: "File Base Name"
|
|
draw_text: {
|
|
color: #9c9c9c
|
|
text_style: <THEME_FONT_BOLD>{
|
|
font_size: 8.0
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
<RView> {
|
|
height: Fill
|
|
width: Fill
|
|
flow: Down
|
|
|
|
draw_bg: {
|
|
color: #202121
|
|
border_color: #9c9c9c
|
|
border_width: 0.5
|
|
}
|
|
|
|
list = <PortalList> {
|
|
|
|
keep_invisible: true
|
|
auto_tail: false
|
|
width: Fill, height: Fill
|
|
flow: Down,
|
|
|
|
title_bar = <TitleBar> {}
|
|
list_item = <SelectorList> {}
|
|
}
|
|
}
|
|
|
|
<View> {
|
|
width: Fill
|
|
height:Fit
|
|
|
|
padding: {
|
|
top: 30
|
|
}
|
|
|
|
align: {
|
|
x: 1.0
|
|
}
|
|
|
|
<View> {
|
|
|
|
width: Fit
|
|
height: Fit
|
|
|
|
flow: Right
|
|
spacing:10.0
|
|
|
|
pre_button = <Button> {
|
|
width: 90
|
|
text: "Previous"
|
|
}
|
|
|
|
ok_button = <Button> {
|
|
width: 90
|
|
text: "Next"
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(DefaultNone, Debug, Clone)]
|
|
pub enum SelectorAction {
|
|
None,
|
|
SelectItem(ItemKey, ItemValue, usize, usize),
|
|
|
|
Selected(ItemKey, usize),
|
|
Unselected(ItemKey, usize),
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
pub struct ItemKey {
|
|
pub path: String,
|
|
pub category: String,
|
|
pub description: String,
|
|
}
|
|
|
|
#[derive(Clone, PartialEq, Eq, Hash, Default, Debug)]
|
|
pub struct ItemValue {
|
|
pub name: String,
|
|
pub icon: String,
|
|
}
|
|
|
|
#[derive(Live, LiveHook, Widget)]
|
|
pub struct SelectorList {
|
|
#[redraw]
|
|
#[live]
|
|
draw_bg: DrawColor,
|
|
#[layout]
|
|
layout: Layout,
|
|
#[walk]
|
|
walk: Walk,
|
|
#[rust]
|
|
class: Option<ItemKey>,
|
|
#[rust]
|
|
items: IndexSet<ItemValue>,
|
|
#[rust]
|
|
item_refs: Vec<WidgetRef>,
|
|
#[live]
|
|
item_ref: Option<LivePtr>,
|
|
#[rust]
|
|
selected: Option<usize>,
|
|
|
|
#[rust]
|
|
entry_id: usize,
|
|
}
|
|
|
|
impl Widget for SelectorList {
|
|
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
|
|
let key = self.class.clone().unwrap();
|
|
self.item_refs.iter().enumerate().for_each(|(idx, item)| {
|
|
// item.handle_event(_cx, _event, _scope);
|
|
let _item = item.as_selector_item();
|
|
if _item.handle_event_with_a(cx, event, scope) {
|
|
cx.widget_action(
|
|
self.widget_uid(),
|
|
&scope.path,
|
|
SelectorAction::SelectItem(
|
|
key.clone(),
|
|
self.items.get_index(idx).cloned().unwrap(),
|
|
self.entry_id,
|
|
idx,
|
|
),
|
|
);
|
|
}
|
|
});
|
|
}
|
|
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
|
|
self.draw_bg.begin(cx, Walk::fill(), self.layout);
|
|
|
|
for item in self.item_refs.iter() {
|
|
item.draw_walk(cx, scope, walk).step();
|
|
}
|
|
|
|
self.draw_bg.end(cx);
|
|
DrawStep::done()
|
|
}
|
|
}
|
|
|
|
impl SelectorList {
|
|
pub fn set_items(&mut self, cx: &mut Cx, class: ItemKey, items: &Vec<ItemValue>) {
|
|
for item in items.iter() {
|
|
self.items.insert(item.clone());
|
|
}
|
|
|
|
self.class = Some(class.clone());
|
|
|
|
for item in self.items.iter() {
|
|
let new_ref = WidgetRef::new_from_ptr(cx, self.item_ref);
|
|
let mut _item = new_ref.as_selector_item();
|
|
_item.set_text(&item.name);
|
|
|
|
// self.item_refs.insert(k.clone(), new_ref);
|
|
self.item_refs.push(new_ref);
|
|
}
|
|
}
|
|
|
|
pub fn insert_if_not_exists(&mut self, cx: &mut Cx, item_id: usize, item: &ItemValue) {
|
|
self.entry_id = item_id;
|
|
if !self.items.contains(item) {
|
|
self.items.insert(item.clone());
|
|
let new_ref = WidgetRef::new_from_ptr(cx, self.item_ref);
|
|
new_ref.as_selector_item().set_text(&item.name);
|
|
self.item_refs.push(new_ref);
|
|
}
|
|
}
|
|
|
|
pub fn set_selected(&mut self, cx: &mut Cx, idx: usize, selected: bool) {
|
|
self.item_refs
|
|
.get_mut(idx)
|
|
.unwrap()
|
|
.as_selector_item()
|
|
.set_selected(selected);
|
|
|
|
self.redraw(cx);
|
|
}
|
|
}
|
|
|
|
impl SelectorListRef {
|
|
pub fn set_items(&self, cx: &mut Cx, class: ItemKey, items: &Vec<ItemValue>) {
|
|
if let Some(mut item) = self.borrow_mut() {
|
|
item.set_items(cx, class, items);
|
|
}
|
|
}
|
|
|
|
pub fn set_class(&self, class: ItemKey) {
|
|
if let Some(mut item) = self.borrow_mut() {
|
|
item.class = Some(class);
|
|
}
|
|
}
|
|
|
|
pub fn insert_if_not_exists(&self, cx: &mut Cx, item_id: usize, item: &ItemValue) {
|
|
if let Some(mut _item) = self.borrow_mut() {
|
|
_item.insert_if_not_exists(cx, item_id, item);
|
|
}
|
|
}
|
|
|
|
pub fn set_selected(&self, cx: &mut Cx, idx: usize, selected: bool) {
|
|
if let Some(mut _item) = self.borrow_mut() {
|
|
_item.set_selected(cx, idx, selected);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Live, LiveHook, Widget)]
|
|
pub struct SelectorItem {
|
|
#[redraw]
|
|
area: Area,
|
|
#[live]
|
|
draw_icon: DrawIcon,
|
|
#[live]
|
|
draw_icon_bg: DrawQuad,
|
|
#[live]
|
|
draw_title: DrawText,
|
|
#[live]
|
|
draw_title_bg: DrawQuad,
|
|
#[layout]
|
|
layout: Layout,
|
|
#[walk]
|
|
walk: Walk,
|
|
|
|
#[live]
|
|
text: ArcStringMut,
|
|
#[rust]
|
|
selected: bool,
|
|
}
|
|
|
|
impl Widget for SelectorItem {
|
|
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 {
|
|
let walk = Walk::fit().with_margin_left(10.0).with_margin_right(10.0);
|
|
|
|
let icon_bg_color = if self.selected {
|
|
vec4(0.0392, 0.3764, 0.9960, 0.1)
|
|
} else {
|
|
vec4(0.0, 0.0, 0.0, 0.0)
|
|
};
|
|
|
|
let title_bg_color = if self.selected {
|
|
vec4(0.0, 0.4706, 0.8431, 1.0)
|
|
} else {
|
|
vec4(0.0, 0.0, 0.0, 0.0)
|
|
};
|
|
|
|
self.apply_over(
|
|
cx,
|
|
live! {
|
|
draw_icon_bg: {
|
|
color: (icon_bg_color)
|
|
}
|
|
draw_title_bg: {
|
|
color: (title_bg_color)
|
|
}
|
|
},
|
|
);
|
|
|
|
cx.begin_turtle(walk, Layout::flow_down().with_align_x(0.5));
|
|
|
|
self.draw_icon_bg.begin(
|
|
cx,
|
|
Walk::fixed(64.0, 64.0),
|
|
Layout::default().with_align_x(0.5).with_align_y(0.5),
|
|
);
|
|
|
|
let icon_walk = Walk {
|
|
width: Size::Fixed(32.0),
|
|
height: Size::Fit,
|
|
..Default::default()
|
|
};
|
|
|
|
self.draw_icon.draw_walk(cx, icon_walk);
|
|
self.draw_icon_bg.end(cx);
|
|
|
|
self.draw_title_bg.begin(
|
|
cx,
|
|
Walk::fit().with_margin_top(5.0),
|
|
Layout::default().with_align_x(0.5).with_align_y(0.5),
|
|
);
|
|
|
|
self.draw_title.draw_walk(
|
|
cx,
|
|
Walk::fit().with_margin_all(5.0),
|
|
Align { x: 0.5, y: 0.5 },
|
|
self.text.as_ref(),
|
|
);
|
|
|
|
self.draw_title_bg.end(cx);
|
|
|
|
cx.end_turtle_with_area(&mut self.area);
|
|
|
|
DrawStep::done()
|
|
}
|
|
|
|
fn set_text(&mut self, _v: &str) {
|
|
self.text.as_mut_empty().push_str(_v);
|
|
}
|
|
}
|
|
|
|
impl SelectorItem {
|
|
fn handle_event_with(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) -> bool {
|
|
match event.hits_with_sweep_area(cx, self.draw_icon_bg.area(), self.draw_title_bg.area()) {
|
|
Hit::FingerDown(_fe) => true,
|
|
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
fn set_selected(&mut self, selected: bool) {
|
|
self.selected = selected;
|
|
}
|
|
}
|
|
|
|
impl SelectorItemRef {
|
|
pub fn handle_event_with_a(&self, cx: &mut Cx, event: &Event, scope: &mut Scope) -> bool {
|
|
if let Some(mut item) = self.borrow_mut() {
|
|
item.handle_event_with(cx, event, scope)
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
|
|
fn set_selected(&self, selected: bool) {
|
|
if let Some(mut item) = self.borrow_mut() {
|
|
item.set_selected(selected);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Live, LiveHook, Widget)]
|
|
pub struct Selector {
|
|
#[deref]
|
|
view: View,
|
|
|
|
#[rust]
|
|
class: Vec<(ItemKey, Vec<ItemValue>)>,
|
|
|
|
#[rust]
|
|
selected: Option<(ItemKey, ItemValue, usize, usize)>,
|
|
}
|
|
|
|
impl Widget for Selector {
|
|
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
|
|
let list = self.view.widget(id!(list)).as_portal_list();
|
|
let ok_button = self.view.widget(id!(ok_button)).as_button();
|
|
for action in cx.capture_actions(|cx| self.view.handle_event(cx, event, scope)) {
|
|
match action.as_widget_action().cast() {
|
|
SelectorAction::SelectItem(key, item, list_id, item_id) => {
|
|
let raw_selected = &self.selected;
|
|
if let Some(raw_selected) = raw_selected {
|
|
list.get_item(raw_selected.2).map(|v| {
|
|
v.1.as_selector_list()
|
|
.set_selected(cx, raw_selected.3, false);
|
|
});
|
|
}
|
|
self.selected = Some((key, item, list_id, item_id));
|
|
|
|
list.get_item(list_id).map(|v| {
|
|
v.1.as_selector_list().set_selected(cx, item_id, true);
|
|
});
|
|
|
|
ok_button.set_enabled(self.selected.is_some());
|
|
}
|
|
|
|
_ => {}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
|
|
while let Some(widget) = self.view.draw_walk(cx, scope, walk).step() {
|
|
let list = widget.as_portal_list();
|
|
let Some(mut list) = list.borrow_mut() else {
|
|
continue;
|
|
};
|
|
|
|
let count = self.class.len() * 2;
|
|
// TitleBar and List
|
|
list.set_item_range(cx, 0, count);
|
|
|
|
while let Some(item_id) = list.next_visible_item(cx) {
|
|
let prepared = self.class.get(item_id / 2);
|
|
if let Some(prepared) = prepared {
|
|
let item = if item_id % 2 == 0 {
|
|
let title_bar = list.item(cx, item_id, live_id!(title_bar));
|
|
let title = &prepared.0.category;
|
|
title_bar.as_view().apply_over(
|
|
cx,
|
|
live! {
|
|
class_title = {
|
|
text: (title)
|
|
}
|
|
},
|
|
);
|
|
title_bar
|
|
} else {
|
|
let list_item = list.item(cx, item_id, live_id!(list_item));
|
|
let list = list_item.as_selector_list();
|
|
|
|
for item in prepared.1.iter() {
|
|
list.insert_if_not_exists(cx, item_id, item);
|
|
}
|
|
|
|
list.set_class(prepared.0.clone());
|
|
list_item
|
|
};
|
|
|
|
item.draw_all(cx, scope);
|
|
}
|
|
}
|
|
}
|
|
DrawStep::done()
|
|
}
|
|
}
|
|
|
|
impl Selector {
|
|
fn set_items(&mut self, cx: &mut Cx, items: Vec<(ItemKey, Vec<ItemValue>)>) {
|
|
self.class = items;
|
|
self.view.redraw(cx);
|
|
}
|
|
|
|
fn set_base_name(&mut self, name: &str) {
|
|
let title_bar = self.view.widget(id!(file_base_name)).as_label();
|
|
title_bar.set_text(name);
|
|
}
|
|
}
|
|
|
|
impl SelectorRef {
|
|
pub fn set_items(&self, cx: &mut Cx, items: Vec<(ItemKey, Vec<ItemValue>)>) {
|
|
if let Some(mut item) = self.borrow_mut() {
|
|
item.set_items(cx, items);
|
|
}
|
|
}
|
|
|
|
pub fn set_base_name(&self, name: &str) {
|
|
if let Some(mut item) = self.borrow_mut() {
|
|
item.set_base_name(name);
|
|
}
|
|
}
|
|
}
|