2 Commit-ok 49f766f3e5 ... 5709cb809f

Szerző SHA1 Üzenet Dátum
  Kestrel 5709cb809f Finished implementing form group and added example. 3 hónapja
  Kestrel 97b26ff0c2 Remove some debugging information. 3 hónapja

+ 59 - 0
examples/form_demo.rs

@@ -0,0 +1,59 @@
+use kahlo::math::PixelSideOffsets;
+use patina::{
+    layout::{HorizontalAlignment, TableCell, VerticalAlignment, SizePolicy},
+    prelude::*,
+    ui::{UIControlMsg, UIHandle},
+    widget,
+};
+
+struct FormWindow {
+    root: widget::Frame<Self>,
+}
+
+impl FormWindow {
+    fn new(uih: &mut UIHandle) -> Self {
+        let mut group = widget::FormGroup::new(uih);
+        group.layout_node_mut().set_height_policy(SizePolicy::fixed(0));
+        // XXX: left margin not working??
+        group.set_label_margin(PixelSideOffsets::new(0, 10, 0, 10));
+        group.add_widget("button", widget::Button::new_with_label(uih, "engage").boxed());
+        group.add_widget("button 2", widget::Button::new_with_label(uih, "engage").boxed());
+        group.add_widget("button 3", widget::Button::new_with_label(uih, "engage").boxed());
+        group.add_widget("button 4 with longer description", widget::Button::new_with_label(uih, "engage").boxed());
+        Self { root: widget::Frame::wrap_widget(group) }
+    }
+}
+
+impl Component for FormWindow {
+    type ParentMsg = UIControlMsg;
+    type Msg = ();
+
+    fn process(&mut self, _msg: Self::Msg) -> Vec<Self::ParentMsg> {
+        let mut out = String::new();
+        patina::layout::dump_node_tree(self.root.layout_node(), &mut out);
+        std::fs::write("patina-form-example.layout_tree", out)
+            .expect("couldn't dump node tree");
+        vec![UIControlMsg::Terminate]
+    }
+}
+
+impl WindowComponent for FormWindow {
+    fn map_window_event(&self, we: patina::window::WindowEvent) -> Option<Self::Msg> {
+        match we {
+            patina::window::WindowEvent::CloseRequested => Some(()),
+            _ => None,
+        }
+    }
+
+    type RootWidget = widget::Frame<Self>;
+    fn root_widget(&self) -> &Self::RootWidget {
+        &self.root
+    }
+    fn root_widget_mut(&mut self) -> &mut Self::RootWidget {
+        &mut self.root
+    }
+}
+
+fn main() {
+    patina::ui::make_opinionated_ui(FormWindow::new).run();
+}

+ 4 - 0
src/layout.rs

@@ -286,6 +286,10 @@ impl LayoutNode {
     pub fn render_needed(&self) {
         self.cache.render_queue.borrow_mut().push(self.cache_key);
     }
+
+    pub fn make_new_node(&self) -> Self {
+        Self::new(self.cache.clone())
+    }
 }
 
 /// Accessors

+ 0 - 8
src/layout/arr.rs

@@ -25,13 +25,6 @@ fn do_align(node: &LayoutNodeAccess, mut inside: PixelBox) -> PixelBox {
     let net_policy = node
         .with_state(|ns| ns.net_policy)
         .expect("do_align invoked with node that has no net_policy");
-    println!("do_align | net: {net_policy:?}");
-    println!(
-        "         | alignments: {:?} {:?}",
-        node.halign(),
-        node.valign()
-    );
-    println!("         | inside before: {inside:?}");
     if net_policy.width.slack_weight == 0 {
         let slack = (inside.width() - net_policy.width.desired as i32).max(0);
         let hshrink = match node.halign() {
@@ -54,7 +47,6 @@ fn do_align(node: &LayoutNodeAccess, mut inside: PixelBox) -> PixelBox {
         };
         inside = inside.inner_box(vshrink);
     }
-    println!("         | inside after:  {inside:?}");
     inside
 }
 

+ 1 - 1
src/layout/arr/container.rs

@@ -10,7 +10,7 @@ pub struct ContainerArrangement;
 impl ArrangementCalculator for ContainerArrangement {
     fn arrange_step(&self, node: LayoutNodeAccess) -> SizePolicy2D {
         let Some(child) = node.child(0) else {
-            return (node.width_policy(), node.height_policy()).into();
+            return SizePolicy2D::new(node.width_policy(), node.height_policy()) + node.margin_as_policy();
         };
         SizePolicy2D::new(
             node.width_policy.max_preserve_slack(child.width_policy()),

+ 1 - 1
src/layout/arr/line.rs

@@ -23,7 +23,7 @@ impl LineArrangement {
 impl ArrangementCalculator for LineArrangement {
     fn arrange_step(&self, node: LayoutNodeAccess) -> SizePolicy2D {
         if node.child_len() == 0 {
-            return (node.width_policy, node.height_policy).into();
+            return SizePolicy2D::new(node.width_policy, node.height_policy) + node.margin_as_policy();
         }
 
         let child_policies = node

+ 1 - 12
src/layout/arr/table.rs

@@ -18,17 +18,6 @@ struct TableState {
 }
 
 impl TableArrangement {
-    fn table_size(&self, node: &LayoutNodeAccess) -> Option<TableSize> {
-        let mut max_point = Some(TableCell::zero());
-        for ch in node.child_iter() {
-            if let Some(cell) = ch.table_cell() {
-                let mp = max_point.unwrap_or_default();
-                max_point = Some(TableCell::new(mp.x.max(cell.x), mp.y.max(cell.y)));
-            }
-        }
-        max_point.and_then(|p| Some(TableSize::new(p.x + 1, p.y + 1)))
-    }
-
     fn build_table_state(&self, node: &LayoutNodeAccess) -> TableState {
         let mut tstate = TableState {
             row_policies: vec![],
@@ -63,7 +52,7 @@ impl ArrangementCalculator for TableArrangement {
 
         // if there are no table cells...
         if tstate.row_policies.len() == 0 || tstate.col_policies.len() == 0 {
-            return (node.width_policy(), node.height_policy()).into();
+            return SizePolicy2D::new(node.width_policy(), node.height_policy()) + node.margin_as_policy();
         }
 
         let net_rows = tstate

+ 2 - 2
src/widget.rs

@@ -13,7 +13,7 @@ pub use label::Label;
 mod frame;
 pub use frame::Frame;
 mod group;
-pub use group::PlainGroup;
+pub use group::{PlainGroup,FormGroup};
 mod button;
 pub use button::Button;
 mod spacer;
@@ -90,4 +90,4 @@ pub trait WidgetExt<C: Component>: Widget<C> {
     }
 }
 
-impl<C: Component, W: Widget<C>> WidgetExt<C> for W {}
+impl<C: Component, W: ?Sized + Widget<C>> WidgetExt<C> for W {}

+ 7 - 0
src/widget/button.rs

@@ -39,6 +39,7 @@ impl<C: Component> Button<C> {
             })
             .set_valign(VerticalAlignment::Centre)
             .set_halign(HorizontalAlignment::Centre);
+        *layout.margin_mut() = PixelSideOffsets::new_all_same(uih.theme().border_width as i32);
         Self {
             layout,
             label: Label::new(uih),
@@ -47,6 +48,12 @@ impl<C: Component> Button<C> {
         }
     }
 
+    pub fn new_with_label(uih: &UIHandle, label: &str) -> Self {
+        let mut r = Self::new(uih);
+        r.set_label(label);
+        r
+    }
+
     pub fn set_label(&mut self, label: &str) {
         self.label.set_text(label);
     }

+ 1 - 1
src/widget/frame.rs

@@ -36,7 +36,7 @@ impl<C: Component> Frame<C> {
     }
 
     pub fn wrap_widget<W: 'static + Widget<C>>(child: W) -> Self {
-        let mut nnode = LayoutNode::new(child.layout_node().cache().clone());
+        let mut nnode = child.layout_node().make_new_node();
         nnode
             .set_arrangement(ChildArrangement::Container)
             .set_height_policy(SizePolicy::expanding(1))

+ 68 - 15
src/widget/group.rs

@@ -1,7 +1,9 @@
+use kahlo::math::PixelSideOffsets;
+
 use crate::{
     component::Component,
     input::InputState,
-    layout::{ChildArrangement, LayoutNode, LayoutNodeAccess, LayoutNodeContainer, SizePolicy},
+    layout::{ChildArrangement, LayoutNode, LayoutNodeAccess, LayoutNodeContainer, SizePolicy, TableCell, HorizontalAlignment},
     theme::Theme,
     ui::UIHandle,
     widget::Widget,
@@ -87,48 +89,99 @@ impl<C: Component> Widget<C> for PlainGroup<C> {
     }
 }
 
-use super::Label;
+use super::{Label, WidgetExt};
 
 pub struct FormGroup<C: Component> {
+    label_margin: PixelSideOffsets,
     lnode: LayoutNode,
-    labelnode: LayoutNode,
-    widgetnode: LayoutNode,
     labels: Vec<Label<C>>,
     widgets: Vec<Box<dyn Widget<C>>>,
 }
 
-struct LabelContainer<'l, C: Component>(&'l FormGroup<C>);
-struct WidgetContainer<'l, C: Component>(&'l FormGroup<C>);
+impl<C: Component> FormGroup<C> {
+    pub fn new(uih: &UIHandle) -> Self {
+        let mut lnode = uih.new_layout_node();
+        lnode.set_arrangement(ChildArrangement::Table);
+        Self {
+            label_margin: PixelSideOffsets::new(1, 5, 1, 5),
+            lnode,
+            labels: vec![],
+            widgets: vec![],
+        }
+    }
+
+    pub fn label_margin(&self) -> PixelSideOffsets {
+        self.label_margin
+    }
+
+    pub fn set_label_margin(&mut self, margin: PixelSideOffsets) {
+        for lbl in self.labels.iter_mut() {
+            lbl.set_margins(margin);
+        }
+        self.label_margin = margin;
+    }
+
+    pub fn add_widget(&mut self, label: &str, mut widget: Box<dyn Widget<C>>) {
+        widget.set_table_cell(TableCell::new(1, self.labels.len()));
+        self.widgets.push(widget);
+        self.labels.push(
+            Label::new_with_node_and_text(self.lnode.make_new_node(), label)
+                .with_table_cell(TableCell::new(0, self.labels.len()))
+                .with_margins(self.label_margin)
+                .with_halign(HorizontalAlignment::Right)
+        );
+    }
+}
 
 impl<C: Component> LayoutNodeContainer for FormGroup<C> {
     fn layout_node(&self) -> &LayoutNode {
         &self.lnode
     }
     fn layout_child(&self, ndx: usize) -> Option<LayoutNodeAccess<'_>> {
-        match ndx {
-            0 => todo!(), // Some(LayoutNodeAccess::new(LabelContainer(self))),
-            1 => todo!(),
-            _ => None,
+        if ndx < self.labels.len() {
+            Some(self.labels[ndx].layout_node())
+        } else if ndx < (self.labels.len() + self.widgets.len()) {
+            Some(self.widgets[ndx-self.labels.len()].layout_node())
+        } else {
+            None
         }
     }
     fn layout_child_count(&self) -> usize {
-        2
+        self.labels.len() + self.widgets.len()
     }
 }
 
 impl<C: Component> Widget<C> for FormGroup<C> {
     fn layout_node(&self) -> LayoutNodeAccess {
-        todo!()
+        LayoutNodeAccess::new(self)
     }
+
     fn layout_node_mut(&mut self) -> &mut LayoutNode {
-        todo!()
+        &mut self.lnode
     }
+
     fn poll(
         &mut self,
         uih: &mut UIHandle,
         input_state: Option<&InputState>,
     ) -> Vec<<C as Component>::Msg> {
-        todo!()
+        let mut res = vec![];
+        for lbl in self.labels.iter_mut() {
+            res.append(&mut lbl.poll(uih, input_state));
+        }
+        for widget in self.widgets.iter_mut() {
+            res.append(&mut widget.poll(uih, input_state));
+        }
+
+        res
+    }
+
+    fn render(&self, theme: &Theme, target: &mut kahlo::BitmapMut<RenderFormat>) {
+        for lbl in self.labels.iter() {
+            lbl.render(theme, target);
+        }
+        for widget in self.widgets.iter() {
+            widget.render(theme, target);
+        }
     }
-    fn render(&self, theme: &Theme, target: &mut kahlo::BitmapMut<RenderFormat>) {}
 }

+ 14 - 8
src/widget/label.rs

@@ -20,22 +20,27 @@ pub struct Label<C: Component> {
 }
 
 impl<C: Component> Label<C> {
-    pub fn new(uih: &UIHandle) -> Self {
-        let mut node = uih.new_layout_node();
-        node.set_width_policy(SizePolicy::expanding(0))
-            .set_height_policy(SizePolicy::fixed(uih.theme().ui_font_size.ceil() as usize));
+    fn construct(mut node: LayoutNode, text: Option<&str>) -> Self {
+        node.set_width_policy(SizePolicy::fixed(0))
+            .set_height_policy(SizePolicy::fixed(0));
         Self {
             lnode: LeafLayoutNode::new(node),
-            text: None,
+            text: text.map(ToString::to_string),
             rendered: None.into(),
             _ghost: Default::default(),
         }
     }
 
+    pub fn new(uih: &UIHandle) -> Self {
+        Self::construct(uih.new_layout_node(), None)
+    }
+
     pub fn new_with_text(uih: &UIHandle, text: &str) -> Self {
-        let mut r = Self::new(uih);
-        r.set_text(text);
-        r
+        Self::construct(uih.new_layout_node(), Some(text))
+    }
+
+    pub fn new_with_node_and_text(node: LayoutNode, text: &str) -> Self {
+        Self::construct(node, Some(text))
     }
 
     pub fn set_text(&mut self, text: &str) {
@@ -93,6 +98,7 @@ impl<C: Component> Widget<C> for Label<C> {
             return;
         };
         let render_location = self.layout_node().render_area().unwrap();
+        let render_location = render_location.inner_box(self.lnode.margin());
 
         surface.fill_region_masked(
             &rendered,