radarmp/mp/src/widgets/selector.rs
2024-11-26 16:59:54 +08:00

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);
}
}
}