Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update deps, add widget_index!, improve examples/markdown #291

Merged
merged 4 commits into from
Mar 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion crates/kas-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ path = "../kas-macros"

[dependencies.kas-text]
version = "0.4.2"
#git = "https://github.com/kas-gui/kas-text.git"
git = "https://github.com/kas-gui/kas-text.git"
rev = "179278f"

[dependencies.winit]
# Provides translations for several winit types
Expand Down
4 changes: 2 additions & 2 deletions crates/kas-core/src/event/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ pub trait SendEvent: Handler {
/// return Response::Unused;
/// }
/// match self.find_child_index(&id) {
/// Some(0) => self.child0.send(mgr, id, event).into(),
/// Some(1) => self.child1.send(mgr, id, event).into(),
/// Some(widget_index![self.child0]) => self.child0.send(mgr, id, event).into(),
/// Some(widget_index![self.child1]) => self.child1.send(mgr, id, event).into(),
/// // ...
/// _ => {
/// debug_assert_eq!(self.id(), id);
Expand Down
13 changes: 8 additions & 5 deletions crates/kas-core/src/layout/sizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ impl SolveCache {
mgr: &mut SetRectMgr,
mut rect: Rect,
inner_margin: bool,
print_heirarchy: bool,
) {
let start = std::time::Instant::now();

Expand Down Expand Up @@ -220,11 +221,13 @@ impl SolveCache {
widget.set_rect(mgr, rect, AlignHints::NONE);

trace!(target: "kas_perf", "layout::apply_rect: {}ms", start.elapsed().as_millis());
trace!(
"layout::apply_rect: size={:?}, hierarchy:{}",
rect.size,
WidgetHeirarchy(widget, 0),
);
if print_heirarchy {
trace!(
"layout::apply_rect: size={:?}, hierarchy:{}",
rect.size,
WidgetHeirarchy(widget, 0),
);
}

self.refresh_rules = false;
}
Expand Down
2 changes: 1 addition & 1 deletion crates/kas-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ proc-macro-error = "1.0"
version = "1.0.14"
# We need 'extra-traits' for equality testing
# We need 'full' for parsing macros within macro arguments
features = ["extra-traits", "full"]
features = ["extra-traits", "full", "visit-mut"]

[build-dependencies]
version_check = "0.9"
18 changes: 18 additions & 0 deletions crates/kas-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ mod make_layout;
mod make_widget;
pub(crate) mod where_clause;
mod widget;
mod widget_index;

/// A variant of the standard `derive` macro
///
Expand Down Expand Up @@ -277,3 +278,20 @@ pub fn derive_empty_msg(input: TokenStream) -> TokenStream {
};
toks.into()
}

/// Index of a child widget
///
/// This macro is usable only within a [`widget!`] macro.
///
/// Example usage: `widget_index![self.a]`. If `a` is a child widget (a field
/// marked with the `#[widget]` attribute), then this expands to the child
/// widget's index (as used by [`WidgetChildren`]). Otherwise, this is an error.
///
/// [`WidgetChildren`]: https://docs.rs/kas/latest/kas/trait.WidgetChildren.html
#[proc_macro_error]
#[proc_macro]
pub fn widget_index(input: TokenStream) -> TokenStream {
let input2 = input.clone();
let _ = parse_macro_input!(input2 as widget_index::BaseInput);
input
}
4 changes: 4 additions & 0 deletions crates/kas-macros/src/widget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use syn::spanned::Spanned;
use syn::{parse_quote, Result};

pub(crate) fn widget(mut args: Widget) -> Result<TokenStream> {
crate::widget_index::visit_widget(&mut args);
let mut toks = quote! { #args };

let name = &args.ident;
Expand Down Expand Up @@ -42,6 +43,9 @@ pub(crate) fn widget(mut args: Widget) -> Result<TokenStream> {
"impl conflicts with use of #[widget(derive=FIELD)]"
);
}
if !args.children.is_empty() {
emit_warning!(impl_.span(), "use of `#![widget]` on children with custom `WidgetChildren` implementation");
}
impl_widget_children = false;
} else if *path == parse_quote! { ::kas::WidgetConfig }
|| *path == parse_quote! { kas::WidgetConfig }
Expand Down
98 changes: 98 additions & 0 deletions crates/kas-macros/src/widget_index.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License in the LICENSE-APACHE file or at:
// https://www.apache.org/licenses/LICENSE-2.0

use crate::args::{self, Widget};
use proc_macro2::Span;
use proc_macro_error::emit_error;
use syn::parse::{Parse, ParseStream};
use syn::spanned::Spanned;
use syn::visit_mut::{self, VisitMut};
use syn::{parse_quote, Error, Lit, Member, Result, Token};

#[allow(non_camel_case_types)]
mod kw {
syn::custom_keyword!(error_emitted);
}

pub struct BaseInput;
impl Parse for BaseInput {
fn parse(input: ParseStream) -> Result<Self> {
if input.peek(Lit) {
// Okay: macro was expanded internally (or user wrote a number...)
let _ = input.parse::<Lit>()?;
if input.is_empty() {
return Ok(Self);
}
} else if input.peek(kw::error_emitted) {
// An error was already emitted by the visitor
let _ = input.parse::<kw::error_emitted>()?;
if input.is_empty() {
return Ok(Self);
}
}

let msg = "usage of `widget_index!` invalid outside of `widget!` macro";
Err(Error::new(Span::call_site(), msg))
}
}

pub struct WidgetInput {
pub ident: Member,
}

impl Parse for WidgetInput {
fn parse(input: ParseStream) -> Result<Self> {
let _ = input.parse::<Token![self]>()?;
let _ = input.parse::<Token![.]>()?;
let ident = input.parse()?;

Ok(WidgetInput { ident })
}
}

struct Visitor<'a> {
children: &'a [args::Child],
}
impl<'a> VisitMut for Visitor<'a> {
fn visit_macro_mut(&mut self, node: &mut syn::Macro) {
// HACK: we cannot expand the macro here since we do not have an Expr
// to replace. Instead we can only modify the macro's tokens.
// WARNING: if the macro's tokens are modified before printing an error
// message is emitted then the span of that error message is incorrect.
Comment on lines +59 to +63
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is a bit hacky, but still works well enough. The only alternative (as far as I am aware) would be to match one level up, which would require matching both expressions and patterns (and maybe more) to catch everywhere the macro might be used.


if node.path == parse_quote! { widget_index } {
let args = match syn::parse2::<WidgetInput>(node.tokens.clone()) {
Ok(args) => args,
Err(err) => {
emit_error!(node.tokens.span(), "{}", err);
node.tokens = parse_quote! { error_emitted };
return;
}
};

for (i, child) in self.children.iter().enumerate() {
if args.ident == child.ident {
node.tokens = parse_quote! { #i };
return;
}
}

emit_error!(args.ident.span(), "does not match any child widget");
node.tokens = parse_quote! { error_emitted };
return;
}

visit_mut::visit_macro_mut(self, node);
}
}

pub fn visit_widget(node: &mut Widget) {
let children = &node.children;
let mut obj = Visitor { children };

for impl_ in &mut node.extra_impls {
obj.visit_item_impl_mut(impl_);
}
}
4 changes: 2 additions & 2 deletions crates/kas-resvg/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ svg = ["resvg", "usvg"]

[dependencies]
tiny-skia = { version = "0.6.1" }
resvg = { version = "0.18.0", optional = true }
usvg = { version = "0.18.0", optional = true }
resvg = { version = "0.22.0", optional = true }
usvg = { version = "0.22.0", optional = true }

# We must rename this package since macros expect kas to be in scope:
kas = { version = "0.10.0", package = "kas-core", path = "../kas-core" }
6 changes: 4 additions & 2 deletions crates/kas-resvg/src/svg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use kas::geom::Vec2;
use kas::layout::MarginSelector;
use kas::prelude::*;
use std::path::PathBuf;
use tiny_skia::Pixmap;
use tiny_skia::{Pixmap, Transform};

widget! {
/// An SVG image loaded from a path
Expand Down Expand Up @@ -113,6 +113,7 @@ widget! {
keep_named_groups: false,
default_size: usvg::Size::new(def_size, def_size).unwrap(),
fontdb,
image_href_resolver: &Default::default(),
};

let tree = usvg::Tree::from_data(&data, &opts).unwrap();
Expand Down Expand Up @@ -172,7 +173,8 @@ widget! {
let (w, h) = (pm.width(), pm.height());

// alas, we cannot tell resvg to skip the aspect-ratio-scaling!
resvg::render(tree, usvg::FitTo::Height(h), pm.as_mut());
let transform = Transform::identity();
resvg::render(tree, usvg::FitTo::Height(h), transform, pm.as_mut());

let id = mgr.draw_shared().image_alloc((w, h)).unwrap();
mgr.draw_shared().image_upload(id, pm.data(), ImageFormat::Rgba8);
Expand Down
3 changes: 2 additions & 1 deletion crates/kas-wgpu/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ default-features = false

[dependencies.kas-text]
version = "0.4.2"
#git = "https://github.com/kas-gui/kas-text.git"
git = "https://github.com/kas-gui/kas-text.git"
rev = "179278f"

[build-dependencies]
glob = "0.3"
14 changes: 7 additions & 7 deletions crates/kas-wgpu/src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ impl<C: CustomPipe, T: Theme<DrawPipe<C>>> Window<C, T> {
next_avail_frame_time: time,
queued_frame_time: Some(time),
};
r.apply_size(shared);
r.apply_size(shared, true);

trace!("Window::new completed in {}µs", time.elapsed().as_micros());
Ok(r)
Expand Down Expand Up @@ -191,9 +191,9 @@ impl<C: CustomPipe, T: Theme<DrawPipe<C>>> Window<C, T> {
}
if action.contains(TkAction::RESIZE) {
self.solve_cache.invalidate_rule_cache();
self.apply_size(shared);
self.apply_size(shared, false);
} else if action.contains(TkAction::SET_SIZE) {
self.apply_size(shared);
self.apply_size(shared, false);
}
/*if action.contains(TkAction::Popup) {
let widget = &mut self.widget;
Expand Down Expand Up @@ -280,11 +280,11 @@ impl<C: CustomPipe, T: Theme<DrawPipe<C>>> Window<C, T> {
self.ev_state.full_configure(&mut tkw, &mut *self.widget);

self.solve_cache.invalidate_rule_cache();
self.apply_size(shared);
self.apply_size(shared, false);
trace!("reconfigure completed in {}µs", time.elapsed().as_micros());
}

fn apply_size(&mut self, shared: &mut SharedState<C, T>) {
fn apply_size(&mut self, shared: &mut SharedState<C, T>, first: bool) {
let time = Instant::now();
let rect = Rect::new(Coord::ZERO, self.sc_size());
debug!("Resizing window to rect = {:?}", rect);
Expand All @@ -296,7 +296,7 @@ impl<C: CustomPipe, T: Theme<DrawPipe<C>>> Window<C, T> {
&mut shared.draw,
&mut self.ev_state,
);
solve_cache.apply_rect(widget.as_widget_mut(), &mut mgr, rect, true);
solve_cache.apply_rect(widget.as_widget_mut(), &mut mgr, rect, true, first);
widget.resize_popups(&mut mgr);

let restrict_dimensions = self.widget.restrict_dimensions();
Expand Down Expand Up @@ -329,7 +329,7 @@ impl<C: CustomPipe, T: Theme<DrawPipe<C>>> Window<C, T> {

// Note that on resize, width adjustments may affect height
// requirements; we therefore refresh size restrictions.
self.apply_size(shared);
self.apply_size(shared, false);

trace!(
"do_resize completed in {}µs (including apply_size time)",
Expand Down
6 changes: 3 additions & 3 deletions crates/kas-widgets/src/scrollbar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -647,23 +647,23 @@ widget! {
}

match self.find_child_index(&id) {
Some(0) => self.horiz_bar
Some(widget_index![self.horiz_bar]) => self.horiz_bar
.send(mgr, id, event)
.try_into()
.unwrap_or_else(|msg| {
let offset = Offset(msg, self.inner.scroll_offset().1);
self.inner.set_scroll_offset(mgr, offset);
Response::Used
}),
Some(1) => self.vert_bar
Some(widget_index![self.vert_bar]) => self.vert_bar
.send(mgr, id, event)
.try_into()
.unwrap_or_else(|msg| {
let offset = Offset(self.inner.scroll_offset().0, msg);
self.inner.set_scroll_offset(mgr, offset);
Response::Used
}),
Some(2) => {
Some(widget_index![self.inner]) => {
let r = self.inner.send(mgr, id, event);
// We assume the inner already updated its positions; this is just to set bars
if matches!(r, Response::Pan(_) | Response::Scrolled | Response::Focus(_)) {
Expand Down
2 changes: 1 addition & 1 deletion crates/kas-widgets/src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,6 @@ impl<W: Widget> Window<W> {
Rect::new(Coord(x, y), Size::new(w, h))
};

cache.apply_rect(widget, mgr, rect, false);
cache.apply_rect(widget, mgr, rect, false, true);
}
}
Loading