Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  mod.rs   Sprache: unbekannt

 
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! The UI model.
//!
//! Model elements should generally be declared as types with all fields `pub` (to be accessed by
//! UI implementations), though accessor methods are acceptable if needed. An
//! `ElementBuilder<TYPE>` impl should be provided to create methods that will be used in the
//! [`ui!`] macro. The model types are accessible when being _consumed_ by a UI implementation,
//! whereas the `ElementBuilder` types are accessible when the model is being _created_.
//!
//! All elements should be listed in the `element_types!` macro in this file (note that [`Window`],
//! while an element, isn't listed here as it cannot be a child element). This populates the
//! `ElementType` enum and generates `From<Element>` for `ElementType`, and `TryFrom<ElementType>`
//! for the element (as well as reference `TryFrom`).
//!
//! The model is written to accommodate layout and text direction differences (e.g. for RTL
//! languages), and UI implementations are expected to account for this correctly.

use crate::data::Property;
pub use button::Button;
pub use checkbox::Checkbox;
pub use hbox::HBox;
pub use label::Label;
pub use progress::Progress;
pub use scroll::Scroll;
pub use textbox::TextBox;
pub use vbox::VBox;
pub use window::Window;

mod button;
mod checkbox;
mod hbox;
mod label;
mod progress;
mod scroll;
mod textbox;
mod vbox;
mod window;

/// A GUI element, including general style attributes and a more specific type.
///
/// `From<ElementBuilder<...>>` is implemented for all elements listed in `element_types!`.
#[derive(Debug)]
pub struct Element {
    pub style: ElementStyle,
    pub element_type: ElementType,
}

// This macro creates the `ElementType` enum and corresponding `From<ElementBuilder>` impls for
// Element. The `ElementType` discriminants match the element type names.
macro_rules! element_types {
    ( $($name:ident),* ) => {
        /// A type of GUI element.
        #[derive(Debug)]
        pub enum ElementType {
            $($name($name)),*
        }

        $(
        impl From<$name> for ElementType {
            fn from(e: $name) -> ElementType {
                ElementType::$name(e)
            }
        }

        impl TryFrom<ElementType> for $name {
            type Error = &'static str;

            fn try_from(et: ElementType) -> Result<Self, Self::Error> {
                if let ElementType::$name(v) = et {
                    Ok(v)
                } else {
                    Err(concat!("ElementType was not ", stringify!($name)))
                }
            }
        }

        impl<'a> TryFrom<&'a ElementType> for &'a $name {
            type Error = &'static str;

            fn try_from(et: &'a ElementType) -> Result<Self, Self::Error> {
                if let ElementType::$name(v) = et {
                    Ok(v)
                } else {
                    Err(concat!("ElementType was not ", stringify!($name)))
                }
            }
        }

        impl From<ElementBuilder<$name>> for Element {
             fn from(b: ElementBuilder<$name>) -> Self {
                 Element {
                     style: b.style,
                     element_type: b.element_type.into(),
                 }
             }
        }
        )*
    }
}
element_types! {
    Button, Checkbox, HBox, Label, Progress, Scroll, TextBox, VBox
}

/// Common element style values.
#[derive(Debug)]
pub struct ElementStyle {
    pub horizontal_alignment: Alignment,
    pub vertical_alignment: Alignment,
    pub horizontal_size_request: Option<u32>,
    pub vertical_size_request: Option<u32>,
    pub margin: Margin,
    pub visible: Property<bool>,
    pub enabled: Property<bool>,
    #[cfg(test)]
    pub id: Option<String>,
}

impl Default for ElementStyle {
    fn default() -> Self {
        ElementStyle {
            horizontal_alignment: Default::default(),
            vertical_alignment: Default::default(),
            horizontal_size_request: Default::default(),
            vertical_size_request: Default::default(),
            margin: Default::default(),
            visible: true.into(),
            enabled: true.into(),
            #[cfg(test)]
            id: Default::default(),
        }
    }
}

/// A builder for `Element`s.
///
/// Each element should add an `impl ElementBuilder<TYPE>` to add methods to their builder.
#[derive(Debug, Default)]
pub struct ElementBuilder<T> {
    pub style: ElementStyle,
    pub element_type: T,
}

impl<T> ElementBuilder<T> {
    /// Set horizontal alignment.
    pub fn halign(&mut self, alignment: Alignment) {
        self.style.horizontal_alignment = alignment;
    }

    /// Set vertical alignment.
    pub fn valign(&mut self, alignment: Alignment) {
        self.style.vertical_alignment = alignment;
    }

    /// Set the horizontal size request.
    pub fn hsize(&mut self, value: u32) {
        assert!(value <= i32::MAX as u32);
        self.style.horizontal_size_request = Some(value);
    }

    /// Set the vertical size request.
    pub fn vsize(&mut self, value: u32) {
        assert!(value <= i32::MAX as u32);
        self.style.vertical_size_request = Some(value);
    }

    /// Set start margin.
    pub fn margin_start(&mut self, amount: u32) {
        self.style.margin.start = amount;
    }

    /// Set end margin.
    pub fn margin_end(&mut self, amount: u32) {
        self.style.margin.end = amount;
    }

    /// Set start and end margins.
    pub fn margin_horizontal(&mut self, amount: u32) {
        self.margin_start(amount);
        self.margin_end(amount)
    }

    /// Set top margin.
    pub fn margin_top(&mut self, amount: u32) {
        self.style.margin.top = amount;
    }

    /// Set bottom margin.
    pub fn margin_bottom(&mut self, amount: u32) {
        self.style.margin.bottom = amount;
    }

    /// Set top and bottom margins.
    pub fn margin_vertical(&mut self, amount: u32) {
        self.margin_top(amount);
        self.margin_bottom(amount)
    }

    /// Set all margins.
    pub fn margin(&mut self, amount: u32) {
        self.margin_horizontal(amount);
        self.margin_vertical(amount)
    }

    /// Set visibility.
    pub fn visible(&mut self, value: impl Into<Property<bool>>) {
        self.style.visible = value.into();
    }

    /// Set whether an element is enabled.
    ///
    /// This generally should enable/disable interaction with an element.
    pub fn enabled(&mut self, value: impl Into<Property<bool>>) {
        self.style.enabled = value.into();
    }

    /// Set the element identifier.
    #[cfg(test)]
    pub fn id(&mut self, value: impl Into<String>) {
        self.style.id = Some(value.into());
    }

    /// Set the element identifier (stub).
    #[cfg(not(test))]
    pub fn id(&mut self, _value: impl Into<String>) {}

    fn single_child(slot: &mut Option<Box<Element>>, child: Element) {
        if slot.replace(Box::new(child)).is_some() {
            panic!("{} can only have one child", std::any::type_name::<T>());
        }
    }
}

/// A typed [`Element`].
///
/// This is useful for the [`ui!`] macro when a method should accept a specific element type, since
/// the macro always creates [`ElementBuilder<T>`](ElementBuilder) and ends with a `.into()` (and this implements
/// `From<ElementBuilder<T>>`).
#[derive(Debug, Default)]
pub struct TypedElement<T> {
    pub style: ElementStyle,
    pub element_type: T,
}

impl<T> From<ElementBuilder<T>> for TypedElement<T> {
    fn from(b: ElementBuilder<T>) -> Self {
        TypedElement {
            style: b.style,
            element_type: b.element_type,
        }
    }
}

/// The alignment of an element in one direction.
///
/// Note that rather than `Left`/`Right`, this class has `Start`/`End` as it is meant to be
/// layout-direction-aware.
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
#[allow(dead_code)]
pub enum Alignment {
    /// Align to the start of the direction.
    #[default]
    Start,
    /// Align to the center of the direction.
    Center,
    /// Align to the end of the direction.
    End,
    /// Fill all available space.
    Fill,
}

/// The margins of an element.
///
/// These are RTL-aware: for instance, `start` is the left margin in left-to-right languages and
/// the right margin in right-to-left languages.
#[derive(Default, Debug)]
pub struct Margin {
    pub start: u32,
    pub end: u32,
    pub top: u32,
    pub bottom: u32,
}

/// A macro to allow a convenient syntax for creating elements.
///
/// The macro expects the following syntax:
/// ```
/// ElementTypeName some_method(arg1, arg2) other_method() {
///     Child ...,
///     Child2 ...
/// }
/// ```
///
/// The type is wrapped in an `ElementBuilder`, and methods are called on this builder with a
/// mutable reference. This means that element types must implement Default and must implement
/// builder methods on `ElementBuilder<ElementTypeName>`. The children block is optional, and calls
/// `add_child(child: Element)` for each provided child (so implement this method if desired).
///
/// For testing, a string identifier can be set on any element with a `["my_identifier"]` following
/// the element type.
macro_rules! ui {
    ( $el:ident
        $([ $id:literal ])?
        $( $method:ident $methodargs:tt )*
        $({ $($contents:tt)* })?
    ) => {
        {
            #[allow(unused_imports)]
            use $crate::ui::model::*;
            let mut el: ElementBuilder<$el> = Default::default();
            $( el.id($id); )?
            $( el.$method $methodargs ; )*
            $( ui! { @children (el) $($contents)* } )?
            el.into()
        }
    };
    ( @children ($parent:expr) ) => {};
    ( @children ($parent:expr)
      $el:ident
        $([ $id:literal ])?
        $( $method:ident $methodargs:tt )*
        $({ $($contents:tt)* })?
      $(, $($rest:tt)* )?
    ) => {
        $parent.add_child(ui!( $el $([$id])? $( $method $methodargs )* $({ $($contents)* })? ));
        $(ui!( @children ($parent) $($rest)* ))?
    };
}

pub(crate) use ui;

/// An application, defined as a set of windows.
///
/// When all windows are closed, the application is considered complete (and loops should exit).
pub struct Application {
    pub windows: Vec<TypedElement<Window>>,
    /// Whether the text direction should be right-to-left.
    #[cfg_attr(test, allow(dead_code))]
    pub rtl: bool,
}

/// A function to be invoked in the UI loop.
pub type InvokeFn = Box<dyn FnOnce() + Send + 'static>;

[ Dauer der Verarbeitung: 0.25 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge