1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
//! The editor module provides an editor for Layouts. The editor ensures that
//! all the different invariants of the Layout objects are upheld no matter what
//! kind of operations are being applied. It provides the current state of the
//! editor as state objects that can be visualized by any kind of User
//! Interface.

use super::{Component, Layout, LayoutState};
use crate::settings::Value;
use crate::Timer;
use std::result::Result as StdResult;

mod state;

pub use self::state::{Buttons as ButtonsState, State};

/// The Layout Editor allows modifying Layouts while ensuring all the different
/// invariants of the Layout objects are upheld no matter what kind of
/// operations are being applied. It provides the current state of the editor as
/// state objects that can be visualized by any kind of User Interface.
pub struct Editor {
    layout: Layout,
    selected_component: usize,
}

quick_error! {
    /// Describes an Error that occurred while opening the Layout Editor.
    #[derive(Debug)]
    pub enum Error {
        /// The Layout Editor couldn't be opened because an empty layout with no
        /// components was provided.
        EmptyLayout {}
    }
}

/// The Result type for the Layout Editor.
pub type Result<T> = StdResult<T, Error>;

impl Editor {
    /// Creates a new Layout Editor that modifies the Layout provided. Creation
    /// of the Layout Editor fails when a Layout with no components is provided.
    pub fn new(mut layout: Layout) -> Result<Self> {
        if layout.components.is_empty() {
            return Err(Error::EmptyLayout);
        }

        layout.remount();

        Ok(Self {
            layout,
            selected_component: 0,
        })
    }

    /// Closes the Layout Editor and gives back access to the modified Layout.
    /// In case you want to implement a Cancel Button, just drop the Layout
    /// object you get here.
    pub fn close(self) -> Layout {
        self.layout
    }

    /// Calculates the layout's state based on the timer provided. You can use
    /// this to visualize all of the components of a layout, while it is still
    /// being edited by the Layout Editor.
    pub fn layout_state(&mut self, timer: &Timer) -> LayoutState {
        self.layout.state(timer)
    }

    /// Selects the component with the given index in order to modify its
    /// settings. Only a single component is selected at any given time. You may
    /// not provide an invalid index.
    pub fn select(&mut self, index: usize) {
        if index < self.layout.components.len() {
            self.selected_component = index;
        }
    }

    /// Adds the component provided to the end of the layout. The newly added
    /// component becomes the selected component.
    pub fn add_component<C: Into<Component>>(&mut self, component: C) {
        self.selected_component = self.layout.components.len();
        self.layout.push(component);
    }

    /// Checks if the currently selected component can be removed. If there's
    /// only one component in the layout, it can't be removed.
    pub fn can_remove_component(&self) -> bool {
        // We need to ensure there's always at least one component.
        self.layout.components.len() > 1
    }

    /// Removes the currently selected component, unless there's only one
    /// component in the layout. The next component becomes the selected
    /// component. If there's none, the previous component becomes the selected
    /// component instead.
    pub fn remove_component(&mut self) {
        if self.can_remove_component() {
            self.layout.components.remove(self.selected_component);
            if self.selected_component >= self.layout.components.len() {
                self.selected_component = self.layout.components.len() - 1;
            }
            self.layout.remount();
        }
    }

    /// Checks if the currently selected component can be moved up. If the first
    /// component is selected, it can't be moved up.
    pub fn can_move_component_up(&self) -> bool {
        self.selected_component > 0
    }

    /// Moves the selected component up, unless the first component is selected.
    pub fn move_component_up(&mut self) {
        if self.can_move_component_up() {
            self.layout
                .components
                .swap(self.selected_component, self.selected_component - 1);
            self.selected_component -= 1;
            self.layout.remount();
        }
    }

    /// Checks if the currently selected component can be moved down. If the
    /// last component is selected, it can't be moved down.
    pub fn can_move_component_down(&self) -> bool {
        self.selected_component < self.layout.components.len() - 1
    }

    /// Moves the selected component down, unless the last component is
    /// selected.
    pub fn move_component_down(&mut self) {
        if self.can_move_component_down() {
            self.layout
                .components
                .swap(self.selected_component, self.selected_component + 1);
            self.selected_component += 1;
            self.layout.remount();
        }
    }

    /// Moves the selected component to the index provided. You may not provide
    /// an invalid index.
    pub fn move_component(&mut self, dst_index: usize) {
        if dst_index < self.layout.components.len() {
            while self.selected_component > dst_index {
                self.move_component_up();
            }
            while self.selected_component < dst_index {
                self.move_component_down();
            }
        }
    }

    /// Duplicates the currently selected component. The copy gets placed right
    /// after the selected component and becomes the newly selected component.
    pub fn duplicate_component(&mut self) {
        let index = self.selected_component;
        let new_index = index + 1;

        let component = self.layout.components[index].clone();
        self.layout.components.insert(new_index, component);

        self.selected_component = new_index;
        self.layout.remount();
    }

    /// Sets a setting's value of the selected component by its setting index
    /// to the given value.
    ///
    /// # Panics
    ///
    /// This panics if the type of the value to be set is not compatible with
    /// the type of the setting's value. A panic can also occur if the index of
    /// the setting provided is out of bounds.
    pub fn set_component_settings_value(&mut self, index: usize, value: Value) {
        self.layout.components[self.selected_component].set_value(index, value);
    }

    /// Sets a setting's value of the general settings by its setting index to
    /// the given value.
    ///
    /// # Panics
    ///
    /// This panics if the type of the value to be set is not compatible with
    /// the type of the setting's value. A panic can also occur if the index of
    /// the setting provided is out of bounds.
    pub fn set_general_settings_value(&mut self, index: usize, value: Value) {
        self.layout.general_settings_mut().set_value(index, value);
    }
}