Skip to main content

Appearance

Struct Appearance 

Source
pub struct Appearance { /* private fields */ }
Expand description

The appearance section of a settings page or a setup wizard: language, theme, icons and reduced motion as the ecosystem shares them, and the pillar, as rows of a SettingsList; and, where the application asks for its updates, the ecosystem’s update notice with updates.

Each shared row has a box under it, “In every Quvyta application”, checked while the application follows the ecosystem: a change then goes to the ecosystem’s shared file and every application that follows it changes too. Cleared, the change stays in the application’s own file. The pillar is the application’s own. The update notice is one switch for the whole ecosystem, kept in the shared file; see Ecosystem::update_notice. Its switch can be written in the background, so a settings page never waits for the disk; see updates_in_background. A change is applied at once and saved at once, each file read again right before it is written; see Ecosystem::set. When the QUVYTA_REDUCED_MOTION environment variable decides, the reduced motion row and its box are disabled and the row says why. Texts come from the framework’s language files.

use qframe::i18n::I18n;
use qframe::prelude::*;
use qframe::storage::{Ecosystem, Settings};
use qframe::widgets::{Appearance, AppearanceChange, SettingsList};

struct Code {
    settings: Settings,
    appearance: Appearance,
}

#[derive(Debug, Clone)]
enum Msg {
    Appearance(AppearanceChange),
}

impl App for Code {
    type Msg = Msg;
    fn update(&mut self, msg: Msg) -> Command<Msg> {
        match msg {
            Msg::Appearance(change) => self.appearance.update(change, &mut self.settings),
        }
    }
    fn view(&self, ui: &mut View<'_, Msg>) {
        SettingsList::show(ui, |list| self.appearance.section(list, Msg::Appearance));
    }
}

let ecosystem = Ecosystem::QUVYTA;
// An application passes `ecosystem.preferences("code", &i18n)`; the example stays in a folder of its own.
let preferences = ecosystem.preferences_in(&folder, "code", &I18n::builtin());
let appearance = Appearance::new(ecosystem, "code", preferences).in_folder(&folder);
let settings = Settings::open(folder.join("code.conf")).member_of(&ecosystem);
let mut app = Harness::new(Code { settings, appearance }, 60, 20);
assert!(app.screen().contains("In every Quvyta application"));

Implementations§

Source§

impl Appearance

Source

pub fn label(i18n: &I18n, key: Shared) -> String

The title the Appearance box gives the row of key, in the language of i18n, so an application that shows the same preference elsewhere, such as in a table of what follows the ecosystem, calls it by the same name.

Source

pub fn new( ecosystem: Ecosystem, app: impl Into<String>, preferences: Preferences, ) -> Self

The appearance of application app of ecosystem, starting from the preferences Ecosystem::preferences resolved for it. Changes are saved in the ecosystem’s folder.

Source

pub fn in_folder(self, folder: impl Into<PathBuf>) -> Self

Saves changes in folder as the ecosystem’s folder instead of this platform’s, for a test or a demo that must leave the user’s own files alone; see Ecosystem::set_in.

Source

pub fn without_saving(self) -> Self

Applies every change without writing a file: the shared preferences and the settings given to update take it, the screen shows it, and the files are left to whoever writes them later.

For the first step of a setup wizard, which writes both files only when the wizard finishes, so a wizard closed half-way leaves nothing behind.

Source

pub fn updates_in_background(self) -> Self

Has the update notice row write the shared file on a thread of its own, so a settings page never waits for the disk to turn the switch over: the switch shows the new value at once and the file takes it behind the screen. A file that cannot be written puts the switch back where the person left it and says so as an AppearanceSave, which the application shows as a toast.

The application hands every change to update_saving instead of update, and the outcome back to saved. Every other row is written as update writes it, on the thread that draws.

Source

pub fn preferences(&self) -> &Preferences

The shared preferences as they stand after the changes made so far.

Source

pub fn refresh(&mut self, preferences: Preferences)

Takes preferences resolved again after the files changed while the section is open, such as the ones App::preferences hears when another application switches the theme for the whole ecosystem. The rows then show the new values and the box under each shared row whether the application follows the ecosystem now, and the next change is saved where that box says.

Nothing is written and nothing is applied: the runtime has already switched the screen. What the person is doing on the section stays as it is: an open list stays open, and a reason a change could not be saved stays under its row until the next change.

Source

