wgui: WguiWindow, open/close
This commit is contained in:
@@ -18,8 +18,8 @@ use wgui::{
|
||||
i18n::Translation,
|
||||
layout::{Layout, LayoutParams, RcLayout, Widget},
|
||||
parser::{Fetchable, ParseDocumentExtra, ParseDocumentParams, ParserState},
|
||||
taffy::{self, prelude::length},
|
||||
widget::{div::WidgetDiv, label::WidgetLabel, rectangle::WidgetRectangle},
|
||||
widget::{label::WidgetLabel, rectangle::WidgetRectangle},
|
||||
windowing::{WguiWindow, WguiWindowParams},
|
||||
};
|
||||
|
||||
pub enum TestbedTask {
|
||||
@@ -30,12 +30,15 @@ struct Data {
|
||||
tasks: VecDeque<TestbedTask>,
|
||||
#[allow(dead_code)]
|
||||
state: ParserState,
|
||||
|
||||
popup_window: WguiWindow,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TestbedGeneric {
|
||||
pub layout: RcLayout,
|
||||
|
||||
globals: WguiGlobals,
|
||||
data: Rc<RefCell<Data>>,
|
||||
}
|
||||
|
||||
@@ -106,7 +109,7 @@ impl TestbedGeneric {
|
||||
let (layout, state) = wgui::parser::new_layout_from_assets(
|
||||
listeners,
|
||||
&ParseDocumentParams {
|
||||
globals,
|
||||
globals: globals.clone(),
|
||||
path: XML_PATH,
|
||||
extra,
|
||||
},
|
||||
@@ -148,9 +151,11 @@ impl TestbedGeneric {
|
||||
|
||||
let testbed = Self {
|
||||
layout: layout.as_rc(),
|
||||
globals: globals.clone(),
|
||||
data: Rc::new(RefCell::new(Data {
|
||||
state,
|
||||
tasks: Default::default(),
|
||||
popup_window: WguiWindow::default(),
|
||||
})),
|
||||
};
|
||||
|
||||
@@ -187,51 +192,14 @@ impl TestbedGeneric {
|
||||
&mut self,
|
||||
params: &mut TestbedUpdateParams,
|
||||
layout: &mut Layout,
|
||||
_data: &mut Data,
|
||||
data: &mut Data,
|
||||
) -> anyhow::Result<()> {
|
||||
const XML_PATH: AssetPath = AssetPath::WguiInternal("wgui/window_frame.xml");
|
||||
|
||||
let globals = WguiGlobals::new(
|
||||
Box::new(assets::Asset {}),
|
||||
wgui::globals::Defaults::default(),
|
||||
)?;
|
||||
|
||||
let (widget, _) = layout.add_topmost_child(
|
||||
WidgetDiv::create(),
|
||||
taffy::Style {
|
||||
position: taffy::Position::Absolute,
|
||||
margin: taffy::Rect {
|
||||
left: length(64.0),
|
||||
right: length(0.0),
|
||||
top: length(64.0),
|
||||
bottom: length(0.0),
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
)?;
|
||||
|
||||
let state = wgui::parser::parse_from_assets(
|
||||
&ParseDocumentParams {
|
||||
globals,
|
||||
path: XML_PATH,
|
||||
extra: Default::default(),
|
||||
},
|
||||
data.popup_window.open(WguiWindowParams {
|
||||
globals: self.globals.clone(),
|
||||
position: Vec2::new(128.0, 128.0),
|
||||
layout,
|
||||
params.listeners,
|
||||
widget.id,
|
||||
)?;
|
||||
|
||||
let button = state
|
||||
.fetch_component_as::<ComponentButton>("button")
|
||||
.unwrap();
|
||||
|
||||
button.on_click(Box::new(move |_common, _e| {
|
||||
log::info!("click");
|
||||
Ok(())
|
||||
}));
|
||||
|
||||
// temporary, preserve listeners state
|
||||
Box::leak(Box::new(state));
|
||||
listeners: params.listeners,
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -11,7 +11,9 @@
|
||||
<rectangle width="100%" height="100%" round="4" align_items="center" justify_content="space_between"
|
||||
gradient="vertical" color="#224466" color2="#113355">
|
||||
<label margin_left="8" text="Window title" weight="bold" />
|
||||
<sprite src_internal="wgui/close.svg" width="24" height="24" />
|
||||
<Button id="but_close" border="0" round="0">
|
||||
<sprite src_internal="wgui/close.svg" width="24" height="24" />
|
||||
</Button>
|
||||
</rectangle>
|
||||
|
||||
<!-- content itself -->
|
||||
|
||||
@@ -180,6 +180,8 @@ _Translated by key_
|
||||
|
||||
`round`: **float** (default: 4) | **percent** (0-100%)
|
||||
|
||||
`border`: **float** (default: 2)
|
||||
|
||||
`color`: #FFAABB | #FFAABBCC
|
||||
|
||||
`border_color`: #FFAABB | #FFAABBCC
|
||||
|
||||
@@ -24,6 +24,7 @@ use taffy::{AlignItems, JustifyContent};
|
||||
pub struct Params {
|
||||
pub text: Option<Translation>, // if unset, label will not be populated
|
||||
pub color: Option<drawing::Color>,
|
||||
pub border: f32,
|
||||
pub border_color: Option<drawing::Color>,
|
||||
pub hover_border_color: Option<drawing::Color>,
|
||||
pub hover_color: Option<drawing::Color>,
|
||||
@@ -39,6 +40,7 @@ impl Default for Params {
|
||||
color: None,
|
||||
hover_color: None,
|
||||
border_color: None,
|
||||
border: 2.0,
|
||||
hover_border_color: None,
|
||||
round: WLength::Units(4.0),
|
||||
style: Default::default(),
|
||||
@@ -116,7 +118,6 @@ fn anim_hover(
|
||||
rect.params.color = bgcolor;
|
||||
rect.params.color2 = get_color2(&bgcolor);
|
||||
rect.params.border_color = data.initial_border_color.lerp(&data.initial_hover_border_color, mult);
|
||||
rect.params.border = 2.0;
|
||||
}
|
||||
|
||||
fn anim_hover_create(data: Rc<Data>, state: Rc<RefCell<State>>, widget_id: WidgetID, fade_in: bool) -> Animation {
|
||||
@@ -313,7 +314,7 @@ pub fn construct<U1, U2>(
|
||||
gradient: drawing::GradientMode::Vertical,
|
||||
round: params.round,
|
||||
border_color,
|
||||
border: 2.0,
|
||||
border: params.border,
|
||||
}),
|
||||
style,
|
||||
)?;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::{
|
||||
cell::{RefCell, RefMut},
|
||||
collections::VecDeque,
|
||||
io::Write,
|
||||
rc::{Rc, Weak},
|
||||
};
|
||||
@@ -108,9 +109,28 @@ pub struct LayoutState {
|
||||
pub tree: taffy::tree::TaffyTree<WidgetID>,
|
||||
}
|
||||
|
||||
pub enum LayoutTask {
|
||||
RemoveWidget(WidgetID),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct LayoutTasks(pub Rc<RefCell<VecDeque<LayoutTask>>>);
|
||||
|
||||
impl LayoutTasks {
|
||||
fn new() -> Self {
|
||||
Self(Rc::new(RefCell::new(VecDeque::new())))
|
||||
}
|
||||
|
||||
pub fn push(&self, task: LayoutTask) {
|
||||
self.0.borrow_mut().push_back(task);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Layout {
|
||||
pub state: LayoutState,
|
||||
|
||||
pub tasks: LayoutTasks,
|
||||
|
||||
pub components_to_init: Vec<Component>,
|
||||
pub widgets_to_tick: Vec<WidgetID>,
|
||||
|
||||
@@ -221,6 +241,14 @@ impl Layout {
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_widget_single(&mut self, widget_id: WidgetID, node_id: Option<taffy::NodeId>) {
|
||||
self.state.widgets.remove_single(widget_id);
|
||||
self.state.nodes.remove(widget_id);
|
||||
if let Some(node_id) = node_id {
|
||||
let _ = self.state.tree.remove(node_id);
|
||||
}
|
||||
}
|
||||
|
||||
// removes all children of a specific widget
|
||||
pub fn remove_children(&mut self, widget_id: WidgetID) {
|
||||
let mut ids = Vec::new();
|
||||
@@ -231,12 +259,18 @@ impl Layout {
|
||||
}
|
||||
|
||||
for (widget_id, node_id) in ids {
|
||||
self.state.widgets.remove_single(widget_id);
|
||||
self.state.nodes.remove(widget_id);
|
||||
self.state.tree.remove(node_id).unwrap();
|
||||
self.remove_widget_single(widget_id, Some(node_id));
|
||||
}
|
||||
}
|
||||
|
||||
// remove widget and its children, recursively
|
||||
pub fn remove_widget(&mut self, widget_id: WidgetID) {
|
||||
self.remove_children(widget_id);
|
||||
let node_id = self.state.nodes.get(widget_id);
|
||||
self.remove_widget_single(widget_id, node_id.copied());
|
||||
self.mark_redraw();
|
||||
}
|
||||
|
||||
pub const fn mark_redraw(&mut self) {
|
||||
self.needs_redraw = true;
|
||||
}
|
||||
@@ -468,6 +502,7 @@ impl Layout {
|
||||
animations: Animations::default(),
|
||||
components_to_init: Vec::new(),
|
||||
widgets_to_tick: Vec::new(),
|
||||
tasks: LayoutTasks::new(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -541,7 +576,21 @@ impl Layout {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process_tasks(&mut self) {
|
||||
let tasks = self.tasks.clone();
|
||||
let mut tasks = tasks.0.borrow_mut();
|
||||
while let Some(task) = tasks.pop_front() {
|
||||
match task {
|
||||
LayoutTask::RemoveWidget(widget_id) => {
|
||||
self.remove_widget(widget_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process_alterables(&mut self, alterables: EventAlterables) -> anyhow::Result<()> {
|
||||
self.process_tasks();
|
||||
|
||||
for node in alterables.dirty_nodes {
|
||||
self.state.tree.mark_dirty(node)?;
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ pub mod parser;
|
||||
pub mod renderer_vk;
|
||||
pub mod stack;
|
||||
pub mod widget;
|
||||
pub mod windowing;
|
||||
|
||||
// re-exported libs
|
||||
pub use cosmic_text;
|
||||
|
||||
@@ -4,7 +4,7 @@ use crate::{
|
||||
i18n::Translation,
|
||||
layout::WidgetID,
|
||||
parser::{
|
||||
AttribPair, ParserContext, ParserFile, parse_children, process_component,
|
||||
AttribPair, ParserContext, ParserFile, parse_check_f32, parse_children, process_component,
|
||||
style::{parse_color_opt, parse_round, parse_style, parse_text_style},
|
||||
},
|
||||
widget::util::WLength,
|
||||
@@ -18,10 +18,12 @@ pub fn parse_component_button<'a, U1, U2>(
|
||||
attribs: &[AttribPair],
|
||||
) -> anyhow::Result<WidgetID> {
|
||||
let mut color: Option<Color> = None;
|
||||
let mut border = 2.0;
|
||||
let mut border_color: Option<Color> = None;
|
||||
let mut hover_color: Option<Color> = None;
|
||||
let mut hover_border_color: Option<Color> = None;
|
||||
let mut round = WLength::Units(4.0);
|
||||
|
||||
let mut translation: Option<Translation> = None;
|
||||
|
||||
let text_style = parse_text_style(attribs);
|
||||
@@ -42,6 +44,9 @@ pub fn parse_component_button<'a, U1, U2>(
|
||||
"color" => {
|
||||
parse_color_opt(value, &mut color);
|
||||
}
|
||||
"border" => {
|
||||
parse_check_f32(value, &mut border);
|
||||
}
|
||||
"border_color" => {
|
||||
parse_color_opt(value, &mut border_color);
|
||||
}
|
||||
@@ -64,6 +69,7 @@ pub fn parse_component_button<'a, U1, U2>(
|
||||
parent_id,
|
||||
button::Params {
|
||||
color,
|
||||
border,
|
||||
border_color,
|
||||
hover_border_color,
|
||||
hover_color,
|
||||
|
||||
110
wgui/src/windowing.rs
Normal file
110
wgui/src/windowing.rs
Normal file
@@ -0,0 +1,110 @@
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use glam::Vec2;
|
||||
use taffy::prelude::length;
|
||||
|
||||
use crate::{
|
||||
assets::AssetPath,
|
||||
components::button::ComponentButton,
|
||||
event::EventListenerCollection,
|
||||
globals::WguiGlobals,
|
||||
layout::{Layout, LayoutTask, LayoutTasks, WidgetPair},
|
||||
parser::{self, Fetchable, ParserState},
|
||||
widget::div::WidgetDiv,
|
||||
};
|
||||
|
||||
struct OpenedWindow {
|
||||
layout_tasks: LayoutTasks,
|
||||
widget: WidgetPair,
|
||||
|
||||
#[allow(dead_code)]
|
||||
state: ParserState,
|
||||
}
|
||||
|
||||
impl Drop for OpenedWindow {
|
||||
fn drop(&mut self) {
|
||||
self.layout_tasks.push(LayoutTask::RemoveWidget(self.widget.id));
|
||||
}
|
||||
}
|
||||
|
||||
struct State {
|
||||
opened_window: Option<OpenedWindow>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct WguiWindow(Rc<RefCell<State>>);
|
||||
|
||||
pub struct WguiWindowParams<'a> {
|
||||
pub position: Vec2,
|
||||
pub globals: WguiGlobals,
|
||||
pub layout: &'a mut Layout,
|
||||
pub listeners: &'a mut EventListenerCollection<(), ()>,
|
||||
}
|
||||
|
||||
impl Default for WguiWindow {
|
||||
fn default() -> Self {
|
||||
Self(Rc::new(RefCell::new(State { opened_window: None })))
|
||||
}
|
||||
}
|
||||
|
||||
impl WguiWindow {
|
||||
pub fn close(&self) {
|
||||
self.0.borrow_mut().opened_window = None;
|
||||
}
|
||||
|
||||
pub fn open(&mut self, params: WguiWindowParams) -> anyhow::Result<()> {
|
||||
// close previous one if it's already open
|
||||
self.close();
|
||||
|
||||
const XML_PATH: AssetPath = AssetPath::WguiInternal("wgui/window_frame.xml");
|
||||
|
||||
let (widget, _) = params.layout.add_topmost_child(
|
||||
WidgetDiv::create(),
|
||||
taffy::Style {
|
||||
position: taffy::Position::Absolute,
|
||||
margin: taffy::Rect {
|
||||
left: length(params.position.x),
|
||||
right: length(0.0),
|
||||
top: length(params.position.y),
|
||||
bottom: length(0.0),
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
)?;
|
||||
|
||||
let state = parser::parse_from_assets(
|
||||
&parser::ParseDocumentParams {
|
||||
globals: params.globals,
|
||||
path: XML_PATH,
|
||||
extra: Default::default(),
|
||||
},
|
||||
params.layout,
|
||||
params.listeners,
|
||||
widget.id,
|
||||
)?;
|
||||
|
||||
let but_close = state.fetch_component_as::<ComponentButton>("but_close").unwrap();
|
||||
but_close.on_click({
|
||||
let this = self.clone();
|
||||
Box::new(move |_common, _e| {
|
||||
this.close();
|
||||
Ok(())
|
||||
})
|
||||
});
|
||||
|
||||
let button = state.fetch_component_as::<ComponentButton>("button").unwrap();
|
||||
|
||||
button.on_click(Box::new(move |_common, _e| {
|
||||
log::info!("click");
|
||||
Ok(())
|
||||
}));
|
||||
|
||||
self.0.borrow_mut().opened_window = Some(OpenedWindow {
|
||||
widget,
|
||||
state,
|
||||
layout_tasks: params.layout.tasks.clone(),
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user