Skip to content

Commit

Permalink
Add geometry reader
Browse files Browse the repository at this point in the history
  • Loading branch information
fwcd committed Sep 9, 2024
1 parent 1de52d8 commit daaf51d
Show file tree
Hide file tree
Showing 9 changed files with 103 additions and 1 deletion.
21 changes: 21 additions & 0 deletions examples/geometry_reader.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#![feature(type_alias_impl_trait, impl_trait_in_assoc_type)]

use nuit::{prelude::*, GeometryReader, Insets, Text};

#[derive(Bind)]
struct GeometryReaderView;

impl View for GeometryReaderView {
type Body = impl View;

fn body(&self) -> Self::Body {
GeometryReader::new(|geometry| {
Text::new(format!("This view has width {} and height {}", geometry.width(), geometry.height()))
.padding(Insets::default())
})
}
}

fn main() {
nuit::run_app(GeometryReaderView);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ enum Event: Codable, Hashable {
case updateSliderValue(value: Double)
case updateNavigationPath(path: [Value])
case getNavigationDestination(value: Value)
case getGeometryReaderView(geometry: Geometry)
}
1 change: 1 addition & 0 deletions nuit-bridge-swiftui/Sources/NuitBridgeSwiftUI/Node.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ indirect enum Node: Codable, Hashable {
case group(children: [Identified<Node>])

// MARK: Layout
case geometryReader
case vStack(alignment: HorizontalAlignment, spacing: Double, wrapped: Identified<Node>)
case hStack(alignment: VerticalAlignment, spacing: Double, wrapped: Identified<Node>)
case zStack(alignment: Alignment, spacing: Double, wrapped: Identified<Node>)
Expand Down
6 changes: 6 additions & 0 deletions nuit-bridge-swiftui/Sources/NuitBridgeSwiftUI/NodeView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ struct NodeView: View {
}

// MARK: Layout
case .geometryReader:
GeometryReader { proxy in
if case let .node(node: node) = root.fire(event: .getGeometryReaderView(geometry: .init(proxy)), for: idPath) {
NodeView(node: node.value, idPath: idPath + [node.id])
}
}
case let .vStack(alignment: alignment, spacing: spacing, wrapped: wrapped):
VStack(alignment: .init(alignment), spacing: spacing) {
NodeView(node: wrapped.value, idPath: idPath + [wrapped.id])
Expand Down
57 changes: 57 additions & 0 deletions nuit-core/src/compose/view/layout/geometry_reader.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use std::marker::PhantomData;

use nuit_derive::Bind;

use crate::{Context, Event, EventResponse, Geometry, Id, IdPath, IdentifyExt, Node, View};

#[derive(Debug, Clone, PartialEq, Eq, Bind)]
pub struct GeometryReader<F, T> {
view_func: F,
phantom_view: PhantomData<T>,
}

impl<F, T> GeometryReader<F, T>
where
F: Fn(Geometry) -> T,
T: View,
{
#[must_use]
pub const fn new(view_func: F) -> Self {
Self {
view_func,
phantom_view: PhantomData,
}
}
}

impl<F, T> View for GeometryReader<F, T>
where
F: Fn(Geometry) -> T,
T: View,
{
fn fire(&self, event: &Event, event_path: &IdPath, context: &Context) -> EventResponse {
if let Some(head) = event_path.head() {
match head {
Id::Index(i) => panic!("Cannot fire event for child id {i} on GeometryReader"),
Id::String(geometry_json) => {
let geometry = serde_json::from_str(&geometry_json).expect("Could not deserialize geometry reader id");
let view = (self.view_func)(geometry);
view.fire(event, event_path.tail(), &context.child(geometry_json))
},
}
} else if let Event::GetGeometryReaderView { geometry } = event {
let geometry_json = serde_json::to_string(geometry).expect("Could not serialize geometry reader id");
let id = Id::string(geometry_json);

let view = (self.view_func)(*geometry);
let node = view.render(&context.child(id.clone())).identify(id);
EventResponse::Node { node }
} else {
EventResponse::default()
}
}

fn render(&self, _context: &Context) -> Node {
Node::GeometryReader {}
}
}
2 changes: 2 additions & 0 deletions nuit-core/src/compose/view/layout/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
mod geometry_reader;
mod list;
mod overlay;
mod stack;

pub use geometry_reader::*;
pub use list::*;
pub use overlay::*;
pub use stack::*;
5 changes: 4 additions & 1 deletion nuit-core/src/event/event.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use serde::{Serialize, Deserialize};
use serde_json::Value;

use crate::Id;
use crate::{Geometry, Id};

use super::GestureEvent;

Expand All @@ -20,6 +20,9 @@ pub enum Event {
UpdateNavigationPath { path: Vec<Value> },
GetNavigationDestination { value: Value },

// Layout
GetGeometryReaderView { geometry: Geometry },

// Lifecycle
Appear,
Disappear,
Expand Down
1 change: 1 addition & 0 deletions nuit-core/src/node/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub enum Node {
Group { children: Vec<Identified<Node>> },

// Layout
GeometryReader {},
VStack { alignment: HorizontalAlignment, spacing: f64, wrapped: Box<Identified<Node>> },
HStack { alignment: VerticalAlignment, spacing: f64, wrapped: Box<Identified<Node>> },
ZStack { alignment: Alignment, spacing: f64, wrapped: Box<Identified<Node>> },
Expand Down
10 changes: 10 additions & 0 deletions nuit-core/src/utils/geometry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,14 @@ impl Geometry {
pub const fn size(self) -> Vec2<f64> {
self.size
}

#[must_use]
pub const fn width(self) -> f64 {
self.size.x
}

#[must_use]
pub const fn height(self) -> f64 {
self.size.y
}
}

0 comments on commit daaf51d

Please sign in to comment.