Skip to main content

View

Struct View 

Source
pub struct View<'a, Msg> { /* private fields */ }
Expand description

Collects the nodes of one container while an application’s view runs.

Implementations§

Source§

impl<'a, Msg: 'static> View<'a, Msg>

Source

pub fn env(&self) -> &Env

The environment: theme, icons, language and keymap.

Source

pub fn size(&self) -> Size

The room the application is drawing into: the whole terminal, in columns and rows.

This is the value for an application’s own layout decision, such as “below 48 columns, fold the three columns into one”: if ui.size().width < 48 { .. } else { .. } in view.

The application’s view fills the screen, so at the top of view this is exactly the area it lays out. Every nested builder reports the same value: the children of column, row, stack, page and add_with, the parts of an AppShell, SidePanel, Splitter or Popover, the content of a Modal or other layer. The view is built before layout divides the screen, so a container’s own share is not known yet while its children are being built; the number never pretends to be that share. A widget that adapts to its own rectangle (a column that shortens its labels) does so in measure and paint, which receive it.

Reading it performs no I/O: it is the size of the frame the framework is about to draw, which it already holds. After a terminal resize the next frame reports the new size, and Harness::resize does the same in tests.

Source

pub fn idle_for(&self) -> Duration

How long no input has reached this terminal: the time since the last key, mouse event or paste the runtime received, or since the application started when none came yet.

Everything the user does in this terminal counts: a key going down, repeating or coming up, a mouse button, the wheel, the pointer moving over the window, a paste, and the end of a Handoff, because the program that had the terminal was being used meanwhile. A terminal resize does not count: a window manager or a monitor change resizes a window nobody is sitting at. Messages, background work and timers do not count either; they are the application, not the user. Other programs and other terminals are out of reach: this is idleness here, not idleness of the machine.

Reading the value keeps it current on screen: while view reads it, the runtime draws again each time it passes a whole second, and stops once view no longer reads it. A view that shows minutes therefore redraws once a second while it shows them; one that only needs to act after a silence uses View::on_idle, which wakes the application once, at that moment, without drawing in between.

Harness::advance moves it forward in tests, and every simulated input starts it again from zero.

Source

