Skip to content

Instantly share code, notes, and snippets.

@raphlinus
Created June 18, 2019 14:49
Show Gist options
  • Select an option

  • Save raphlinus/25a64eb7ce4a4d2012944d4ff22130ea to your computer and use it in GitHub Desktop.

Select an option

Save raphlinus/25a64eb7ce4a4d2012944d4ff22130ea to your computer and use it in GitHub Desktop.
Very rough sketch of parent-owns widget scheme
// Sketch of new widget scheme for druid. Some pseudocode within.
/// Base of a widget.
///
/// Name should likely change, this name is chosen to express
/// what's actually in it.
pub struct Base<W: WidgetInner> {
state: BaseState,
inner: W,
}
/// Per-widget state.
///
/// This mostly corresponds to state stored in a Vec in current druid
/// `Ui` and `LayoutCtx`.
pub struct BaseState {
geom: Geometry,
is_active: bool,
is_hot: bool,
is_focused: bool,
// Rename to needs_anim_frame?
is_animating: bool,
needs_layout: bool,
// Future: paint rect...
}
/// A trait expressing widget behavior and appearance.
///
/// This is broadly similar to the existing Widget trait in druid.
pub trait WidgetInner {
fn paint(&mut self, paint_ctx: &mut PaintCtx, base_state: &BaseState, env: &Env);
fn layout(&mut self, layout_ctx: &mut LayoutCtx, bc: &BoxConstraints, env: &Env) -> Geometry;
fn event(&mut self, event: &Event, ctx, env) -> Option<Action>;
/// Value in env is new value; delta can be used to recover old value.
fn delta(&mut self, delta: &Delta, ctx, env);
/// Send arbitrary data to a widget.
///
/// Likely to be little-used. Path is used to route (when path matches exactly, widget
/// can consume).
fn poke(&mut self, &Any, ctx, env);
}
pub struct Env {
path: Path,
value: Value,
// more stuff for looking up theme data etc.
}
pub enum Event {
MouseEvent(...),
KeyEvent(...),
FocusChanged,
...
}
impl Base {
pub fn paint(&mut self, paint_ctx: &mut PaintCtx, env: &Env) {
if self.state.paint_rect.intersect(paint_ctx.rect) {
self.inner.paint(paint_ctx, &self.state, env);
}
}
pub fn layout(...) {
// Note: could cache here, avoiding recursive call
self.inner.layout(...);
}
pub fn delta(...) {
if env.path.intersects(path) {
self.inner.delta(...)
}
}
pub fn event(...) -> Option<Action> {
// use self.state.is_active etc to route
}
}
struct TypicalContainer {
// Note: dyn is only needed for heterogeneous children
//
// The path here is a *relative* path, to be joined
children: Vec<(Path, Base<Box<dyn WidgetInner>>)>,
}
impl WidgetInner for TypicalContainer {
fn paint(&mut self, ...) {
for (path, child) in &mut self.children {
// Note: because this is used so pervasively, maybe most `Base` methods
// include a relative path to join as an extra arg; this would also reduce
// the risk of accidentally forgetting to do so.
let child_env = env.join_path(path);
child.paint(paint_ctx, child_env);
}
}
fn event(&mut self, ...) {
for (path, child) in &mut self.children {
let child_env = env.join_path(path);
let action = child.event(ctx, event, child_env);
if action.is_some() {
return action;
}
}
None
}
fn layout(&mut self, ...) {
// call layout on children in arbitrary order
}
fn delta(...) {
}
fn poke(...) {
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment