use crate::color::Color;
use crate::views::{self, ViewHandle};
pub struct View {
pub(crate) handle: ViewHandle,
}
impl View {
pub fn new(handle: ViewHandle) -> Self {
Self { handle }
}
pub fn handle(&self) -> &ViewHandle {
&self.handle
}
pub fn into_handle(self) -> ViewHandle {
self.handle
}
}
mod accessibility;
mod generated;
pub mod style;
mod appearance;
mod controls;
mod interaction;
mod layout;
mod navigation;
mod system;
mod text;
impl std::fmt::Debug for View {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "View({:?})", self.handle)
}
}
pub trait IntoView {
fn into_view(self) -> View;
}
impl IntoView for View {
fn into_view(self) -> View {
self
}
}
impl IntoView for ViewHandle {
fn into_view(self) -> View {
View::new(self)
}
}
impl IntoView for &str {
fn into_view(self) -> View {
text(self)
}
}
impl IntoView for String {
fn into_view(self) -> View {
text(&self)
}
}
impl IntoView for &String {
fn into_view(self) -> View {
text(self)
}
}
macro_rules! into_view_via_display {
($($t:ty),*) => {
$(
impl IntoView for $t {
fn into_view(self) -> View {
text(&self.to_string())
}
}
)*
};
}
into_view_via_display!(i32, i64, u32, u64, usize, f32, f64, bool);
impl<T: IntoView> IntoView for Option<T> {
fn into_view(self) -> View {
match self {
Some(v) => v.into_view(),
None => empty(),
}
}
}
pub fn text(s: &str) -> View {
View::new(views::text(s))
}
pub fn color(c: Color) -> View {
View::new(views::color(c.r, c.g, c.b, c.a))
}
pub fn spacer() -> View {
View::new(views::spacer())
}
pub fn divider() -> View {
View::new(views::divider())
}
pub fn empty() -> View {
View::new(views::empty_view())
}
pub fn shape(s: &views::CustomShape) -> View {
View::new(views::custom_shape(s))
}
#[cfg(target_os = "macos")]
pub fn ns_view(rep: &views::NSViewRep) -> View {
View::new(views::ns_view_representable(rep))
}
pub fn scroll_reader<V: IntoView>(
content: impl Fn(views::ScrollProxy) -> V + 'static,
) -> View {
View::new(views::scroll_view_reader(move |proxy| {
content(proxy).into_view().into_handle()
}))
}
pub fn content_unavailable(title: &str, system_image: &str, description: Option<&str>) -> View {
View::new(views::content_unavailable(title, system_image, description))
}
pub fn image(system_name: &str) -> View {
View::new(views::system_image(system_name))
}
pub fn vstack(children: &[View]) -> View {
let handles: Vec<ViewHandle> = children.iter().map(|v| v.handle.clone()).collect();
View::new(views::vstack(&handles))
}
fn handles(children: &[View]) -> Vec<ViewHandle> {
children.iter().map(|v| v.handle.clone()).collect()
}
pub fn async_image(url: Option<&str>, scale: f64) -> View {
View::new(views::async_image(url, scale))
}
pub fn canvas() -> View {
View::new(views::canvas())
}
pub fn progress_indeterminate() -> View {
View::new(views::progress_indeterminate())
}
pub fn text_verbatim(s: &str) -> View {
View::new(views::text_verbatim(s))
}
pub fn equatable_text(text: &str) -> View {
View::new(views::equatable_text(text))
}
pub fn table(rows: &[views::Orientation], title: &str, cell: &str) -> View {
View::new(views::table(rows, title, cell))
}
pub fn tuple_view(children: &[View]) -> View {
View::new(views::tuple_view(&handles(children)))
}
pub fn vstack_aligned(children: &[View], alignment: views::HorizontalAlignment) -> View {
View::new(views::vstack_aligned(&handles(children), alignment))
}
pub fn vstack_aligned_spacing(
children: &[View],
alignment: views::HorizontalAlignment,
spacing: Option<f64>,
) -> View {
View::new(views::vstack_aligned_spacing(&handles(children), alignment, spacing))
}
pub fn hstack_aligned(children: &[View], alignment: views::VerticalAlignment) -> View {
View::new(views::hstack_aligned(&handles(children), alignment))
}
pub fn hstack_aligned_spacing(
children: &[View],
alignment: views::VerticalAlignment,
spacing: Option<f64>,
) -> View {
View::new(views::hstack_aligned_spacing(&handles(children), alignment, spacing))
}
pub fn section(content: View) -> View {
View::new(views::section_content(content.handle()))
}
pub fn section_titled(header: View, content: View) -> View {
View::new(views::section(header.handle(), content.handle()))
}
pub fn section_footer(footer: View, content: View) -> View {
View::new(views::section_footer(footer.handle(), content.handle()))
}
pub fn section_titled_footer(header: View, footer: View, content: View) -> View {
View::new(views::section_header_footer(
header.handle(),
footer.handle(),
content.handle(),
))
}
pub fn section_expanded(
is_expanded: &views::BoundState<bool>,
header: View,
content: View,
) -> View {
View::new(views::section_expanded(
is_expanded,
header.handle(),
content.handle(),
))
}
pub fn tab(title: &str, system_image: &str, content: View) -> View {
View::new(views::tab(title, system_image, content.handle()))
}
pub fn tab_view_tabs(tabs: Vec<View>) -> View {
let handles: Vec<_> = tabs.into_iter().map(|t| t.into_handle()).collect();
View::new(views::tab_view_tabs(&handles))
}
pub fn stepper_actions(
label: &str,
on_increment: Option<Box<dyn Fn()>>,
on_decrement: Option<Box<dyn Fn()>>,
) -> View {
View::new(views::stepper_actions(label, on_increment, on_decrement))
}
pub fn toggle_labelled(label: View, state: &views::BoundState<bool>) -> View {
View::new(views::toggle_labelled(label.handle(), state))
}
pub fn date_picker_range(
title: &str,
state: &views::BoundState<f64>,
lower: f64,
upper: f64,
) -> View {
View::new(views::date_picker_range(title, state, lower, upper))
}
pub fn toggle_sources(label: &str, states: &[&views::BoundState<bool>]) -> View {
View::new(views::toggle_sources(label, states))
}
pub fn group(content: View) -> View {
View::new(views::group(content.handle()))
}
pub fn control_group(content: View) -> View {
View::new(views::control_group(content.handle()))
}
pub fn menu(label: &str, content: View) -> View {
View::new(views::menu(label, content.handle()))
}
pub fn link(title: &str, destination: &str) -> View {
View::new(views::link(title, destination))
}
pub fn share_link(url: &str) -> View {
View::new(views::share_link_url(url))
}
pub fn gauge(value: f64, lower: f64, upper: f64, label: View) -> View {
View::new(views::gauge(value, lower, upper, label.handle()))
}
pub fn view_that_fits(content: View, axes: &[views::AxisSet]) -> View {
View::new(views::view_that_fits(content.handle(), axes))
}
pub fn navigation_split_view(sidebar: View, detail: View) -> View {
View::new(views::navigation_split_view(sidebar.handle(), detail.handle()))
}
pub fn navigation_split_view3(sidebar: View, content: View, detail: View) -> View {
View::new(views::navigation_split_view3(
sidebar.handle(),
content.handle(),
detail.handle(),
))
}
pub fn outline_group(root: i64, leaf: View) -> View {
View::new(views::outline_group(root, leaf.handle()))
}
pub fn text_editor(value: &str) -> View {
View::new(views::text_editor(value))
}
#[cfg(not(target_os = "macos"))]
pub fn edit_button() -> View {
View::new(views::edit_button())
}
pub fn button_labelled(label: View, action: impl Fn() + 'static) -> View {
View::new(views::button_labelled(label.handle(), action))
}
pub fn vstack_spacing(children: &[View], spacing: f64) -> View {
View::new(views::vstack_spacing(&handles(children), spacing))
}
pub fn hstack_spacing(children: &[View], spacing: f64) -> View {
View::new(views::hstack_spacing(&handles(children), spacing))
}
pub fn zstack_aligned(children: &[View], alignment: views::Alignment) -> View {
View::new(views::zstack_aligned(&handles(children), alignment))
}
pub fn hstack(children: &[View]) -> View {
let handles: Vec<ViewHandle> = children.iter().map(|v| v.handle.clone()).collect();
View::new(views::hstack(&handles))
}
pub fn zstack(children: &[View]) -> View {
let handles: Vec<ViewHandle> = children.iter().map(|v| v.handle.clone()).collect();
View::new(views::zstack(&handles))
}
pub fn button(label: &str, action: impl Fn() + 'static) -> View {
View::new(views::button(label, action))
}
pub fn toggle(label: &str, is_on: bool) -> View {
View::new(views::toggle(label, is_on))
}
pub fn toggle_bound(label: &str, state: &views::BoundState<bool>) -> View {
View::new(views::toggle_bound(label, state))
}
pub fn textfield(placeholder: &str, value: &str) -> View {
View::new(views::textfield(placeholder, value))
}
pub fn textfield_bound(placeholder: &str, state: &views::BoundState<String>) -> View {
View::new(views::textfield_bound(placeholder, state))
}
pub fn textfield_axis(
placeholder: &str,
state: &views::BoundState<String>,
axis: views::Axis,
) -> View {
View::new(views::textfield_axis(placeholder, state, axis))
}
pub fn securefield(placeholder: &str, value: &str) -> View {
View::new(views::securefield(placeholder, value))
}
pub fn securefield_bound(placeholder: &str, state: &views::BoundState<String>) -> View {
View::new(views::securefield_bound(placeholder, state))
}
pub fn slider(value: f64, lower: f64, upper: f64) -> View {
View::new(views::slider(value, lower, upper))
}
pub fn slider_bound(state: &views::BoundState<f64>, lower: f64, upper: f64) -> View {
View::new(views::slider_bound(state, lower, upper))
}
pub fn slider_step_bound(
state: &views::BoundState<f64>,
lower: f64,
upper: f64,
step: f64,
) -> View {
View::new(views::slider_step_bound(state, lower, upper, step))
}
pub fn picker(title: &str, selection: i64, content: View) -> View {
View::new(views::picker(title, selection, content.handle()))
}
pub fn picker_bound(title: &str, state: &views::BoundState<i64>, content: View) -> View {
View::new(views::picker_bound(title, state, content.handle()))
}
pub fn date_picker(title: &str, interval: f64) -> View {
View::new(views::date_picker(title, interval))
}
pub fn date_picker_bound(title: &str, state: &views::BoundState<f64>) -> View {
View::new(views::date_picker_bound(title, state))
}
pub fn color_picker(title: &str, color: Color, supports_opacity: bool) -> View {
View::new(views::color_picker(title, color, supports_opacity))
}
pub fn color_picker_bound(
title: &str,
state: &views::BoundState<u64>,
supports_opacity: bool,
) -> View {
View::new(views::color_picker_bound(title, state, supports_opacity))
}
pub fn progress(value: f64, total: f64) -> View {
View::new(views::progress(value, total))
}
pub fn label(title: &str, system_image: &str) -> View {
View::new(views::label(title, system_image))
}
pub fn rectangle() -> View {
View::new(views::rectangle())
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum Transition {
Opacity,
Slide,
Scale { scale: f64, anchor: (f64, f64) },
ScaleWithOpacity { scale: f64, anchor: (f64, f64) },
ScaleDefault,
Identity,
}
pub struct Shape {
inner: views::Shape,
}
impl Shape {
pub fn fill(self, c: Color) -> View {
View::new(self.inner.fill(views::color_value_of(c)))
}
pub fn fill_system(self, c: views::SystemColor) -> View {
View::new(self.inner.fill(views::system_color_value(c)))
}
pub fn view(self) -> View {
View::new(self.inner.fill(views::color_value_of(Color::BLACK)))
}
pub fn stroke(self, c: Color, line_width: f64) -> View {
View::new(self.inner.stroke(views::color_value_of(c), line_width))
}
pub fn stroke_system(self, c: views::SystemColor, line_width: f64) -> View {
View::new(self.inner.stroke(views::system_color_value(c), line_width))
}
pub fn fill_then_stroke(self, fill: Color, stroke: Color, line_width: f64) -> View {
use crate::mangle;
let shape = self.inner.ty().clone();
let color = mangle::ty("Color");
let filled = mangle::ty("_ShapeView").of(vec![shape.clone(), color.clone()]);
let result = mangle::ty("StrokeShapeView").of(vec![shape, color, filled]).mangle();
View::new(views::fill_then_stroke(
&self.inner,
views::color_value_of(fill),
views::color_value_of(stroke),
line_width,
result.as_bytes(),
))
}
pub fn fill_then_stroke_border(self, fill: Color, border: Color, line_width: f64) -> View {
use crate::mangle;
let shape = self.inner.ty().clone();
let color = mangle::ty("Color");
let filled = mangle::ty("_ShapeView").of(vec![shape.clone(), color.clone()]);
let result = mangle::ty("StrokeBorderShapeView")
.of(vec![shape, color, filled])
.mangle();
View::new(views::fill_then_stroke_border(
&self.inner,
views::color_value_of(fill),
views::color_value_of(border),
line_width,
result.as_bytes(),
))
}
pub fn stroke_default(self, line_width: f64) -> View {
View::new(self.inner.stroke_default(line_width))
}
pub fn trim(self, from: f64, to: f64) -> Self {
Self { inner: self.inner.trim(from, to) }
}
pub fn inset(self, amount: f64) -> Self {
Self { inner: self.inner.inset(amount) }
}
pub fn offset(self, x: f64, y: f64) -> Self {
Self { inner: self.inner.offset(x, y) }
}
pub fn rotation(self, degrees: f64) -> Self {
Self { inner: self.inner.rotation(degrees) }
}
}
impl IntoView for Shape {
fn into_view(self) -> View {
self.view()
}
}
pub struct Image {
inner: views::Image,
}
impl Image {
pub fn resizable(self) -> Self {
Self { inner: self.inner.resizable(views::ResizingMode::Stretch) }
}
pub fn resizable_mode(self, mode: views::ResizingMode) -> Self {
Self { inner: self.inner.resizable(mode) }
}
pub fn rendering_mode(self, mode: Option<views::TemplateRenderingMode>) -> Self {
Self { inner: self.inner.rendering_mode(mode) }
}
pub fn symbol_rendering_mode(self, mode: Option<views::SymbolRenderingMode>) -> Self {
Self { inner: self.inner.symbol_rendering_mode(mode) }
}
pub fn antialiased(self, on: bool) -> Self {
Self { inner: self.inner.antialiased(on) }
}
pub fn view(self) -> View {
View::new(self.inner.view())
}
}
impl IntoView for Image {
fn into_view(self) -> View {
self.view()
}
}
pub fn system_image(name: &str) -> Image {
Image { inner: views::Image::system(name) }
}
pub fn named_image(name: &str) -> Image {
Image { inner: views::Image::named(name) }
}
pub fn circle_shape() -> Shape {
Shape { inner: views::Shape::circle() }
}
pub fn capsule_shape() -> Shape {
Shape { inner: views::Shape::capsule() }
}
pub fn rectangle_shape() -> Shape {
Shape { inner: views::Shape::rectangle() }
}
pub fn rounded_rectangle_shape(radius: f64) -> Shape {
Shape { inner: views::Shape::rounded_rectangle(radius, true) }
}
pub fn rounded_rectangle_shape_styled(radius: f64, continuous: bool) -> Shape {
Shape { inner: views::Shape::rounded_rectangle(radius, continuous) }
}
pub fn ellipse_shape() -> Shape {
Shape { inner: views::Shape::ellipse() }
}
pub fn circle() -> View {
View::new(views::circle())
}
pub fn ellipse() -> View {
View::new(views::ellipse())
}
pub fn capsule() -> View {
View::new(views::capsule())
}
pub fn rounded_rectangle(radius: f64) -> View {
View::new(views::rounded_rectangle(radius))
}
pub fn for_each<R: views::ForEachRange, V: IntoView>(
range: R,
builder: impl Fn(i64) -> V + 'static,
) -> View {
View::new(views::for_each(range, move |i| builder(i).into_view().into_handle()))
}
pub fn scroll_view(content: View) -> View {
View::new(views::scroll(content.handle()))
}
pub fn for_each_items<T: Clone + 'static, V: IntoView>(
items: &[T],
builder: impl Fn(&T) -> V + 'static,
) -> View {
View::new(views::for_each_items(items, move |t| builder(t).into_view().into_handle()))
}
pub fn environment_reader<V: IntoView>(
builder: impl Fn(views::EnvironmentValues) -> V + 'static,
) -> View {
View::new(views::environment_reader(move |env| {
builder(env).into_view().into_handle()
}))
}
pub fn geometry_reader<V: IntoView>(builder: impl Fn(f64, f64) -> V + 'static) -> View {
View::new(views::geometry_reader(move |w, h| builder(w, h).into_view().into_handle()))
}
pub fn navigation_link_labelled(label: View, destination: View) -> View {
View::new(views::navigation_link_labelled(label.handle(), destination.handle()))
}
pub fn navigation_link(label: &str, destination: View) -> View {
View::new(views::navigation_link(label, destination.handle()))
}
pub fn conditional(flag: bool, if_true: View, if_false: View) -> View {
View::new(views::conditional(flag, if_true.handle(), if_false.handle()))
}
pub fn timeline<V: IntoView>(builder: impl Fn() -> V + 'static) -> View {
View::new(views::timeline(move || builder().into_view().into_handle()))
}
pub fn list_identified<V: IntoView>(ids: &[i64], builder: impl Fn(i64) -> V + 'static) -> View {
View::new(views::list_identified(ids, move |id| builder(id).into_view().into_handle()))
}
pub fn list_identified_str<V: IntoView>(
ids: &[&str],
builder: impl Fn(&str) -> V + 'static,
) -> View {
View::new(views::list_identified_str(ids, move |id| {
builder(id).into_view().into_handle()
}))
}
pub fn list(child: View) -> View {
View::new(views::list(child.handle()))
}
pub fn form(child: View) -> View {
View::new(views::form(child.handle()))
}
pub fn group_box(child: View) -> View {
View::new(views::group_box(child.handle()))
}
pub fn tab_view(child: View) -> View {
View::new(views::tab_view(child.handle()))
}
pub fn navigation_stack(child: View) -> View {
View::new(views::navigation_stack(child.handle()))
}
pub fn disclosure_group(label: &str, child: View) -> View {
View::new(views::disclosure_group(label, child.handle()))
}
pub fn laid_out(layout: &views::CustomLayout, children: &[View]) -> View {
View::new(views::custom_layout(layout, &handles(children)))
}
pub fn layout(
size: impl Fn(&views::Subviews<'_>, views::Proposal) -> (f64, f64) + Send + Sync + 'static,
place: impl Fn(&views::Subviews<'_>, views::Rect, views::Proposal) + Send + Sync + 'static,
) -> views::CustomLayout {
views::layout(size, place)
}
pub fn grid(rows: &[View]) -> View {
let handles: Vec<ViewHandle> = rows.iter().map(|v| v.handle.clone()).collect();
View::new(views::grid(&handles))
}
pub fn grid_row(children: &[View]) -> View {
let handles: Vec<ViewHandle> = children.iter().map(|v| v.handle.clone()).collect();
View::new(views::grid_row(&handles))
}
pub fn grid_row_aligned(children: &[View], alignment: views::VerticalAlignment) -> View {
let handles: Vec<ViewHandle> = children.iter().map(|v| v.handle.clone()).collect();
View::new(views::grid_row_aligned(&handles, alignment))
}
pub fn stepper(label: &str, value: f64, lower: f64, upper: f64, step: f64) -> View {
View::new(views::stepper(label, value, lower, upper, step))
}
pub fn stepper_bound(
label: &str,
state: &views::BoundState<f64>,
lower: f64,
upper: f64,
step: f64,
) -> View {
View::new(views::stepper_bound(label, state, lower, upper, step))
}
pub fn lazy_vstack(children: &[View]) -> View {
let handles: Vec<ViewHandle> = children.iter().map(|v| v.handle.clone()).collect();
View::new(views::lazy_vstack(&handles))
}
pub fn lazy_vstack_aligned(children: &[View], alignment: views::HorizontalAlignment) -> View {
let handles: Vec<ViewHandle> = children.iter().map(|v| v.handle.clone()).collect();
View::new(views::lazy_vstack_aligned(&handles, alignment))
}
pub fn lazy_hstack(children: &[View]) -> View {
let handles: Vec<ViewHandle> = children.iter().map(|v| v.handle.clone()).collect();
View::new(views::lazy_hstack(&handles))
}
pub fn lazy_hstack_aligned(children: &[View], alignment: views::VerticalAlignment) -> View {
let handles: Vec<ViewHandle> = children.iter().map(|v| v.handle.clone()).collect();
View::new(views::lazy_hstack_aligned(&handles, alignment))
}