pub fn section<Msg: Clone + 'static>( &self, list: &mut SettingsRows<'_, Msg>, message: impl Fn(AppearanceChange) -> Msg + Clone + 'static, )

Adds an “Appearance” heading, the three shared rows, reduced motion with its box and the application’s own pillar to list.

Source

pub fn updates<Msg: Clone + 'static>( &self, list: &mut SettingsRows<'_, Msg>, message: impl Fn(AppearanceChange) -> Msg + Clone + 'static, )

Adds the ecosystem’s update notice switch to list, with the text saying what it asks and what it never sends: for an application that asks whether a newer version of itself is out (Command::check_for_update), right after section. The switch is the ecosystem’s, one for every application, kept in the shared file; see Ecosystem::update_notice. An application that never asks leaves the row out, so its settings offer nothing that does nothing there.

The change is written where the section is drawn, unless the application asked for updates_in_background.

Source

pub fn rows<Msg: Clone + 'static>( &self, list: &mut SettingsRows<'_, Msg>, message: impl Fn(AppearanceChange) -> Msg + Clone + 'static, )

Adds the three rows the ecosystem shares, language, theme and icons, each with its box, to list, without a heading and without the application’s own rows: what the first step of a setup wizard asks, on a page that names the section itself. Every change is sent as message.

Source

pub fn update<Msg: Send + 'static>( &mut self, change: AppearanceChange, settings: &mut Settings, ) -> Command<Msg>

Saves change and returns the command that shows it at once. settings are the application’s own settings as it holds them in memory; they take the change too, so a later Settings::save writes what the file now says instead of what it said before.

A change that cannot be saved is still applied, and the row it was made on says why it was not saved until the next change.

Source

pub fn update_saving<Msg: Send + 'static>( &mut self, change: AppearanceChange, settings: &mut Settings, saved: impl FnOnce(AppearanceSave) -> Msg + Send + 'static, ) -> Command<Msg>

update, and the command that writes the ecosystem’s update notice in the background when updates_in_background is on: the switch shows the new value at once and saved is called with what became of the write once it is done, so the application can show it as a toast and hand it to saved.

Without the option every change is written as update writes it, on the thread that draws, and saved is never called; an application can then turn the option on without touching this line.

use qframe::prelude::*;
use qframe::storage::{Ecosystem, Settings};
use qframe::widgets::{Appearance, AppearanceChange, AppearanceSave, SettingsList};

struct Code {
    settings: Settings,
    appearance: Appearance,
    /// What the last background write left.
    saved: Option<AppearanceSave>,
}

#[derive(Debug, Clone)]
enum Msg {
    Appearance(AppearanceChange),
    Saved(AppearanceSave),
}

impl App for Code {
    type Msg = Msg;
    fn update(&mut self, msg: Msg) -> Command<Msg> {
        match msg {
            Msg::Appearance(change) => {
                self.appearance.update_saving(change, &mut self.settings, Msg::Saved)
            }
            Msg::Saved(save) => {
                self.saved = Some(save.clone());
                self.appearance.saved(&save);
                Command::none()
            }
        }
    }
    fn view(&self, ui: &mut View<'_, Msg>) {
        SettingsList::show(ui, |list| {
            self.appearance.section(list, Msg::Appearance);
            self.appearance.updates(list, Msg::Appearance);
        });
    }
}

// An application passes `ecosystem.preferences("code", &i18n)`; the example stays in a folder of its own.
let appearance = Appearance::new(ecosystem, "code", preferences).in_folder(&folder).updates_in_background();
let settings = Settings::open(folder.join("code.conf")).member_of(&ecosystem);
let mut app = Harness::new(Code { settings, appearance, saved: None }, 60, 20);
// The row's change is written off the drawing thread and its outcome comes back as a message.
app.send(Msg::Appearance(AppearanceChange::UpdateNotice(false)));
assert_eq!(app.app().saved, Some(AppearanceSave::Saved), "the file took the new value");
let shared = std::fs::read_to_string(folder.join("quvyta.conf")).expect("the shared file");
assert!(shared.contains("update-notice = false"), "{shared}");
Source

pub fn saved(&mut self, save: &AppearanceSave)

Takes what became of a change the rows saved in the background, as update_saving and updates_in_background say. A value the file could not take leaves the row showing what the file still says, so the switch is where the person left it. Nothing is written under the row: the application has the outcome and shows it itself, as a toast.

Trait Implementations§

Source§

impl Clone for Appearance

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Appearance

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

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> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. 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> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. 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.