wgui: Separate user and wgui assets, topmost widgets (poc)
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -6285,6 +6285,7 @@ dependencies = [
|
||||
"regex",
|
||||
"resvg",
|
||||
"roxmltree 0.20.0",
|
||||
"rust-embed",
|
||||
"rustc-hash 2.1.1",
|
||||
"serde_json",
|
||||
"slotmap",
|
||||
|
||||
@@ -3,6 +3,7 @@ use std::{cell::RefCell, collections::VecDeque, rc::Rc};
|
||||
use chrono::Timelike;
|
||||
use glam::Vec2;
|
||||
use wgui::{
|
||||
assets::AssetPath,
|
||||
components::button::ComponentButton,
|
||||
event::{CallbackDataCommon, EventAlterables, EventListenerCollection},
|
||||
globals::WguiGlobals,
|
||||
@@ -56,7 +57,7 @@ impl Frontend {
|
||||
params.listeners,
|
||||
&ParseDocumentParams {
|
||||
globals: globals.clone(),
|
||||
path: "gui/dashboard.xml",
|
||||
path: AssetPath::BuiltIn("gui/dashboard.xml"),
|
||||
extra: Default::default(),
|
||||
},
|
||||
&LayoutParams { resize_to_parent: true },
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::{collections::HashMap, rc::Rc};
|
||||
|
||||
use wgui::{
|
||||
assets::AssetPath,
|
||||
components::button::ComponentButton,
|
||||
layout::WidgetPair,
|
||||
parser::{Fetchable, ParseDocumentParams, ParserData, ParserState},
|
||||
@@ -36,7 +37,7 @@ impl TabApps {
|
||||
pub fn new(mut tab_params: TabParams) -> anyhow::Result<Self> {
|
||||
let doc_params = &ParseDocumentParams {
|
||||
globals: tab_params.globals.clone(),
|
||||
path: "gui/tab/apps.xml",
|
||||
path: AssetPath::BuiltIn("gui/tab/apps.xml"),
|
||||
extra: Default::default(),
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
use wgui::parser::{ParseDocumentParams, ParserState};
|
||||
use wgui::{
|
||||
assets::AssetPath,
|
||||
parser::{ParseDocumentParams, ParserState},
|
||||
};
|
||||
|
||||
use crate::tab::{Tab, TabParams, TabType};
|
||||
|
||||
@@ -18,7 +21,7 @@ impl TabGames {
|
||||
let state = wgui::parser::parse_from_assets(
|
||||
&ParseDocumentParams {
|
||||
globals: params.globals.clone(),
|
||||
path: "gui/tab/games.xml",
|
||||
path: AssetPath::BuiltIn("gui/tab/games.xml"),
|
||||
extra: Default::default(),
|
||||
},
|
||||
params.layout,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use wgui::{
|
||||
assets::AssetPath,
|
||||
components::button::ComponentButton,
|
||||
i18n::Translation,
|
||||
parser::{Fetchable, ParseDocumentParams, ParserState},
|
||||
@@ -38,7 +39,7 @@ impl TabHome {
|
||||
let state = wgui::parser::parse_from_assets(
|
||||
&ParseDocumentParams {
|
||||
globals: params.globals.clone(),
|
||||
path: "gui/tab/home.xml",
|
||||
path: AssetPath::BuiltIn("gui/tab/home.xml"),
|
||||
extra: Default::default(),
|
||||
},
|
||||
params.layout,
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
use wgui::parser::{ParseDocumentParams, ParserState};
|
||||
use wgui::{
|
||||
assets::AssetPath,
|
||||
parser::{ParseDocumentParams, ParserState},
|
||||
};
|
||||
|
||||
use crate::tab::{Tab, TabParams, TabType};
|
||||
|
||||
@@ -18,7 +21,7 @@ impl TabMonado {
|
||||
let state = wgui::parser::parse_from_assets(
|
||||
&ParseDocumentParams {
|
||||
globals: params.globals.clone(),
|
||||
path: "gui/tab/monado.xml",
|
||||
path: AssetPath::BuiltIn("gui/tab/monado.xml"),
|
||||
extra: Default::default(),
|
||||
},
|
||||
params.layout,
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
use wgui::parser::{ParseDocumentParams, ParserState};
|
||||
use wgui::{
|
||||
assets::AssetPath,
|
||||
parser::{ParseDocumentParams, ParserState},
|
||||
};
|
||||
|
||||
use crate::tab::{Tab, TabParams, TabType};
|
||||
|
||||
@@ -18,7 +21,7 @@ impl TabProcesses {
|
||||
let state = wgui::parser::parse_from_assets(
|
||||
&ParseDocumentParams {
|
||||
globals: params.globals.clone(),
|
||||
path: "gui/tab/processes.xml",
|
||||
path: AssetPath::BuiltIn("gui/tab/processes.xml"),
|
||||
extra: Default::default(),
|
||||
},
|
||||
params.layout,
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
use wgui::parser::{ParseDocumentParams, ParserState};
|
||||
use wgui::{
|
||||
assets::AssetPath,
|
||||
parser::{ParseDocumentParams, ParserState},
|
||||
};
|
||||
|
||||
use crate::tab::{Tab, TabParams, TabType};
|
||||
|
||||
@@ -18,7 +21,7 @@ impl TabSettings {
|
||||
let state = wgui::parser::parse_from_assets(
|
||||
&ParseDocumentParams {
|
||||
globals: params.globals.clone(),
|
||||
path: "gui/tab/settings.xml",
|
||||
path: AssetPath::BuiltIn("gui/tab/settings.xml"),
|
||||
extra: Default::default(),
|
||||
},
|
||||
params.layout,
|
||||
|
||||
@@ -2,6 +2,7 @@ use gio::prelude::{AppInfoExt, IconExt};
|
||||
use gtk::traits::IconThemeExt;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)] // TODO: remove this
|
||||
pub struct DesktopEntry {
|
||||
pub exec_path: String,
|
||||
pub exec_args: Vec<String>,
|
||||
@@ -10,6 +11,7 @@ pub struct DesktopEntry {
|
||||
pub categories: Vec<String>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)] // TODO: remove this
|
||||
pub struct EntrySearchCell {
|
||||
pub exec_path: String,
|
||||
pub exec_args: Vec<String>,
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
<layout>
|
||||
<elements>
|
||||
<rectangle
|
||||
color="#888888"
|
||||
width="1000" height="500" min_width="1000" min_height="500"
|
||||
width="1000" height="1000" min_width="1000" min_height="500"
|
||||
gap="4" flex_direction="column"
|
||||
color="#444444ee"
|
||||
overflow_y="scroll">
|
||||
<label text="Raw text" color="#FFFFFF" />
|
||||
<label translation="TESTBED.HELLO_WORLD" color="#FFFFFF" />
|
||||
|
||||
<div margin_left="16" gap="8" flex_direction="column">
|
||||
<label id="label_current_option" text="Click any of these buttons" size="20" weight="bold" />
|
||||
<Button id="button_popup" text="Show pop-up" width="200" height="32" color="#777777" />
|
||||
<div gap="4">
|
||||
<Button id="button_red" text="Red button" width="150" height="32" color="#FF0000" />
|
||||
<Button id="button_aqua" text="Aqua button" width="150" height="32" color="#00FFFF" />
|
||||
|
||||
@@ -309,7 +309,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
log::trace!("drawing frame {frame_index}");
|
||||
frame_index += 1;
|
||||
|
||||
limiter.start(120); // max 120 fps
|
||||
limiter.start(0); // max 120 fps
|
||||
profiler.start();
|
||||
|
||||
{
|
||||
|
||||
@@ -4,6 +4,7 @@ use crate::{
|
||||
};
|
||||
use glam::Vec2;
|
||||
use wgui::{
|
||||
assets::AssetPath,
|
||||
event::EventListenerCollection,
|
||||
globals::WguiGlobals,
|
||||
layout::{LayoutParams, RcLayout},
|
||||
@@ -19,7 +20,7 @@ pub struct TestbedAny {
|
||||
|
||||
impl TestbedAny {
|
||||
pub fn new(name: &str, listeners: &mut EventListenerCollection<(), ()>) -> anyhow::Result<Self> {
|
||||
let path = format!("gui/{name}.xml");
|
||||
let path = AssetPath::BuiltIn(&format!("gui/{name}.xml"));
|
||||
|
||||
let globals = WguiGlobals::new(
|
||||
Box::new(assets::Asset {}),
|
||||
@@ -30,7 +31,7 @@ impl TestbedAny {
|
||||
listeners,
|
||||
&ParseDocumentParams {
|
||||
globals,
|
||||
path: &path,
|
||||
path,
|
||||
extra: Default::default(),
|
||||
},
|
||||
&LayoutParams::default(),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::rc::Rc;
|
||||
use std::{cell::RefCell, collections::VecDeque, rc::Rc};
|
||||
|
||||
use crate::{
|
||||
assets,
|
||||
@@ -6,25 +6,37 @@ use crate::{
|
||||
};
|
||||
use glam::Vec2;
|
||||
use wgui::{
|
||||
assets::AssetPath,
|
||||
components::{
|
||||
Component,
|
||||
button::{ButtonClickCallback, ComponentButton},
|
||||
checkbox::ComponentCheckbox,
|
||||
Component,
|
||||
},
|
||||
drawing::Color,
|
||||
event::EventListenerCollection,
|
||||
globals::WguiGlobals,
|
||||
i18n::Translation,
|
||||
layout::{LayoutParams, RcLayout, Widget},
|
||||
layout::{Layout, LayoutParams, RcLayout, Widget},
|
||||
parser::{Fetchable, ParseDocumentExtra, ParseDocumentParams, ParserState},
|
||||
widget::{label::WidgetLabel, rectangle::WidgetRectangle},
|
||||
taffy::{self, prelude::length},
|
||||
widget::{div::WidgetDiv, label::WidgetLabel, rectangle::WidgetRectangle},
|
||||
};
|
||||
|
||||
pub enum TestbedTask {
|
||||
ShowPopup,
|
||||
}
|
||||
|
||||
struct Data {
|
||||
tasks: VecDeque<TestbedTask>,
|
||||
#[allow(dead_code)]
|
||||
state: ParserState,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TestbedGeneric {
|
||||
pub layout: RcLayout,
|
||||
|
||||
#[allow(dead_code)]
|
||||
state: ParserState,
|
||||
data: Rc<RefCell<Data>>,
|
||||
}
|
||||
|
||||
fn button_click_callback(
|
||||
@@ -57,7 +69,7 @@ fn handle_button_click(button: Rc<ComponentButton>, label: Widget, text: &'stati
|
||||
|
||||
impl TestbedGeneric {
|
||||
pub fn new(listeners: &mut EventListenerCollection<(), ()>) -> anyhow::Result<Self> {
|
||||
const XML_PATH: &str = "gui/various_widgets.xml";
|
||||
const XML_PATH: AssetPath = AssetPath::BuiltIn("gui/various_widgets.xml");
|
||||
|
||||
let globals = WguiGlobals::new(
|
||||
Box::new(assets::Asset {}),
|
||||
@@ -112,6 +124,7 @@ impl TestbedGeneric {
|
||||
Ok(())
|
||||
}));
|
||||
|
||||
let button_popup = state.fetch_component_as::<ComponentButton>("button_popup")?;
|
||||
let button_red = state.fetch_component_as::<ComponentButton>("button_red")?;
|
||||
let button_aqua = state.fetch_component_as::<ComponentButton>("button_aqua")?;
|
||||
let button_yellow = state.fetch_component_as::<ComponentButton>("button_yellow")?;
|
||||
@@ -133,19 +146,102 @@ impl TestbedGeneric {
|
||||
Ok(())
|
||||
}));
|
||||
|
||||
Ok(Self {
|
||||
let testbed = Self {
|
||||
layout: layout.as_rc(),
|
||||
state,
|
||||
})
|
||||
data: Rc::new(RefCell::new(Data {
|
||||
state,
|
||||
tasks: Default::default(),
|
||||
})),
|
||||
};
|
||||
|
||||
button_popup.on_click({
|
||||
let testbed = testbed.clone();
|
||||
Box::new(move |_, _| {
|
||||
testbed.push_task(TestbedTask::ShowPopup);
|
||||
Ok(())
|
||||
})
|
||||
});
|
||||
|
||||
Ok(testbed)
|
||||
}
|
||||
|
||||
fn push_task(&self, task: TestbedTask) {
|
||||
self.data.borrow_mut().tasks.push_back(task);
|
||||
}
|
||||
|
||||
fn process_task(
|
||||
&mut self,
|
||||
task: &TestbedTask,
|
||||
params: &mut TestbedUpdateParams,
|
||||
layout: &mut Layout,
|
||||
data: &mut Data,
|
||||
) -> anyhow::Result<()> {
|
||||
match task {
|
||||
TestbedTask::ShowPopup => self.show_popup(params, layout, data)?,
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn show_popup(
|
||||
&mut self,
|
||||
params: &mut TestbedUpdateParams,
|
||||
layout: &mut Layout,
|
||||
_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(),
|
||||
},
|
||||
layout,
|
||||
params.listeners,
|
||||
widget.id,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Testbed for TestbedGeneric {
|
||||
fn update(&mut self, params: TestbedUpdateParams) -> anyhow::Result<()> {
|
||||
self.layout.borrow_mut().update(
|
||||
fn update(&mut self, mut params: TestbedUpdateParams) -> anyhow::Result<()> {
|
||||
let layout = self.layout.clone();
|
||||
let data = self.data.clone();
|
||||
|
||||
let mut layout = layout.borrow_mut();
|
||||
let mut data = data.borrow_mut();
|
||||
|
||||
layout.update(
|
||||
Vec2::new(params.width, params.height),
|
||||
params.timestep_alpha,
|
||||
)?;
|
||||
|
||||
while let Some(task) = data.tasks.pop_front() {
|
||||
self.process_task(&task, &mut params, &mut layout, &mut data)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -29,3 +29,4 @@ smallvec = "1.15.1"
|
||||
taffy = "0.9.1"
|
||||
vulkano = { workspace = true }
|
||||
vulkano-shaders = { workspace = true }
|
||||
rust-embed = { workspace = true }
|
||||
|
||||
3
wgui/assets/wgui/close.svg
Normal file
3
wgui/assets/wgui/close.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24">
|
||||
<path fill="white" d="M8.4 17L7 15.6l3.6-3.6L7 8.425l1.4-1.4l3.6 3.6l3.575-3.6l1.4 1.4l-3.6 3.575l3.6 3.6l-1.4 1.4L12 13.4z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 220 B |
25
wgui/assets/wgui/window_frame.xml
Normal file
25
wgui/assets/wgui/window_frame.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<layout>
|
||||
<elements>
|
||||
<rectangle
|
||||
flex_direction="column"
|
||||
round="8"
|
||||
border="2"
|
||||
border_color="#778899"
|
||||
color="#001122ee"
|
||||
padding="2">
|
||||
|
||||
<!-- window title -->
|
||||
<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" />
|
||||
</rectangle>
|
||||
|
||||
<!-- content itself -->
|
||||
<div width="100%" height="100%" padding="8" gap="4" flex_direction="column">
|
||||
<label text="Window content" />
|
||||
<Button color="#9911AA" text="I'm clickable." width="128" height="24" />
|
||||
</div>
|
||||
</rectangle>
|
||||
</elements>
|
||||
</layout>
|
||||
@@ -152,7 +152,11 @@ _Internal (assets) image path_
|
||||
|
||||
`src_ext`: **string**
|
||||
|
||||
_External image path_
|
||||
_External (filesystem) image path_
|
||||
|
||||
`src_internal`: **string**
|
||||
|
||||
_wgui internal image path. Do not use directly unless it's related to the core wgui assets._
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,77 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum AssetPath<'a> {
|
||||
WguiInternal(&'a str), // tied to internal wgui AssetProvider. Used internally
|
||||
BuiltIn(&'a str), // tied to user AssetProvider
|
||||
Filesystem(&'a str), // tied to filesystem path
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum AssetPathOwned {
|
||||
WguiInternal(PathBuf),
|
||||
BuiltIn(PathBuf),
|
||||
Filesystem(PathBuf),
|
||||
}
|
||||
|
||||
impl AssetPath<'_> {
|
||||
pub const fn get_str(&self) -> &str {
|
||||
match &self {
|
||||
AssetPath::WguiInternal(path) => path,
|
||||
AssetPath::BuiltIn(path) => path,
|
||||
AssetPath::Filesystem(path) => path,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_owned(&self) -> AssetPathOwned {
|
||||
match self {
|
||||
AssetPath::WguiInternal(path) => AssetPathOwned::WguiInternal(PathBuf::from(path)),
|
||||
AssetPath::BuiltIn(path) => AssetPathOwned::BuiltIn(PathBuf::from(path)),
|
||||
AssetPath::Filesystem(path) => AssetPathOwned::Filesystem(PathBuf::from(path)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AssetPathOwned {
|
||||
pub fn as_ref(&'_ self) -> AssetPath<'_> {
|
||||
match self {
|
||||
AssetPathOwned::WguiInternal(buf) => AssetPath::WguiInternal(buf.to_str().unwrap()),
|
||||
AssetPathOwned::BuiltIn(buf) => AssetPath::BuiltIn(buf.to_str().unwrap()),
|
||||
AssetPathOwned::Filesystem(buf) => AssetPath::Filesystem(buf.to_str().unwrap()),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn get_path_buf(&self) -> &PathBuf {
|
||||
match self {
|
||||
AssetPathOwned::WguiInternal(buf) => buf,
|
||||
AssetPathOwned::BuiltIn(buf) => buf,
|
||||
AssetPathOwned::Filesystem(buf) => buf,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AssetPathOwned {
|
||||
#[must_use]
|
||||
pub fn push_include(&self, include: &str) -> AssetPathOwned {
|
||||
let buf = self.get_path_buf();
|
||||
let mut new_path = buf.parent().unwrap_or_else(|| Path::new("/")).to_path_buf();
|
||||
new_path.push(include);
|
||||
let new_path = normalize_path(&new_path);
|
||||
|
||||
match self {
|
||||
AssetPathOwned::WguiInternal(_) => AssetPathOwned::WguiInternal(new_path),
|
||||
AssetPathOwned::BuiltIn(_) => AssetPathOwned::BuiltIn(new_path),
|
||||
AssetPathOwned::Filesystem(_) => AssetPathOwned::Filesystem(new_path),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AssetPathOwned {
|
||||
fn default() -> Self {
|
||||
Self::WguiInternal(PathBuf::default())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AssetProvider {
|
||||
fn load_from_path(&mut self, path: &str) -> anyhow::Result<Vec<u8>>;
|
||||
}
|
||||
|
||||
12
wgui/src/assets_internal.rs
Normal file
12
wgui/src/assets_internal.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
#[derive(rust_embed::Embed)]
|
||||
#[folder = "assets/"]
|
||||
pub struct AssetInternal;
|
||||
|
||||
impl crate::assets::AssetProvider for AssetInternal {
|
||||
fn load_from_path(&mut self, path: &str) -> anyhow::Result<Vec<u8>> {
|
||||
match AssetInternal::get(path) {
|
||||
Some(data) => Ok(data.data.to_vec()),
|
||||
None => anyhow::bail!("internal file {path} not found"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -146,6 +146,7 @@ pub struct PrimitiveExtent {
|
||||
}
|
||||
|
||||
pub enum RenderPrimitive {
|
||||
NewPass,
|
||||
Rectangle(PrimitiveExtent, Rectangle),
|
||||
Text(PrimitiveExtent, Rc<RefCell<Buffer>>, Option<TextShadow>),
|
||||
Sprite(PrimitiveExtent, Option<CustomGlyph>), //option because we want as_slice
|
||||
@@ -276,7 +277,7 @@ fn draw_widget(
|
||||
|
||||
widget_state.draw_all(state, &draw_params);
|
||||
|
||||
draw_children(params, state, node_id);
|
||||
draw_children(params, state, node_id, false);
|
||||
|
||||
if scissor_pushed {
|
||||
state.scissor_stack.pop();
|
||||
@@ -296,7 +297,7 @@ fn draw_widget(
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_children(params: &DrawParams, state: &mut DrawState, parent_node_id: taffy::NodeId) {
|
||||
fn draw_children(params: &DrawParams, state: &mut DrawState, parent_node_id: taffy::NodeId, is_topmost: bool) {
|
||||
let layout = ¶ms.layout;
|
||||
|
||||
for node_id in layout.state.tree.child_ids(parent_node_id) {
|
||||
@@ -316,6 +317,10 @@ fn draw_children(params: &DrawParams, state: &mut DrawState, parent_node_id: taf
|
||||
};
|
||||
|
||||
draw_widget(params, state, node_id, style, widget);
|
||||
|
||||
if is_topmost {
|
||||
state.primitives.push(RenderPrimitive::NewPass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,14 +329,6 @@ pub fn draw(params: &mut DrawParams) -> anyhow::Result<Vec<RenderPrimitive>> {
|
||||
let mut transform_stack = TransformStack::new();
|
||||
let mut scissor_stack = ScissorStack::new();
|
||||
|
||||
let Some(root_widget) = params.layout.state.widgets.get(params.layout.root_widget) else {
|
||||
panic!();
|
||||
};
|
||||
|
||||
let Ok(style) = params.layout.state.tree.style(params.layout.root_node) else {
|
||||
panic!();
|
||||
};
|
||||
|
||||
let mut alterables = EventAlterables::default();
|
||||
|
||||
let mut state = DrawState {
|
||||
@@ -342,7 +339,8 @@ pub fn draw(params: &mut DrawParams) -> anyhow::Result<Vec<RenderPrimitive>> {
|
||||
alterables: &mut alterables,
|
||||
};
|
||||
|
||||
draw_widget(params, &mut state, params.layout.root_node, style, root_widget);
|
||||
draw_children(params, &mut state, params.layout.tree_root_node, true);
|
||||
|
||||
params.layout.process_alterables(alterables)?;
|
||||
|
||||
Ok(primitives)
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
use std::{marker::PhantomData, ops::Range, sync::Arc};
|
||||
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
use vulkano::{
|
||||
buffer::{
|
||||
allocator::{SubbufferAllocator, SubbufferAllocatorCreateInfo},
|
||||
BufferContents, BufferUsage, Subbuffer,
|
||||
allocator::{SubbufferAllocator, SubbufferAllocatorCreateInfo},
|
||||
},
|
||||
descriptor_set::{
|
||||
layout::{DescriptorBindingFlags, DescriptorSetLayoutCreateFlags},
|
||||
DescriptorSet, WriteDescriptorSet,
|
||||
layout::{DescriptorBindingFlags, DescriptorSetLayoutCreateFlags},
|
||||
},
|
||||
format::Format,
|
||||
image::{
|
||||
@@ -17,8 +17,9 @@ use vulkano::{
|
||||
},
|
||||
memory::allocator::MemoryTypeFilter,
|
||||
pipeline::{
|
||||
DynamicState, GraphicsPipeline, Pipeline, PipelineLayout,
|
||||
graphics::{
|
||||
self,
|
||||
self, GraphicsPipelineCreateInfo,
|
||||
color_blend::{AttachmentBlend, ColorBlendAttachmentState, ColorBlendState},
|
||||
input_assembly::{InputAssemblyState, PrimitiveTopology},
|
||||
multisample::MultisampleState,
|
||||
@@ -26,15 +27,13 @@ use vulkano::{
|
||||
subpass::PipelineRenderingCreateInfo,
|
||||
vertex_input::{Vertex, VertexDefinition, VertexInputState},
|
||||
viewport::ViewportState,
|
||||
GraphicsPipelineCreateInfo,
|
||||
},
|
||||
layout::PipelineDescriptorSetLayoutCreateInfo,
|
||||
DynamicState, GraphicsPipeline, Pipeline, PipelineLayout,
|
||||
},
|
||||
shader::{EntryPoint, ShaderModule},
|
||||
};
|
||||
|
||||
use super::{pass::WGfxPass, WGfx};
|
||||
use super::{WGfx, pass::WGfxPass};
|
||||
|
||||
pub struct WGfxPipeline<V> {
|
||||
pub graphics: Arc<WGfx>,
|
||||
@@ -47,6 +46,7 @@ impl<V> WGfxPipeline<V>
|
||||
where
|
||||
V: Sized,
|
||||
{
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn new_from_stages(
|
||||
graphics: Arc<WGfx>,
|
||||
format: Format,
|
||||
@@ -207,21 +207,25 @@ impl WPipelineCreateInfo {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn use_blend(mut self, blend: AttachmentBlend) -> Self {
|
||||
#[must_use]
|
||||
pub const fn use_blend(mut self, blend: AttachmentBlend) -> Self {
|
||||
self.blend = Some(blend);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn use_topology(mut self, topology: PrimitiveTopology) -> Self {
|
||||
#[must_use]
|
||||
pub const fn use_topology(mut self, topology: PrimitiveTopology) -> Self {
|
||||
self.topology = topology;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn use_instanced(mut self) -> Self {
|
||||
#[must_use]
|
||||
pub const fn use_instanced(mut self) -> Self {
|
||||
self.instanced = true;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn use_updatable_descriptors(mut self, updatable_sets: SmallVec<[usize; 8]>) -> Self {
|
||||
self.updatable_sets = updatable_sets;
|
||||
self
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
use std::{
|
||||
cell::{RefCell, RefMut},
|
||||
io::Read,
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
use crate::{assets::AssetProvider, drawing, i18n::I18n};
|
||||
use crate::{
|
||||
assets::{AssetPath, AssetProvider},
|
||||
assets_internal, drawing,
|
||||
i18n::I18n,
|
||||
};
|
||||
|
||||
pub struct Defaults {
|
||||
pub dark_mode: bool,
|
||||
@@ -22,8 +27,9 @@ impl Default for Defaults {
|
||||
}
|
||||
|
||||
pub struct Globals {
|
||||
pub assets: Box<dyn AssetProvider>,
|
||||
pub i18n: I18n,
|
||||
pub assets_internal: Box<dyn AssetProvider>,
|
||||
pub assets_builtin: Box<dyn AssetProvider>,
|
||||
pub i18n_builtin: I18n,
|
||||
pub defaults: Defaults,
|
||||
}
|
||||
|
||||
@@ -31,10 +37,33 @@ pub struct Globals {
|
||||
pub struct WguiGlobals(Rc<RefCell<Globals>>);
|
||||
|
||||
impl WguiGlobals {
|
||||
pub fn new(mut assets: Box<dyn AssetProvider>, defaults: Defaults) -> anyhow::Result<Self> {
|
||||
let i18n = I18n::new(&mut assets)?;
|
||||
pub fn new(mut assets_builtin: Box<dyn AssetProvider>, defaults: Defaults) -> anyhow::Result<Self> {
|
||||
let i18n_builtin = I18n::new(&mut assets_builtin)?;
|
||||
let assets_internal = Box::new(assets_internal::AssetInternal {});
|
||||
|
||||
Ok(Self(Rc::new(RefCell::new(Globals { assets, i18n, defaults }))))
|
||||
Ok(Self(Rc::new(RefCell::new(Globals {
|
||||
assets_internal,
|
||||
assets_builtin,
|
||||
i18n_builtin,
|
||||
defaults,
|
||||
}))))
|
||||
}
|
||||
|
||||
pub fn get_asset(&self, asset_path: AssetPath) -> anyhow::Result<Vec<u8>> {
|
||||
match asset_path {
|
||||
AssetPath::WguiInternal(path) => self.assets_internal().load_from_path(path),
|
||||
AssetPath::BuiltIn(path) => self.assets_builtin().load_from_path(path),
|
||||
AssetPath::Filesystem(path) => {
|
||||
let mut file = std::fs::File::open(path)?;
|
||||
/* 16 MiB safeguard */
|
||||
if file.metadata()?.len() > 16 * 1024 * 1024 {
|
||||
anyhow::bail!("Too large file size");
|
||||
}
|
||||
let mut data = Vec::new();
|
||||
file.read_to_end(&mut data)?;
|
||||
Ok(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self) -> RefMut<'_, Globals> {
|
||||
@@ -42,10 +71,14 @@ impl WguiGlobals {
|
||||
}
|
||||
|
||||
pub fn i18n(&self) -> RefMut<'_, I18n> {
|
||||
RefMut::map(self.0.borrow_mut(), |x| &mut x.i18n)
|
||||
RefMut::map(self.0.borrow_mut(), |x| &mut x.i18n_builtin)
|
||||
}
|
||||
|
||||
pub fn assets(&self) -> RefMut<'_, Box<dyn AssetProvider>> {
|
||||
RefMut::map(self.0.borrow_mut(), |x| &mut x.assets)
|
||||
pub fn assets_internal(&self) -> RefMut<'_, Box<dyn AssetProvider>> {
|
||||
RefMut::map(self.0.borrow_mut(), |x| &mut x.assets_internal)
|
||||
}
|
||||
|
||||
pub fn assets_builtin(&self) -> RefMut<'_, Box<dyn AssetProvider>> {
|
||||
RefMut::map(self.0.borrow_mut(), |x| &mut x.assets_builtin)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,14 +6,14 @@ use std::{
|
||||
use crate::{
|
||||
animation::Animations,
|
||||
components::{Component, InitData},
|
||||
drawing::{push_scissor_stack, push_transform_stack, Boundary},
|
||||
drawing::{Boundary, push_scissor_stack, push_transform_stack},
|
||||
event::{self, CallbackDataCommon, EventAlterables, EventListenerCollection},
|
||||
globals::WguiGlobals,
|
||||
widget::{self, div::WidgetDiv, EventParams, WidgetObj, WidgetState},
|
||||
widget::{self, EventParams, WidgetObj, WidgetState, div::WidgetDiv},
|
||||
};
|
||||
|
||||
use glam::{vec2, Vec2};
|
||||
use slotmap::{new_key_type, HopSlotMap, SecondaryMap};
|
||||
use glam::{Vec2, vec2};
|
||||
use slotmap::{HopSlotMap, SecondaryMap, new_key_type};
|
||||
use taffy::{NodeId, TaffyTree, TraversePartialTree};
|
||||
|
||||
new_key_type! {
|
||||
@@ -113,8 +113,15 @@ pub struct Layout {
|
||||
pub components_to_init: Vec<Component>,
|
||||
pub widgets_to_tick: Vec<WidgetID>,
|
||||
|
||||
pub root_widget: WidgetID,
|
||||
pub root_node: taffy::NodeId,
|
||||
// *Main root*
|
||||
// contains content_root_widget and topmost widgets
|
||||
pub tree_root_widget: WidgetID,
|
||||
pub tree_root_node: taffy::NodeId,
|
||||
|
||||
// *Main topmost widget*
|
||||
// main topmost widget, always present, parent of `tree_root_widget`
|
||||
pub content_root_widget: WidgetID,
|
||||
pub content_root_node: taffy::NodeId,
|
||||
|
||||
pub prev_size: Vec2,
|
||||
pub content_size: Vec2,
|
||||
@@ -165,6 +172,22 @@ impl Layout {
|
||||
Rc::new(RefCell::new(self))
|
||||
}
|
||||
|
||||
pub fn add_topmost_child(
|
||||
&mut self,
|
||||
widget: WidgetState,
|
||||
style: taffy::Style,
|
||||
) -> anyhow::Result<(WidgetPair, taffy::NodeId)> {
|
||||
self.mark_redraw();
|
||||
add_child_internal(
|
||||
&mut self.state.tree,
|
||||
&mut self.state.widgets,
|
||||
&mut self.state.nodes,
|
||||
Some(self.tree_root_node),
|
||||
widget,
|
||||
style,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn add_child(
|
||||
&mut self,
|
||||
parent_widget_id: WidgetID,
|
||||
@@ -217,7 +240,7 @@ impl Layout {
|
||||
self.needs_redraw = true;
|
||||
}
|
||||
|
||||
fn process_pending_components(&mut self, alterables: &mut EventAlterables) -> anyhow::Result<()> {
|
||||
fn process_pending_components(&mut self, alterables: &mut EventAlterables) {
|
||||
for comp in &self.components_to_init {
|
||||
let mut common = CallbackDataCommon {
|
||||
state: &self.state,
|
||||
@@ -227,7 +250,6 @@ impl Layout {
|
||||
comp.0.init(&mut InitData { common: &mut common });
|
||||
}
|
||||
self.components_to_init.clear();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process_pending_widget_ticks(&mut self, alterables: &mut EventAlterables) {
|
||||
@@ -358,9 +380,7 @@ impl Layout {
|
||||
mut user_data: (&mut U1, &mut U2),
|
||||
) -> anyhow::Result<()> {
|
||||
let mut alterables = EventAlterables::default();
|
||||
|
||||
self.push_event_widget(listeners, self.root_node, event, &mut alterables, &mut user_data)?;
|
||||
|
||||
self.push_event_widget(listeners, self.tree_root_node, event, &mut alterables, &mut user_data)?;
|
||||
self.process_alterables(alterables)?;
|
||||
|
||||
listeners.gc();
|
||||
@@ -376,7 +396,7 @@ impl Layout {
|
||||
globals,
|
||||
};
|
||||
|
||||
let (root_widget, root_node) = add_child_internal(
|
||||
let (tree_root_widget, tree_root_node) = add_child_internal(
|
||||
&mut state.tree,
|
||||
&mut state.widgets,
|
||||
&mut state.nodes,
|
||||
@@ -392,12 +412,23 @@ impl Layout {
|
||||
},
|
||||
)?;
|
||||
|
||||
let (content_root_widget, content_root_node) = add_child_internal(
|
||||
&mut state.tree,
|
||||
&mut state.widgets,
|
||||
&mut state.nodes,
|
||||
Some(tree_root_node),
|
||||
WidgetDiv::create(),
|
||||
taffy::Style::default(),
|
||||
)?;
|
||||
|
||||
Ok(Self {
|
||||
state,
|
||||
prev_size: Vec2::default(),
|
||||
content_size: Vec2::default(),
|
||||
root_node,
|
||||
root_widget: root_widget.id,
|
||||
tree_root_node,
|
||||
tree_root_widget: tree_root_widget.id,
|
||||
content_root_node,
|
||||
content_root_widget: content_root_widget.id,
|
||||
needs_redraw: true,
|
||||
haptics_triggered: false,
|
||||
animations: Animations::default(),
|
||||
@@ -407,7 +438,7 @@ impl Layout {
|
||||
}
|
||||
|
||||
fn try_recompute_layout(&mut self, size: Vec2) -> anyhow::Result<()> {
|
||||
if !self.state.tree.dirty(self.root_node)? && self.prev_size == size {
|
||||
if !self.state.tree.dirty(self.tree_root_node)? && self.prev_size == size {
|
||||
// Nothing to do
|
||||
return Ok(());
|
||||
}
|
||||
@@ -417,7 +448,7 @@ impl Layout {
|
||||
self.prev_size = size;
|
||||
|
||||
self.state.tree.compute_layout_with_measure(
|
||||
self.root_node,
|
||||
self.tree_root_node,
|
||||
taffy::Size {
|
||||
width: taffy::AvailableSpace::Definite(size.x),
|
||||
height: taffy::AvailableSpace::Definite(size.y),
|
||||
@@ -443,7 +474,7 @@ impl Layout {
|
||||
}
|
||||
},
|
||||
)?;
|
||||
let root_size = self.state.tree.layout(self.root_node).unwrap().size;
|
||||
let root_size = self.state.tree.layout(self.tree_root_node).unwrap().size;
|
||||
if self.content_size.x != root_size.width || self.content_size.y != root_size.height {
|
||||
log::debug!(
|
||||
"content size changed: {:.0}x{:.0} → {:.0}x{:.0}",
|
||||
@@ -470,7 +501,7 @@ impl Layout {
|
||||
pub fn tick(&mut self) -> anyhow::Result<()> {
|
||||
let mut alterables = EventAlterables::default();
|
||||
self.animations.tick(&self.state, &mut alterables);
|
||||
self.process_pending_components(&mut alterables)?;
|
||||
self.process_pending_components(&mut alterables);
|
||||
self.process_pending_widget_ticks(&mut alterables);
|
||||
self.process_alterables(alterables)?;
|
||||
Ok(())
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
pub mod animation;
|
||||
pub mod any;
|
||||
pub mod assets;
|
||||
mod assets_internal;
|
||||
pub mod components;
|
||||
pub mod drawing;
|
||||
pub mod event;
|
||||
|
||||
@@ -8,7 +8,7 @@ mod widget_rectangle;
|
||||
mod widget_sprite;
|
||||
|
||||
use crate::{
|
||||
assets::{self, AssetProvider},
|
||||
assets::{AssetPath, AssetPathOwned, normalize_path},
|
||||
components::{Component, ComponentWeak},
|
||||
drawing::{self},
|
||||
event::EventListenerCollection,
|
||||
@@ -22,12 +22,7 @@ use crate::{
|
||||
};
|
||||
use ouroboros::self_referencing;
|
||||
use smallvec::SmallVec;
|
||||
use std::{
|
||||
cell::RefMut,
|
||||
collections::HashMap,
|
||||
path::{Path, PathBuf},
|
||||
rc::Rc,
|
||||
};
|
||||
use std::{cell::RefMut, collections::HashMap, path::Path, rc::Rc};
|
||||
|
||||
#[self_referencing]
|
||||
struct XmlDocument {
|
||||
@@ -44,7 +39,7 @@ pub struct Template {
|
||||
}
|
||||
|
||||
struct ParserFile {
|
||||
path: PathBuf,
|
||||
path: AssetPathOwned,
|
||||
document: Rc<XmlDocument>,
|
||||
template_parameters: HashMap<Rc<str>, Rc<str>>,
|
||||
}
|
||||
@@ -201,7 +196,7 @@ impl Fetchable for ParserData {
|
||||
#[derive(Default)]
|
||||
pub struct ParserState {
|
||||
pub data: ParserData,
|
||||
pub path: PathBuf,
|
||||
pub path: AssetPathOwned,
|
||||
}
|
||||
|
||||
impl ParserState {
|
||||
@@ -558,11 +553,22 @@ fn parse_tag_include<U1, U2>(
|
||||
#[allow(clippy::single_match)]
|
||||
match pair.attrib.as_ref() {
|
||||
"src" => {
|
||||
let mut new_path = file.path.parent().unwrap_or_else(|| Path::new("/")).to_path_buf();
|
||||
new_path.push(pair.value.as_ref());
|
||||
let new_path = assets::normalize_path(&new_path);
|
||||
let new_path = {
|
||||
let this = &file.path.clone();
|
||||
let include: &str = &pair.value;
|
||||
let buf = this.get_path_buf();
|
||||
let mut new_path = buf.parent().unwrap_or_else(|| Path::new("/")).to_path_buf();
|
||||
new_path.push(include);
|
||||
let new_path = normalize_path(&new_path);
|
||||
|
||||
let (new_file, node_layout) = get_doc_from_path(ctx, &new_path)?;
|
||||
match this {
|
||||
AssetPathOwned::WguiInternal(_) => AssetPathOwned::WguiInternal(new_path),
|
||||
AssetPathOwned::BuiltIn(_) => AssetPathOwned::BuiltIn(new_path),
|
||||
AssetPathOwned::Filesystem(_) => AssetPathOwned::Filesystem(new_path),
|
||||
}
|
||||
};
|
||||
let new_path_ref = new_path.as_ref();
|
||||
let (new_file, node_layout) = get_doc_from_asset_path(ctx, new_path_ref)?;
|
||||
parse_document_root(&new_file, ctx, parent_id, node_layout)?;
|
||||
|
||||
return Ok(());
|
||||
@@ -992,7 +998,7 @@ pub struct ParseDocumentExtra {
|
||||
// filled-in by you in `new_layout_from_assets` function
|
||||
pub struct ParseDocumentParams<'a> {
|
||||
pub globals: WguiGlobals, // mandatory field
|
||||
pub path: &'a str, // mandatory field
|
||||
pub path: AssetPath<'a>, // mandatory field
|
||||
pub extra: ParseDocumentExtra, // optional field, can be Default-ed
|
||||
}
|
||||
|
||||
@@ -1002,18 +1008,15 @@ pub fn parse_from_assets<U1, U2>(
|
||||
listeners: &mut EventListenerCollection<U1, U2>,
|
||||
parent_id: WidgetID,
|
||||
) -> anyhow::Result<ParserState> {
|
||||
let path = PathBuf::from(doc_params.path);
|
||||
|
||||
let parser_data = ParserData::default();
|
||||
let mut ctx = create_default_context(doc_params, layout, listeners, &parser_data);
|
||||
|
||||
let (file, node_layout) = get_doc_from_path(&ctx, &path)?;
|
||||
let (file, node_layout) = get_doc_from_asset_path(&ctx, doc_params.path)?;
|
||||
parse_document_root(&file, &mut ctx, parent_id, node_layout)?;
|
||||
|
||||
// move everything essential to the result
|
||||
let result = ParserState {
|
||||
data: std::mem::take(&mut ctx.data_local),
|
||||
path,
|
||||
path: doc_params.path.to_owned(),
|
||||
};
|
||||
|
||||
drop(ctx);
|
||||
@@ -1027,21 +1030,18 @@ pub fn new_layout_from_assets<U1, U2>(
|
||||
layout_params: &LayoutParams,
|
||||
) -> anyhow::Result<(Layout, ParserState)> {
|
||||
let mut layout = Layout::new(doc_params.globals.clone(), layout_params)?;
|
||||
let widget = layout.root_widget;
|
||||
let widget = layout.content_root_widget;
|
||||
let state = parse_from_assets(doc_params, &mut layout, listeners, widget)?;
|
||||
Ok((layout, state))
|
||||
}
|
||||
|
||||
fn assets_path_to_xml(assets: &mut Box<dyn AssetProvider>, path: &Path) -> anyhow::Result<String> {
|
||||
let data = assets.load_from_path(&path.to_string_lossy())?;
|
||||
Ok(String::from_utf8(data)?)
|
||||
}
|
||||
|
||||
fn get_doc_from_path<U1, U2>(
|
||||
fn get_doc_from_asset_path<U1, U2>(
|
||||
ctx: &ParserContext<U1, U2>,
|
||||
path: &Path,
|
||||
asset_path: AssetPath,
|
||||
) -> anyhow::Result<(ParserFile, roxmltree::NodeId)> {
|
||||
let xml = assets_path_to_xml(&mut ctx.layout.state.globals.assets(), path)?;
|
||||
let data = ctx.layout.state.globals.get_asset(asset_path)?;
|
||||
let xml = String::from_utf8(data)?;
|
||||
|
||||
let document = Rc::new(XmlDocument::new(xml, |xml| {
|
||||
let opt = roxmltree::ParsingOptions {
|
||||
allow_dtd: true,
|
||||
@@ -1054,7 +1054,7 @@ fn get_doc_from_path<U1, U2>(
|
||||
let tag_layout = require_tag_by_name(&root, "layout")?;
|
||||
|
||||
let file = ParserFile {
|
||||
path: PathBuf::from(path),
|
||||
path: asset_path.to_owned(),
|
||||
document: document.clone(),
|
||||
template_parameters: Default::default(),
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::{
|
||||
assets::AssetPath,
|
||||
layout::WidgetID,
|
||||
parser::{AttribPair, ParserContext, ParserFile, parse_children, parse_widget_universal, style::parse_style},
|
||||
renderer_vk::text::custom_glyph::{CustomGlyphContent, CustomGlyphData},
|
||||
@@ -21,9 +22,16 @@ pub fn parse_widget_sprite<'a, U1, U2>(
|
||||
for pair in attribs {
|
||||
let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref());
|
||||
match key {
|
||||
"src" => {
|
||||
"src" | "src_ext" | "src_internal" => {
|
||||
let asset_path = match key {
|
||||
"src" => AssetPath::BuiltIn(value),
|
||||
"src_ext" => AssetPath::Filesystem(value),
|
||||
"src_internal" => AssetPath::WguiInternal(value),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
if !value.is_empty() {
|
||||
glyph = match CustomGlyphContent::from_assets(&mut ctx.layout.state.globals.assets(), value) {
|
||||
glyph = match CustomGlyphContent::from_assets(&mut ctx.layout.state.globals, asset_path) {
|
||||
Ok(glyph) => Some(glyph),
|
||||
Err(e) => {
|
||||
log::warn!("failed to load {value}: {e}");
|
||||
@@ -32,11 +40,6 @@ pub fn parse_widget_sprite<'a, U1, U2>(
|
||||
}
|
||||
}
|
||||
}
|
||||
"src_ext" => {
|
||||
if !value.is_empty() && std::fs::exists(value).unwrap_or(false) {
|
||||
glyph = CustomGlyphContent::from_file(value).ok();
|
||||
}
|
||||
}
|
||||
"color" => {
|
||||
if let Some(color) = parse_color_hex(value) {
|
||||
params.color = Some(color);
|
||||
|
||||
@@ -243,6 +243,9 @@ impl Context {
|
||||
let pass = passes.last_mut().unwrap(); // always safe
|
||||
|
||||
match &primitive {
|
||||
drawing::RenderPrimitive::NewPass => {
|
||||
needs_new_pass = true;
|
||||
}
|
||||
drawing::RenderPrimitive::Rectangle(extent, rectangle) => {
|
||||
pass
|
||||
.rect_renderer
|
||||
|
||||
@@ -10,7 +10,7 @@ use cosmic_text::SubpixelBin;
|
||||
use image::RgbaImage;
|
||||
use resvg::usvg::{Options, Tree};
|
||||
|
||||
use crate::assets::AssetProvider;
|
||||
use crate::{assets::AssetPath, globals::WguiGlobals};
|
||||
|
||||
static AUTO_INCREMENT: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
@@ -32,19 +32,10 @@ impl CustomGlyphContent {
|
||||
}
|
||||
|
||||
#[allow(clippy::case_sensitive_file_extension_comparisons)]
|
||||
pub fn from_assets(provider: &mut Box<dyn AssetProvider>, path: &str) -> anyhow::Result<Self> {
|
||||
let data = provider.load_from_path(path)?;
|
||||
if path.ends_with(".svg") || path.ends_with(".svgz") {
|
||||
Ok(Self::from_bin_svg(&data)?)
|
||||
} else {
|
||||
Ok(Self::from_bin_raster(&data)?)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::case_sensitive_file_extension_comparisons)]
|
||||
pub fn from_file(path: &str) -> anyhow::Result<Self> {
|
||||
let data = std::fs::read(path)?;
|
||||
if path.ends_with(".svg") || path.ends_with(".svgz") {
|
||||
pub fn from_assets(globals: &mut WguiGlobals, path: AssetPath) -> anyhow::Result<Self> {
|
||||
let path_str = path.get_str();
|
||||
let data = globals.get_asset(path)?;
|
||||
if path_str.ends_with(".svg") || path_str.ends_with(".svgz") {
|
||||
Ok(Self::from_bin_svg(&data)?)
|
||||
} else {
|
||||
Ok(Self::from_bin_raster(&data)?)
|
||||
@@ -165,11 +156,7 @@ impl RasterizedCustomGlyph {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn validate(
|
||||
&self,
|
||||
input: &RasterizeCustomGlyphRequest,
|
||||
expected_type: Option<ContentType>,
|
||||
) {
|
||||
pub(super) fn validate(&self, input: &RasterizeCustomGlyphRequest, expected_type: Option<ContentType>) {
|
||||
if let Some(expected_type) = expected_type {
|
||||
assert_eq!(
|
||||
self.content_type, expected_type,
|
||||
@@ -222,10 +209,7 @@ impl ContentType {
|
||||
}
|
||||
}
|
||||
|
||||
fn rasterize_svg(
|
||||
tree: &Tree,
|
||||
input: &RasterizeCustomGlyphRequest,
|
||||
) -> Option<RasterizedCustomGlyph> {
|
||||
fn rasterize_svg(tree: &Tree, input: &RasterizeCustomGlyphRequest) -> Option<RasterizedCustomGlyph> {
|
||||
// Calculate the scale based on the "glyph size".
|
||||
let svg_size = tree.size();
|
||||
let scale_x = f32::from(input.width) / svg_size.width();
|
||||
|
||||
@@ -46,7 +46,7 @@ impl WidgetLabel {
|
||||
buffer.set_wrap(wrap);
|
||||
|
||||
buffer.set_rich_text(
|
||||
[(params.content.generate(&mut globals.i18n).as_ref(), attrs)],
|
||||
[(params.content.generate(&mut globals.i18n_builtin).as_ref(), attrs)],
|
||||
&Attrs::new(),
|
||||
Shaping::Advanced,
|
||||
params.style.align.map(Into::into),
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
use std::{cell::RefCell, rc::Rc, sync::Arc};
|
||||
|
||||
use button::setup_custom_button;
|
||||
use glam::{vec2, Affine2, Vec2};
|
||||
use glam::{Affine2, Vec2, vec2};
|
||||
use label::setup_custom_label;
|
||||
use vulkano::{command_buffer::CommandBufferUsage, image::view::ImageView};
|
||||
use wgui::{
|
||||
assets::AssetPath,
|
||||
drawing,
|
||||
event::{
|
||||
Event as WguiEvent, EventListenerCollection, InternalStateChangeEvent, ListenerHandleVec,
|
||||
@@ -21,7 +22,7 @@ use crate::{
|
||||
backend::input::{Haptics, PointerHit, PointerMode},
|
||||
graphics::{CommandBuffers, ExtentExt},
|
||||
state::AppState,
|
||||
windowing::backend::{ui_transform, FrameMeta, OverlayBackend, ShouldRender},
|
||||
windowing::backend::{FrameMeta, OverlayBackend, ShouldRender, ui_transform},
|
||||
};
|
||||
|
||||
use super::{timer::GuiTimer, timestep::Timestep};
|
||||
@@ -72,7 +73,7 @@ impl<S> GuiPanel<S> {
|
||||
|
||||
let doc_params = wgui::parser::ParseDocumentParams {
|
||||
globals: app.wgui_globals.clone(),
|
||||
path,
|
||||
path: AssetPath::BuiltIn(path),
|
||||
extra: wgui::parser::ParseDocumentExtra {
|
||||
on_custom_attribs: Some(Box::new({
|
||||
let custom_elems = custom_elems.clone();
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
use std::{collections::HashMap, rc::Rc};
|
||||
|
||||
use glam::{vec2, vec3, Affine3A, Mat4, Quat, Vec2, Vec3};
|
||||
use glam::{Affine3A, Mat4, Quat, Vec2, Vec3, vec2, vec3};
|
||||
use wgui::{
|
||||
animation::{Animation, AnimationEasing},
|
||||
assets::AssetPath,
|
||||
drawing::Color,
|
||||
event::{self, CallbackMetadata, EventListenerKind},
|
||||
layout::LayoutParams,
|
||||
@@ -19,14 +20,14 @@ use wgui::{
|
||||
use crate::{
|
||||
gui::panel::GuiPanel,
|
||||
state::AppState,
|
||||
subsystem::hid::{XkbKeymap, ALT, CTRL, META, SHIFT, SUPER},
|
||||
subsystem::hid::{ALT, CTRL, META, SHIFT, SUPER, XkbKeymap},
|
||||
windowing::window::{OverlayWindowConfig, OverlayWindowState, Positioning},
|
||||
};
|
||||
|
||||
use super::{
|
||||
handle_press, handle_release,
|
||||
KEYBOARD_NAME, KeyButtonData, KeyState, KeyboardBackend, KeyboardState, handle_press,
|
||||
handle_release,
|
||||
layout::{self, AltModifier, KeyCapType},
|
||||
KeyButtonData, KeyState, KeyboardBackend, KeyboardState, KEYBOARD_NAME,
|
||||
};
|
||||
|
||||
const BACKGROUND_PADDING: f32 = 4.;
|
||||
@@ -54,7 +55,7 @@ pub fn create_keyboard(
|
||||
let mut panel = GuiPanel::new_blank(app, state)?;
|
||||
|
||||
let (background, _) = panel.layout.add_child(
|
||||
panel.layout.root_widget,
|
||||
panel.layout.content_root_widget,
|
||||
WidgetRectangle::create(WidgetRectangleParams {
|
||||
color: wgui::drawing::Color::new(0., 0., 0., 0.6),
|
||||
round: WLength::Units(4.0),
|
||||
@@ -75,7 +76,7 @@ pub fn create_keyboard(
|
||||
|
||||
let parse_doc_params = wgui::parser::ParseDocumentParams {
|
||||
globals: app.wgui_globals.clone(),
|
||||
path: "gui/keyboard.xml",
|
||||
path: AssetPath::BuiltIn("gui/keyboard.xml"),
|
||||
extra: Default::default(),
|
||||
};
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ use std::{
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use glam::{vec3, Affine3A, Quat, Vec3};
|
||||
use glam::{Affine3A, Quat, Vec3, vec3};
|
||||
use idmap_derive::IntegerId;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use wgui::{
|
||||
@@ -28,8 +28,8 @@ use crate::{
|
||||
gui::panel::GuiPanel,
|
||||
state::{AppState, LeftRight},
|
||||
windowing::{
|
||||
window::{OverlayWindowConfig, OverlayWindowState, Positioning},
|
||||
OverlaySelector, Z_ORDER_TOAST,
|
||||
window::{OverlayWindowConfig, OverlayWindowState, Positioning},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -144,8 +144,7 @@ fn new_toast(toast: Toast, app: &mut AppState) -> Option<OverlayWindowConfig> {
|
||||
Positioning::FollowHead { lerp: 0.1 },
|
||||
),
|
||||
DisplayMethod::Watch => {
|
||||
let mut watch_pos =
|
||||
Vec3::from(app.session.config.watch_pos) + vec3(-0.005, -0.05, 0.02);
|
||||
let mut watch_pos = app.session.config.watch_pos + vec3(-0.005, -0.05, 0.02);
|
||||
let mut watch_rot = app.session.config.watch_rot;
|
||||
let relative_to = match app.session.config.watch_hand {
|
||||
LeftRight::Left => Positioning::FollowHand { hand: 0, lerp: 1.0 },
|
||||
@@ -172,7 +171,7 @@ fn new_toast(toast: Toast, app: &mut AppState) -> Option<OverlayWindowConfig> {
|
||||
let (rect, _) = panel
|
||||
.layout
|
||||
.add_child(
|
||||
panel.layout.root_widget,
|
||||
panel.layout.content_root_widget,
|
||||
WidgetRectangle::create(WidgetRectangleParams {
|
||||
color: parse_color_hex("#1e2030").unwrap(),
|
||||
border_color: parse_color_hex("#5e7090").unwrap(),
|
||||
|
||||
Reference in New Issue
Block a user