This commit is contained in:
Tsuki 2024-11-26 12:02:44 +08:00
parent e022e2b8a8
commit 8cbcc869d3
8 changed files with 638 additions and 210 deletions

1
Cargo.lock generated
View File

@ -1715,6 +1715,7 @@ dependencies = [
"element_bridge",
"futures",
"glam",
"indexmap",
"log",
"makepad-widgets",
"mp_core",

View File

@ -20,3 +20,4 @@ element_bridge = { path = "../element_bridge", version = "*" }
futures = "0.3.31"
async-trait = "0.1.83"
thiserror = "2.0.3"
indexmap = "2.6.0"

View File

@ -1,6 +1,10 @@
use crate::file::*;
use crate::menu::{handle_menu, spawn_background};
use crate::widgets::area::TAreaWidgetRefExt;
use crate::widgets::selector::{
ItemKey, ItemValue, SelectorListWidgetRefExt, SelectorWidgetRefExt,
};
use crate::windows_manager::WM;
use crate::{render_task::RenderTasks, PLUGIN_MANAGER, RUNTIME};
use ::log::info;
@ -16,7 +20,6 @@ live_design! {
import makepad_widgets::base::*;
import makepad_widgets::theme_desktop_dark::*;
import crate::app_ui::MainView;
import crate::widgets::selector_modal::SelectorModal;
HELLO = "Hello, World!";
@ -87,8 +90,6 @@ live_design! {
}
}
selector = <SelectorModal>{}
<MainView>{}
}
window_menu = {
@ -177,8 +178,8 @@ impl LiveRegister for App {
crate::app_ui::live_design(_cx);
crate::widgets::area::live_design(_cx);
crate::widgets::renderer::live_design(_cx);
crate::widgets::selector_modal::live_design(_cx);
crate::widgets::background_text::live_design(_cx);
crate::widgets::selector::live_design(_cx);
}
}
@ -200,6 +201,71 @@ impl MatchEvent for App {
let render_task = render_tasks.lock().await;
render_task.render().await;
});
let items = vec![
(
ItemKey {
path: "path".to_string(),
category: "category".to_string(),
description: "description_1".to_string(),
},
vec![
ItemValue {
name: "Item 1".to_string(),
},
ItemValue {
name: "Item 2".to_string(),
},
],
),
(
ItemKey {
path: "path".to_string(),
category: "category".to_string(),
description: "description_1".to_string(),
},
vec![
ItemValue {
name: "Item 1".to_string(),
},
ItemValue {
name: "Item 2".to_string(),
},
],
),
(
ItemKey {
path: "path".to_string(),
category: "category".to_string(),
description: "description_2".to_string(),
},
vec![
ItemValue {
name: "Item 1".to_string(),
},
ItemValue {
name: "Item 2".to_string(),
},
],
),
(
ItemKey {
path: "path".to_string(),
category: "category".to_string(),
description: "description_1".to_string(),
},
vec![
ItemValue {
name: "Item 1".to_string(),
},
ItemValue {
name: "Item 3".to_string(),
},
],
),
];
ui.selector(id!(a)).set_items(cx, items);
}
fn handle_signal(&mut self, cx: &mut Cx) {

View File

@ -1,4 +1,3 @@
use makepad_widgets::Dock;
use makepad_widgets::*;
live_design! {
import makepad_widgets::base::*;
@ -7,8 +6,9 @@ live_design! {
import crate::widgets::renderer::IRenderer;
import makepad_draw::shader::std::*;
import crate::widgets::selector_modal::SelectorItem;
import crate::widgets::selector_modal::*;
import crate::widgets::background_text::BackgroundLabel;
import crate::widgets::selector::*;
HELLO = "Hello, World!";
@ -62,8 +62,16 @@ live_design! {
x: 0.5,
y: 0.5
},
quad = <Area> {
id: "Primary"
<View> {
width: 600
height: 500
align: {
x: 0.5,
y: 0.5
}
a = <Selector> {}
}
}
@ -130,6 +138,7 @@ live_design! {
<H4>{ text: "Input"}
<View>{
flow: Right
width: Fill
@ -146,10 +155,9 @@ live_design! {
<BackgroundLabel>{
text: "Holy Shit",
}
}
<Group> {
<ButtonGroup> {

View File

@ -1,4 +1,4 @@
pub mod area;
pub mod background_text;
pub mod renderer;
pub mod selector_modal;
pub mod selector;

550
mp/src/widgets/selector.rs Normal file
View File

@ -0,0 +1,550 @@
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")
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:10
}
draw_bg: {
color: #3c3c3c
border_color: #fff
border_width: 0.5
}
class_title = <Label> {
draw_text: {
color: #fff
text_style: <THEME_FONT_BOLD>{
font_size: 8.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: dep("crate://self/resources/logo_makepad.svg")
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}} {
<RoundedShadowView> {
height: Fill
width: Fill
flow: Down
padding: 15.0
align: {y:0.0}
draw_bg: {
color: #3c3c3c
shadow_radius: 30.0
radius: 5.0
}
<H3> {
text: "Selector"
}
<View> {
height: Fill
width: Fill
flow: Down
<RView> {
height: Fill
width: Fill
flow: Down
draw_bg: {
color: #3c3c3c
border_color: #fff
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
<Button> {
text: "Previous"
}
<Button> {
text: "Next"
}
}
}
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ItemKey {
pub path: String,
pub category: String,
pub description: String,
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct ItemValue {
pub name: String,
}
#[derive(Live, LiveHook, Widget)]
pub struct SelectorList {
#[redraw]
#[live]
draw_bg: DrawColor,
#[layout]
layout: Layout,
#[walk]
walk: Walk,
#[rust]
// items: Vec<ItemValue>,
items: IndexSet<ItemValue>,
#[rust]
item_refs: Vec<WidgetRef>,
#[live]
item_ref: Option<LivePtr>,
#[rust]
selected: Option<usize>,
}
impl Widget for SelectorList {
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
let mut selected = None;
self.item_refs
.iter_mut()
.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) {
selected = Some(idx);
}
});
self.selected = selected;
if let Some(selected) = self.selected {
let item = &self.item_refs[selected];
for item in self.item_refs.iter() {
item.as_selector_item().set_selected(false);
}
item.as_selector_item().set_selected(true);
item.redraw(cx);
}
}
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, items: &Vec<ItemValue>) {
for item in items.iter() {
self.items.insert(item.clone());
}
for item in self.items.iter() {
let new_ref = WidgetRef::new_from_ptr(cx, self.item_ref);
new_ref.as_selector_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: &ItemValue) {
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);
}
}
}
impl SelectorListRef {
pub fn set_items(&self, cx: &mut Cx, items: &Vec<ItemValue>) {
if let Some(mut item) = self.borrow_mut() {
item.set_items(cx, items);
}
}
pub fn insert_if_not_exists(&self, cx: &mut Cx, item: &ItemValue) {
if let Some(mut _item) = self.borrow_mut() {
_item.insert_if_not_exists(cx, item);
}
}
}
#[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(54.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>)>,
}
impl Widget for Selector {
fn handle_event(&mut self, _cx: &mut Cx, _event: &Event, _scope: &mut Scope) {
self.view.handle_event(_cx, _event, _scope);
}
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);
}
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);
}
}
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);
}
}
}

