ticking context menu
This commit is contained in:
@@ -196,7 +196,7 @@ impl TestbedGeneric {
|
|||||||
button_context_menu.on_click({
|
button_context_menu.on_click({
|
||||||
let tasks = testbed.tasks.clone();
|
let tasks = testbed.tasks.clone();
|
||||||
Box::new(move |_common, m| {
|
Box::new(move |_common, m| {
|
||||||
tasks.push(TestbedTask::ShowContextMenu(m.mouse_pos_absolute.unwrap()));
|
tasks.push(TestbedTask::ShowContextMenu(m.boundary.bottom_left()));
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
@@ -254,13 +254,9 @@ impl TestbedGeneric {
|
|||||||
data: &mut Data,
|
data: &mut Data,
|
||||||
position: Vec2,
|
position: Vec2,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
data.context_menu.open(&mut context_menu::OpenParams {
|
data.context_menu.open(context_menu::OpenParams {
|
||||||
globals: &self.globals,
|
|
||||||
layout: &mut self.layout,
|
|
||||||
position,
|
position,
|
||||||
on_action: Rc::new(move |action| {
|
data: context_menu::Blueprint {
|
||||||
log::info!("got action: {}", action.name);
|
|
||||||
}),
|
|
||||||
cells: vec![
|
cells: vec![
|
||||||
context_menu::Cell {
|
context_menu::Cell {
|
||||||
title: Translation::from_raw_text("Options"),
|
title: Translation::from_raw_text("Options"),
|
||||||
@@ -275,7 +271,8 @@ impl TestbedGeneric {
|
|||||||
action_name: "restart".into(),
|
action_name: "restart".into(),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})?;
|
},
|
||||||
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -297,6 +294,11 @@ impl Testbed for TestbedGeneric {
|
|||||||
self.process_task(&task, &mut params, &mut data)?;
|
self.process_task(&task, &mut params, &mut data)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let res = data.context_menu.tick(&mut self.layout)?;
|
||||||
|
if let Some(action_name) = res.action_name {
|
||||||
|
log::info!("got action: {}", action_name);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ impl Default for Params<'_> {
|
|||||||
|
|
||||||
pub struct ButtonClickEvent {
|
pub struct ButtonClickEvent {
|
||||||
pub mouse_pos_absolute: Option<Vec2>,
|
pub mouse_pos_absolute: Option<Vec2>,
|
||||||
|
pub boundary: Boundary,
|
||||||
}
|
}
|
||||||
pub type ButtonClickCallback = Box<dyn Fn(&mut CallbackDataCommon, ButtonClickEvent) -> anyhow::Result<()>>;
|
pub type ButtonClickCallback = Box<dyn Fn(&mut CallbackDataCommon, ButtonClickEvent) -> anyhow::Result<()>>;
|
||||||
|
|
||||||
@@ -388,6 +389,7 @@ fn register_event_mouse_release(
|
|||||||
common,
|
common,
|
||||||
ButtonClickEvent {
|
ButtonClickEvent {
|
||||||
mouse_pos_absolute: event_data.metadata.get_mouse_pos_absolute(),
|
mouse_pos_absolute: event_data.metadata.get_mouse_pos_absolute(),
|
||||||
|
boundary: event_data.widget_data.cached_absolute_boundary,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,41 @@ impl Boundary {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const fn bottom_left(&self) -> Vec2 {
|
||||||
|
Vec2::new(self.pos.x, self.pos.y + self.size.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn bottom_right(&self) -> Vec2 {
|
||||||
|
Vec2::new(self.pos.x + self.size.x, self.pos.y + self.size.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn top_right(&self) -> Vec2 {
|
||||||
|
Vec2::new(self.pos.x + self.size.x, self.pos.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn center(&self) -> Vec2 {
|
||||||
|
Vec2::new(self.pos.x + self.size.x / 2.0, self.pos.y + self.size.y / 2.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn width(&self) -> f32 {
|
||||||
|
self.size.x
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn height(&self) -> f32 {
|
||||||
|
self.size.y
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn area(&self) -> f32 {
|
||||||
|
self.size.x * self.size.y
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn contains_point(&self, point: Vec2) -> bool {
|
||||||
|
point.x >= self.pos.x
|
||||||
|
&& point.x <= self.pos.x + self.size.x
|
||||||
|
&& point.y >= self.pos.y
|
||||||
|
&& point.y <= self.pos.y + self.size.y
|
||||||
|
}
|
||||||
|
|
||||||
pub const fn top(&self) -> f32 {
|
pub const fn top(&self) -> f32 {
|
||||||
self.pos.y
|
self.pos.y
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use crate::{
|
|||||||
i18n::Translation,
|
i18n::Translation,
|
||||||
layout::Layout,
|
layout::Layout,
|
||||||
parser::{self, Fetchable},
|
parser::{self, Fetchable},
|
||||||
|
task::Tasks,
|
||||||
windowing::window::{WguiWindow, WguiWindowParams, WguiWindowParamsExtra},
|
windowing::window::{WguiWindow, WguiWindowParams, WguiWindowParamsExtra},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -18,37 +19,60 @@ pub struct Cell {
|
|||||||
pub action_name: Rc<str>,
|
pub action_name: Rc<str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Blueprint {
|
||||||
|
pub cells: Vec<Cell>,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct ContextMenuAction<'a> {
|
pub struct ContextMenuAction<'a> {
|
||||||
pub common: &'a mut CallbackDataCommon<'a>,
|
pub common: &'a mut CallbackDataCommon<'a>,
|
||||||
pub name: Rc<str>, // action name
|
pub name: Rc<str>, // action name
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct OpenParams<'a> {
|
pub struct OpenParams {
|
||||||
pub position: Vec2,
|
pub position: Vec2,
|
||||||
pub globals: &'a WguiGlobals,
|
pub data: Blueprint,
|
||||||
pub layout: &'a mut Layout,
|
}
|
||||||
pub on_action: Rc<dyn Fn(ContextMenuAction)>,
|
|
||||||
pub cells: Vec<Cell>,
|
#[derive(Clone)]
|
||||||
|
enum Task {
|
||||||
|
ActionClicked(Rc<str>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct ContextMenu {
|
pub struct ContextMenu {
|
||||||
window: WguiWindow,
|
window: WguiWindow,
|
||||||
|
pending_open: Option<OpenParams>,
|
||||||
|
tasks: Tasks<Task>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn doc_params<'a>(globals: WguiGlobals) -> parser::ParseDocumentParams<'a> {
|
fn doc_params<'a>(globals: &WguiGlobals) -> parser::ParseDocumentParams<'a> {
|
||||||
parser::ParseDocumentParams {
|
parser::ParseDocumentParams {
|
||||||
globals,
|
globals: globals.clone(),
|
||||||
path: AssetPath::WguiInternal("wgui/context_menu.xml"),
|
path: AssetPath::WguiInternal("wgui/context_menu.xml"),
|
||||||
extra: Default::default(),
|
extra: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct TickResult {
|
||||||
|
pub action_name: Option<Rc<str>>,
|
||||||
|
}
|
||||||
|
|
||||||
impl ContextMenu {
|
impl ContextMenu {
|
||||||
pub fn open(&mut self, params: &mut OpenParams) -> anyhow::Result<()> {
|
pub fn open(&mut self, params: OpenParams) {
|
||||||
|
self.pending_open = Some(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn close(&self) {
|
||||||
|
self.window.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn open_process(&mut self, params: &OpenParams, layout: &mut Layout) -> anyhow::Result<()> {
|
||||||
|
let globals = layout.state.globals.clone();
|
||||||
|
|
||||||
self.window.open(&mut WguiWindowParams {
|
self.window.open(&mut WguiWindowParams {
|
||||||
globals: params.globals,
|
globals: &globals,
|
||||||
layout: params.layout,
|
layout,
|
||||||
position: params.position,
|
position: params.position,
|
||||||
extra: WguiWindowParamsExtra {
|
extra: WguiWindowParamsExtra {
|
||||||
with_decorations: false,
|
with_decorations: false,
|
||||||
@@ -59,46 +83,25 @@ impl ContextMenu {
|
|||||||
|
|
||||||
let content = self.window.get_content();
|
let content = self.window.get_content();
|
||||||
|
|
||||||
let mut state = parser::parse_from_assets(&doc_params(params.globals.clone()), params.layout, content.id)?;
|
let mut state = parser::parse_from_assets(&doc_params(&globals), layout, content.id)?;
|
||||||
|
|
||||||
let id_buttons = state.get_widget_id("buttons")?;
|
let id_buttons = state.get_widget_id("buttons")?;
|
||||||
|
|
||||||
for (idx, cell) in params.cells.iter().enumerate() {
|
for (idx, cell) in params.data.cells.iter().enumerate() {
|
||||||
let mut par = HashMap::new();
|
let mut par = HashMap::new();
|
||||||
par.insert(Rc::from("text"), cell.title.generate(&mut params.globals.i18n()));
|
par.insert(Rc::from("text"), cell.title.generate(&mut globals.i18n()));
|
||||||
let data_cell = state.parse_template(
|
let data_cell = state.parse_template(&doc_params(&globals), "Cell", layout, id_buttons, par)?;
|
||||||
&doc_params(params.globals.clone()),
|
|
||||||
"Cell",
|
|
||||||
params.layout,
|
|
||||||
id_buttons,
|
|
||||||
par,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let button = data_cell.fetch_component_as::<ComponentButton>("button")?;
|
let button = data_cell.fetch_component_as::<ComponentButton>("button")?;
|
||||||
button.on_click({
|
self
|
||||||
let on_action = params.on_action.clone();
|
.tasks
|
||||||
let name = cell.action_name.clone();
|
.handle_button(&button, Task::ActionClicked(cell.action_name.clone()));
|
||||||
let window = self.window.clone();
|
|
||||||
Box::new(move |common, _| {
|
|
||||||
(*on_action)(ContextMenuAction {
|
|
||||||
name: name.clone(),
|
|
||||||
// FIXME: why i can't just provide this as-is!?
|
|
||||||
/* common: common, */
|
|
||||||
common: &mut CallbackDataCommon {
|
|
||||||
alterables: common.alterables,
|
|
||||||
state: common.state,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
window.close();
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
if idx < params.cells.len() - 1 {
|
if idx < params.data.cells.len() - 1 {
|
||||||
state.parse_template(
|
state.parse_template(
|
||||||
&doc_params(params.globals.clone()),
|
&doc_params(&globals),
|
||||||
"Separator",
|
"Separator",
|
||||||
params.layout,
|
layout,
|
||||||
id_buttons,
|
id_buttons,
|
||||||
Default::default(),
|
Default::default(),
|
||||||
)?;
|
)?;
|
||||||
@@ -108,7 +111,22 @@ impl ContextMenu {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn close(&self) {
|
pub fn tick(&mut self, layout: &mut Layout) -> anyhow::Result<TickResult> {
|
||||||
self.window.close();
|
if let Some(p) = self.pending_open.take() {
|
||||||
|
self.open_process(&p, layout)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut result = TickResult::default();
|
||||||
|
|
||||||
|
for task in self.tasks.drain() {
|
||||||
|
match task {
|
||||||
|
Task::ActionClicked(action_name) => {
|
||||||
|
result.action_name = Some(action_name);
|
||||||
|
self.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user