Skip to content

Commit

Permalink
Merge pull request #101 from kas-gui/work2
Browse files Browse the repository at this point in the history
Stable rust compatibility
  • Loading branch information
dhardy authored May 5, 2020
2 parents 4413087 + 88f1b54 commit 390a76a
Show file tree
Hide file tree
Showing 14 changed files with 179 additions and 45 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ conv = "0.3"
log = "0.4"
rusttype = "0.8"
smallvec = "1.4"
stack_dst = { version = "0.6", features = ["unsize"], optional = true }
stack_dst = { version = "0.6", optional = true }
bitflags = "1" # only used without winit

[dependencies.kas-macros]
Expand Down
20 changes: 16 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,20 @@ Goals and status
- User-customisable (*supports themes and colour schemes*)
- Desktop integration (*not yet realised*)

### Stability

### Rustc version

KAS is compatible with **stable rustc**. Using nightly Rust is advantageous:

- Proceedural macros emit better diagnostics. In some cases, diagnostics are
missed without nightly rustc, hence **nightly is recommended for development**.
- The `make_widget!` macro will require nightly Rust until the
`proc_macro_hygiene` feature is complete.
Usage of this macro is optional; most examples do so for convenience.
- A few other minor features are nightly-only. See *feature flags*
documentation below and in other crates' READMEs.

### Code stability