pub fn on_idle( &mut self, after: Duration, message: impl Fn(bool) -> Msg + 'static, )

Tells the application when no input has arrived for after, and when input comes back.

message(true) is delivered once, at the moment the silence reaches after: the runtime wakes for it even when nothing else happens, and does not draw in between. The first input afterwards delivers message(false), before that input reaches any widget, and starts the next wait. What counts as input is listed at View::idle_for.

use std::time::Duration;

use qframe::prelude::*;

#[derive(Default)]
struct Focus {
    away: bool,
}

impl App for Focus {
    type Msg = bool;
    fn update(&mut self, away: bool) -> Command<bool> {
        self.away = away;
        Command::none()
    }
    fn view(&self, ui: &mut View<'_, bool>) {
        ui.on_idle(Duration::from_secs(300), |away| away);
        ui.add(Text::new(if self.away { "away" } else { "working" }));
    }
}

let mut app = Harness::new(Focus::default(), 20, 1);
app.advance(Duration::from_secs(299));
assert!(app.screen().contains("working"));
app.advance(Duration::from_secs(1));
assert!(app.screen().contains("away"));
app.press("x");
assert!(app.screen().contains("working"));

Declare the watch in every frame it should stay active, like a widget: the runtime answers the watches of the latest frame. One that is no longer declared is not told the silence ended. A watch declared when the silence has already lasted after is told at once. Watches with different after are independent, so an application can dim the screen after one minute and pause a timer after five.

Source

pub fn add<W: Widget<Msg>>(&mut self, widget: W) -> NodeMut<'_, Msg>

Adds a widget.

Source

pub fn add_with<W: Container<Msg>>( &mut self, widget: W, build: impl FnOnce(&mut View<'_, Msg>), ) -> NodeMut<'_, Msg>

Adds a widget that contains other widgets, built by build.

Source

pub fn column( &mut self, build: impl FnOnce(&mut View<'_, Msg>), ) -> NodeMut<'_, Msg>

Adds a column whose children are built by build.

Source

pub fn row( &mut self, build: impl FnOnce(&mut View<'_, Msg>), ) -> NodeMut<'_, Msg>

Adds a row whose children are built by build.

Source

pub fn stack( &mut self, build: impl FnOnce(&mut View<'_, Msg>), ) -> NodeMut<'_, Msg>

Adds a stack: children are drawn on top of each other in the same area, later ones on top.

Source

pub fn place( &mut self, rect: Rect, build: impl FnOnce(&mut View<'_, Msg>), ) -> NodeMut<'_, Msg>

Adds children at rect, for a stack whose children sit where the application says, such as windows on a desktop.

Inside a stack, rect counts from the stack’s top left corner, whatever the stack’s alignment: the children fill it, drawn on top of each other. A rectangle may reach past the stack on any side, also to negative coordinates; what lies outside is not drawn and takes no pointer. Children added later are drawn on top and get the pointer first where they overlap, so the order of the calls is the stacking order. A placed child may draw one cell past its right and bottom edges, where a window drops its shadow; that cell never takes the pointer. Outside a stack only the size of rect counts.

Name every placed child whose position in the stack can change, as when a clicked window comes to the front: ui.place(rect, ..).id("htop"). Its state, and a drag it is in the middle of, follow the name.

use qframe::prelude::*;

struct Desk;

impl App for Desk {
    type Msg = ();
    fn update(&mut self, (): ()) -> Command<()> {
        Command::none()
    }
    fn view(&self, ui: &mut View<'_, ()>) {
        ui.stack(|ui| {
            ui.place(Rect::new(2, 1, 6, 1), |ui| {
                ui.add(Text::new("below"));
            })
            .id("first");
            ui.place(Rect::new(6, 1, 5, 1), |ui| {
                ui.add(Text::new("above"));
            })
            .id("second");
        })
        .fill();
    }
}

let app = Harness::new(Desk, 12, 2);
assert_eq!(app.screen(), "\n  beloabove\n");
Source

pub fn page( &mut self, key: impl Into<String>, build: impl FnOnce(&mut View<'_, Msg>), ) -> NodeMut<'_, Msg>

Adds a column that remembers its widgets’ state (focus, scroll, cursors) while it is not shown. Give every page of a router its own key.

Source

pub fn map<Inner: 'static>( &mut self, map: impl Fn(Inner) -> Msg + 'static, build: impl FnOnce(&mut View<'_, Inner>), ) -> NodeMut<'_, Msg>

Adds a column whose children are built by build with messages of their own type Inner, each converted by map on its way to the application. A screen with its own messages writes its view for them, and the application places it in one line:

use qframe::prelude::*;

mod search {
    use qframe::prelude::*;

    #[derive(Clone)]
    pub enum Msg {
        Run,
    }

    pub fn view(ui: &mut View<'_, Msg>) {
        ui.add(Button::new("Search").on_press(Msg::Run));
    }
}

enum Msg {
    Search(search::Msg),
}

fn view(ui: &mut View<'_, Msg>) {
    ui.map(Msg::Search, search::view).fill();
}

Everything the screen does inside arrives converted: the messages of its widgets and handlers, the children of add_with and nested containers, layers such as a Modal and the widgets in them, overlays such as an open dropdown. Focus, memory and ids work as for any column; Command::map converts the commands the screen’s update returns the same way.

Source

pub fn spacer(&mut self) -> NodeMut<'_, Msg>

Adds empty space that takes the room left in a row or column.

Auto Trait Implementations§

§

impl<'a, Msg> !RefUnwindSafe for View<'a, Msg>

§

impl<'a, Msg> !Send for View<'a, Msg>

§

impl<'a, Msg> !Sync for View<'a, Msg>

§

impl<'a, Msg> !UnwindSafe for View<'a, Msg>

§

impl<'a, Msg> Freeze for View<'a, Msg>
where &'a mut Vec<Node<Msg>>: Freeze, &'a IdleScope<Msg>: Freeze,

§

impl<'a, Msg> Unpin for View<'a, Msg>
where &'a mut Vec<Node<Msg>>: Unpin, &'a IdleScope<Msg>: Unpin,

§

impl<'a, Msg> UnsafeUnpin for View<'a, Msg>
where &'a mut Vec<Node<Msg>>: UnsafeUnpin, &'a IdleScope<Msg>: UnsafeUnpin,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.