View File

@ -1,193 +0,0 @@
use makepad_derive_widget::*;
use makepad_micro_serde::*;
use makepad_micro_serde::{DeRon, SerRon};
use makepad_widgets::Dock;
use makepad_widgets::*;
live_design! {
import makepad_draw::shader::std::*;
import makepad_widgets::base::*;
import makepad_widgets::theme_desktop_dark::*;
ICO_SEARCH = dep("crate://self/resources/icons/Icon_Search.svg")
}
#[derive(Debug, Clone, Default, Eq, Hash, Copy, PartialEq, FromLiveId)]
pub struct SelectorModalItemId(pub LiveId);
#[derive(Live, LiveHook, Widget)]
pub struct SelectorModalItem {
#[redraw]
#[live]
draw_bg: DrawQuad,
#[live]
draw_icon_bg: DrawQuad,
#[live]
draw_icon: DrawIcon,
#[live]
draw_label_bg: DrawQuad,
#[live]
draw_label: DrawText,
#[layout]
layout: Layout,
#[walk]
walk: Walk,
#[live]
icon_walk: Walk,
#[rust]
area: Area,
#[rust]
text: ArcStringMut,
#[live]
selected: f32,
#[live]
hover: f32,
}
pub enum SelectorModalItemAction {
WasSweeped,
WasSelected,
MightBeSelected,
None,
}
impl SelectorModalItem {
pub fn handle_event_with(
&mut self,
cx: &mut Cx,
event: &Event,
sweep_area: Area,
dispatch_action: &mut dyn FnMut(&mut Cx, SelectorModalItemAction),
) {
match event.hits_with_options(cx, self.area, HitOptions::new().with_sweep_area(sweep_area))
{
Hit::FingerHoverIn(_) => {}
Hit::FingerHoverOut(_) => {}
Hit::FingerDown(_) => {
dispatch_action(cx, SelectorModalItemAction::WasSweeped);
}
Hit::FingerUp(se) => {
if !se.is_sweep {
dispatch_action(cx, SelectorModalItemAction::WasSelected);
} else {
}
}
_ => {}
}
}
}
impl Widget for SelectorModalItem {
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
let walk = Walk::fit().with_margin_all(5.0);
cx.begin_turtle(walk, Layout::default().with_align_x(0.5));
let icon_walk = Walk::fixed(64.0, 64.0).with_add_padding(Padding {
left: 10.0,
right: 10.0,
top: 10.0,
bottom: 10.0,
});
self.draw_icon_bg.begin(
cx,
icon_walk,
Layout::default().with_align_x(0.5).with_align_y(0.5),
);
self.draw_icon.draw_walk(
cx,
Walk {
width: Size::Fixed(54.0),
height: Size::Fit,
..Default::default()
},
);
self.draw_icon_bg.end(cx);
self.draw_label_bg.begin(
cx,
Walk::fit().with_margin_all(5.0),
Layout::default().with_align_x(0.5).with_align_y(0.5),
);
self.draw_label.draw_walk(
cx,
Walk::fit(),
Align { x: 0.5, y: 0.5 },
self.text.as_ref(),
);
self.draw_label_bg.end(cx);
cx.end_turtle_with_area(&mut self.area);
DrawStep::done()
}
fn text(&self) -> String {
self.text.as_ref().to_string()
}
}
#[derive(Live, LiveHook, Widget)]
pub struct SelectorModalItemList {
#[redraw]
#[live]
draw_bg: DrawQuad,
#[rust]
items: ComponentMap<SelectorModalItemId, SelectorModalItem>,
#[rust]
count: usize,
#[live]
draw_title: DrawText,
#[rust]
title: String,
}
impl Widget for SelectorModalItemList {
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
let walk = Walk {
width: Size::Fill,
height: Size::Fit,
..Default::default()
};
let mut layout = Layout::default();
layout.flow = Flow::RightWrap;
let items = self.items.iter_mut();
cx.begin_turtle(walk, layout);
for (_, item) in items {
item.draw_walk(cx, scope, Walk::default());
}
cx.end_turtle();
DrawStep::done()
}
}
impl SelectorModalItemList {
pub fn set_items(&mut self, cx: &mut Cx, items: Vec<(String, String, String)>) {
self.items.insert(
SelectorModalItemId(LiveId::new(cx)),
SelectorModalItem::new(cx),
);
}
}
#[derive(Clone, Debug, Live, LiveHook, SerRon, DeRon)]
pub struct SelectorItem {
#[live("icon")]
icon: String,
#[live("label")]
label: String,
}

View File

@ -1,7 +1,4 @@
use std::cell::RefCell;
use std::sync::{Arc, Mutex, MutexGuard};
use crate::elements::{Element, ElementAttach, Elements, ElementsRef};
use crate::elements::{Element, ElementAttach, Elements};
use crate::elementvec::ElementVec;
use encase;
use quick_cache::sync::Cache;
@ -277,8 +274,6 @@ impl DrawList {
}
}
// #[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable, Default)]
// #[repr(C)]
#[derive(Debug, Clone, Copy, encase::ShaderType, Default)]
pub struct CommonUniform {
pub model_matrix: glam::Mat4,