The `master` branch has frequent breaking changes. Releases will respect
[semver](https://semver.org/) rules. At this point, most releases will include
Expand All @@ -58,12 +71,11 @@ provide a comprehensive collection of core widgets.
- Persistent widgets with embedded state
- Type-safe user-defined event handlers
- Robust event handling model
- (Mostly) full keyboard and touch-screen support
- Extensive support for keyboard and touch-screen control
- Disabled and error states for widgets
- Scalable (HiDPI) supporting fractional scaling
- Theme API, simple draw API and raw graphics access
- Grid layout with spans
- Width-for-height sizing
- Automatic widget layout including grids with spans and width-for-height sizing

### Missing features

Expand Down
4 changes: 4 additions & 0 deletions kas-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ description = "GUI Toolkit Abstraction System (macros)"
keywords = ["gui", "proc-macro"]
categories = ["gui"]
repository = "https://github.com/kas-gui/kas"
build = "build.rs"

[lib]
proc-macro = true
Expand All @@ -21,3 +22,6 @@ version = "1.0.14"
# We need 'extra-traits' for equality testing
# We need 'full' for parsing macros within macro arguments
features = ["extra-traits", "full"]

[build-dependencies]
version_check = "0.9"
12 changes: 12 additions & 0 deletions kas-macros/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@ solely because procedural macros must current be in a dedicated crate.
Users are advised not to depend on this library directly, but instead rely on
the main KAS lib, which re-exports these macros in its API.


Stable vs nightly
-----------------

This crate is compatible with **stable rustc**, however, usage of **nightly**
has some benefits:

- More macro diagnostics are emitted, resulting in better error messages
(without this, some errors may not even be reported)
- With `#![feature(proc_macro_hygiene)]`, the `make_widget!` macro may be used


Copyright and Licence
-------

Expand Down
8 changes: 8 additions & 0 deletions kas-macros/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
fn main() {
if version_check::Channel::read()
.map(|c| c.is_nightly())
.unwrap_or(false)
{
println!("cargo:rustc-cfg=nightly");
}
}
7 changes: 7 additions & 0 deletions kas-macros/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ pub fn read_attrs(ast: &mut DeriveInput) -> Result<Args> {
for attr in field.attrs.drain(..) {
if attr.path == parse_quote! { layout } || attr.path == parse_quote! { handler } {
// These are valid attributes according to proc_macro_derive, so we need to catch them
#[cfg(nightly)]
attr.span()
.unwrap()
.error("invalid attribute on Widget field (applicable to struct only)")
Expand All @@ -72,6 +73,7 @@ pub fn read_attrs(ast: &mut DeriveInput) -> Result<Args> {
if core_data.is_none() {
core_data = Some(member(i, field.ident.clone()));
} else {
#[cfg(nightly)]
attr.span()
.unwrap()
.error("multiple fields marked with #[widget_core]")
Expand All @@ -82,6 +84,7 @@ pub fn read_attrs(ast: &mut DeriveInput) -> Result<Args> {
if field.ty != parse_quote! { <Self as kas::LayoutData>::Data }
&& field.ty != parse_quote! { <Self as LayoutData>::Data }
{
#[cfg(nightly)]
field
.ty
.span()
Expand All @@ -91,6 +94,7 @@ pub fn read_attrs(ast: &mut DeriveInput) -> Result<Args> {
}
layout_data = Some(member(i, field.ident.clone()));
} else {
#[cfg(nightly)]
attr.span()
.unwrap()
.error("multiple fields marked with #[layout_data]")
Expand All @@ -111,6 +115,7 @@ pub fn read_attrs(ast: &mut DeriveInput) -> Result<Args> {
for attr in ast.attrs.drain(..) {
if attr.path == parse_quote! { widget_core } || attr.path == parse_quote! { layout_data } {
// These are valid attributes according to proc_macro_derive, so we need to catch them
#[cfg(nightly)]
attr.span()
.unwrap()
.error("invalid attribute on Widget struct (applicable to fields only)")
Expand All @@ -119,6 +124,7 @@ pub fn read_attrs(ast: &mut DeriveInput) -> Result<Args> {
if widget.is_none() {
widget = Some(syn::parse2(attr.tokens)?);
} else {
#[cfg(nightly)]
attr.span()
.unwrap()
.error("multiple #[widget(..)] attributes on type")
Expand All @@ -128,6 +134,7 @@ pub fn read_attrs(ast: &mut DeriveInput) -> Result<Args> {
if layout.is_none() {
layout = Some(syn::parse2(attr.tokens)?);
} else {
#[cfg(nightly)]
attr.span()
.unwrap()
.error("multiple #[layout(..)] attributes on type")
Expand Down
13 changes: 10 additions & 3 deletions kas-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// https://www.apache.org/licenses/LICENSE-2.0

#![recursion_limit = "128"]
#![feature(proc_macro_diagnostic)]
#![cfg_attr(nightly, feature(proc_macro_diagnostic))]

extern crate proc_macro;

Expand All @@ -15,6 +15,7 @@ use std::collections::HashMap;
use proc_macro2::{Span, TokenStream};
use quote::{quote, ToTokens, TokenStreamExt};
use std::fmt::Write;
#[cfg(nightly)]
use syn::spanned::Spanned;
use syn::Token;
use syn::{parse_macro_input, parse_quote};
Expand Down Expand Up @@ -332,16 +333,19 @@ pub fn make_widget(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
syn::ImplItem::Method(syn::ImplItemMethod { sig, .. })
if sig.ident == *handler =>
{
if let Some(x) = x {
if let Some(_x) = x {
#[cfg(nightly)]
handler
.span()
.unwrap()
.error("multiple methods with this name")
.emit();
x.0.span()
#[cfg(nightly)]
_x.0.span()
.unwrap()
.error("first method with this name")
.emit();
#[cfg(nightly)]
sig.ident
.span()
.unwrap()
Expand All @@ -350,6 +354,7 @@ pub fn make_widget(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
return None;
}
if sig.inputs.len() != 3 {
#[cfg(nightly)]
sig.span()
.unwrap()
.error("handler functions must have signature: fn handler(&mut self, mgr: &mut Manager, msg: T)")
Expand All @@ -371,6 +376,7 @@ pub fn make_widget(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
find_handler_ty_buf.push((handler.clone(), x.1.clone()));
Some(x.1)
} else {
#[cfg(nightly)]
handler
.span()
.unwrap()
Expand Down Expand Up @@ -435,6 +441,7 @@ pub fn make_widget(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
} else {
// We could default to msg=VoidMsg here. If error messages weren't
// so terrible this might even be a good idea!
#[cfg(nightly)]
args.struct_span
.unwrap()
.error("make_widget: cannot discover msg type from #[handler] attr or Handler impl")
Expand Down
7 changes: 5 additions & 2 deletions kas-theme/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,19 @@ default = ["font-kit", "stack_dst"]

# Use Generic Associated Types (experimental)
# Currently (Feb 2020) compiler support is poor.
gat = []
gat = ["unsize"]

# Use stack_dst crate for sized unsized types
stack_dst = ["kas/stack_dst", "stack_dst_"]

# Use the unstable 'unsize' feature
unsize = ["stack_dst_/unsize"]

[dependencies]
font-kit = { version = "0.6.0", optional = true }
lazy_static = "1.4.0"
log = "0.4"
stack_dst_ = { version = "0.6", package = "stack_dst", features = ["unsize"], optional = true }
stack_dst_ = { version = "0.6", package = "stack_dst", optional = true }

[dependencies.kas]
path = ".."
Expand Down
2 changes: 1 addition & 1 deletion kas-theme/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
//! between themes.
#![cfg_attr(feature = "gat", feature(generic_associated_types))]
#![cfg_attr(feature = "stack_dst", feature(unsize))]
#![cfg_attr(feature = "unsize", feature(unsize))]

mod col;
mod dim;
Expand Down
30 changes: 28 additions & 2 deletions kas-theme/src/multi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,25 @@
//! Wrapper around mutliple themes, supporting run-time switching
use std::collections::HashMap;
#[cfg(feature = "unsize")]
use std::marker::Unsize;

use crate::{StackDst, Theme, ThemeDst, WindowDst};
use kas::draw::{Colour, DrawHandle, DrawShared};
use kas::geom::Rect;
use kas::{string::CowString, ThemeAction, ThemeApi};

#[cfg(feature = "unsize")]
type DynTheme<Draw> = StackDst<dyn ThemeDst<Draw>>;
#[cfg(not(feature = "unsize"))]
type DynTheme<Draw> = Box<dyn ThemeDst<Draw>>;

/// Wrapper around mutliple themes, supporting run-time switching
///
/// **Feature gated**: this is only available with feature `stack_dst`.
pub struct MultiTheme<Draw> {
names: HashMap<CowString, usize>,
themes: Vec<StackDst<dyn ThemeDst<Draw>>>,
themes: Vec<DynTheme<Draw>>,
active: usize,
}

Expand All @@ -27,7 +33,7 @@ pub struct MultiTheme<Draw> {
/// Construct via [`MultiTheme::builder`].
pub struct MultiThemeBuilder<Draw> {
names: HashMap<CowString, usize>,
themes: Vec<StackDst<dyn ThemeDst<Draw>>>,
themes: Vec<DynTheme<Draw>>,
}

impl<Draw> MultiTheme<Draw> {
Expand All @@ -42,6 +48,10 @@ impl<Draw> MultiTheme<Draw> {

impl<Draw> MultiThemeBuilder<Draw> {
/// Add a theme
///
/// Note: the constraints of this method vary depending on the `unsize`
/// feature.
#[cfg(feature = "unsize")]
pub fn add<S: Into<CowString>, U>(mut self, name: S, theme: U) -> Self
where
U: Unsize<dyn ThemeDst<Draw>>,
Expand All @@ -53,6 +63,22 @@ impl<Draw> MultiThemeBuilder<Draw> {
self
}

/// Add a theme
///
/// Note: the constraints of this method vary depending on the `unsize`
/// feature.
#[cfg(not(feature = "unsize"))]
pub fn add<S: Into<CowString>, T>(mut self, name: S, theme: T) -> Self
where
Draw: DrawShared,
T: ThemeDst<Draw> + 'static,
{
let index = self.themes.len();
self.names.insert(name.into(), index);
self.themes.push(Box::new(theme));
self
}

/// Build
///
/// Fails if no themes were added.
Expand Down
42 changes: 38 additions & 4 deletions kas-theme/src/theme_dst.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,22 @@ where
}

fn new_window(&self, draw: &mut D::Draw, dpi_factor: f32) -> StackDst<dyn WindowDst<D::Draw>> {
StackDst::new_or_boxed(<T as Theme<D>>::new_window(self, draw, dpi_factor))
let window = <T as Theme<D>>::new_window(self, draw, dpi_factor);
#[cfg(feature = "unsize")]
{
StackDst::new_or_boxed(window)
}
#[cfg(not(feature = "unsize"))]
{
match StackDst::new_stable(window, |w| w as &dyn WindowDst<D::Draw>) {
Ok(s) => s,
Err(window) => {
StackDst::new_stable(Box::new(window), |w| w as &dyn WindowDst<D::Draw>)
.ok()
.expect("boxed window too big for StackDst!")
}
}
}
}

fn update_window(&self, window: &mut dyn WindowDst<D::Draw>, dpi_factor: f32) {
Expand All @@ -100,7 +115,16 @@ where
) -> StackDst<dyn DrawHandle> {
let window = window.as_any_mut().downcast_mut().unwrap();
let h = <T as Theme<D>>::draw_handle(self, draw, window, rect);
StackDst::new_or_boxed(h)
#[cfg(feature = "unsize")]
{
StackDst::new_or_boxed(h)
}
#[cfg(not(feature = "unsize"))]
{
StackDst::new_stable(h, |h| h as &dyn DrawHandle)
.ok()
.expect("handle too big for StackDst!")
}
}

fn clear_colour(&self) -> Colour {
Expand All @@ -115,7 +139,8 @@ impl<'a, D: DrawShared + 'static, T: Theme<D>> ThemeDst<D> for T {
}

fn new_window(&self, draw: &mut D::Draw, dpi_factor: f32) -> StackDst<dyn WindowDst<D::Draw>> {
StackDst::new_or_boxed(<T as Theme<D>>::new_window(self, draw, dpi_factor))
let window = <T as Theme<D>>::new_window(self, draw, dpi_factor);
StackDst::new_or_boxed(window)
}

fn update_window(&self, window: &mut dyn WindowDst<D::Draw>, dpi_factor: f32) {
Expand Down Expand Up @@ -174,7 +199,16 @@ where
{
unsafe fn size_handle<'a>(&'a mut self, draw: &'a mut Draw) -> StackDst<dyn SizeHandle> {
let h = <W as Window<Draw>>::size_handle(self, draw);
StackDst::new_or_boxed(h)
#[cfg(feature = "unsize")]
{
StackDst::new_or_boxed(h)
}
#[cfg(not(feature = "unsize"))]
{
StackDst::new_stable(h, |h| h as &dyn SizeHandle)
.ok()
.expect("handle too big for StackDst!")
}
}

fn as_any_mut(&mut self) -> &mut dyn Any {
Expand Down
3 changes: 3 additions & 0 deletions kas-wgpu/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ gat = ["kas-theme/gat"]
# Use stack_dst crate for sized unsized types
stack_dst = ["kas-theme/stack_dst"]

# Use kas-theme's unsize feature (nightly-only)
unsize = ["kas-theme/unsize"]

[dependencies]
kas = { path = "..", version = "0.3.0", features = ["winit"] }
kas-theme = { path = "../kas-theme", version = "0.3.0" }
Expand Down
1 change: 1 addition & 0 deletions kas-wgpu/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ This crate has the following feature flags:
- `gat`: enables usage of the Generic Associated Types feature (nightly only
and currently unstable), allowing some usages of `unsafe` to be avoided.
(The plan is to enable this by default once the feature is mature.)
- `unsize`: forwards this feature flag to `kas-theme`

Copyright and Licence
-------
Expand Down
Loading

0 comments on commit 390a76a

Please sign in to comment.