refactor into separate files for app structure

This commit is contained in:
2022-04-03 20:44:41 +02:00
parent d46261792d
commit 410e41a0ab
15 changed files with 417 additions and 331 deletions

View File

@@ -1,339 +1,30 @@
use adw::prelude::AdwApplicationWindowExt;
use gtk::prelude::{BoxExt, GtkWindowExt, OrientableExt};
use relm4::{adw::{self, traits::{PreferencesRowExt, ActionRowExt, AdwWindowExt, PreferencesPageExt, PreferencesWindowExt, PreferencesGroupExt}, prelude::{WidgetExt, ButtonExt, ListBoxRowExt}}, gtk::{self, SelectionMode}, AppUpdate, Model, RelmApp, Sender, WidgetPlus, Widgets, factory::{FactoryVec, FactoryPrototype}, view, RelmComponent, Components, send, ComponentUpdate};
use relm4::{factory::FactoryVec, RelmApp};
use ui::app::{
model::{AppModel, ToolboxStatus, ToolboxContainer},
};
const START_ICON : &str = r#"media-playback-start-symbolic"#;
const START_TOOLTIP : &str = r#"Start toolbox"#;
const SHUTDOWN_ICON : &str = r#"system-shutdown-symbolic"#;
const SHUTDOWN_TOOLTIP : &str = r#"Stop toolbox"#;
const UPDATE_ICON : &str = r#"software-update-available-symbolic"#;
const UPDATE_TOOLTIP : &str = r#"Update all applications inside of the toolbox"#;
const APP_ICON : &str = r#"view-grid-symbolic"#;
const APP_TOOLTIP : &str = r#"Select applications to showup in the application menu"#;
const TERMINAL_ICON : &str = r#"utilities-terminal-symbolic"#;
const TERMINAL_TOOLTIP : &str = r#"Open terminal inside of toolbox"#;
const SETTINGS_ICON : &str = r#"applications-system-symbolic"#;
const SETTINGS_TOOLTIP : &str = r#"Open toolbox settings"#;
const FOLDER_PICKER_ICON : &str = r#"folder-open-symbolic"#;
const FOLDER_PICKER_TOOLTIP : &str = r#"Select folder dialogue"#;
struct AppModel {
toolboxes: FactoryVec<ToolboxContainer>
}
#[derive(Debug)]
enum AppMode {
}
enum AppMsg {
ShowToolboxSettingsRequest,
}
// Components (Book)
#[relm4::widget]
impl Widgets<ToolboxSettingsDialogModel, AppModel> for ToolboxSettingsDialogWidgets {
view! {
adw::PreferencesWindow {
set_title: Some("Preferences: <Toolbox_name>"),
set_transient_for: parent!{Some(&parent_widgets.main_window)},
set_modal: true,
set_visible: watch!(!model.hidden),
connect_close_request(sender) => move |_| {
send!(sender, ToolboxSettingsDialogMsg::Close);
gtk::Inhibit(true)
},
add = &adw::PreferencesPage {
add = &adw::PreferencesGroup {
set_title: "Updates",
add = &adw::PreferencesRow {
set_title: "Update Policy",
set_child = Some(&adw::ActionRow) {
set_title: "Update Policy",
add_suffix = &gtk::Box {
append = &gtk::DropDown::from_strings(&[
"Update automatically",
"Notify about updates",
"Do nothing"
]) {
set_margin_all: 15,
},
}
},
},
},
add = &adw::PreferencesGroup {
set_title: "Home Folder",
add = &adw::PreferencesRow {
set_title: "Seperate Home Folder",
set_child = Some(&adw::ActionRow) {
set_title: "Use separate home folder",
add_suffix = &gtk::Box {
append = &gtk::Switch {
set_margin_all: 15,
set_tooltip_text: Some("Use separate home folder"),
},
}
},
},
add = &adw::PreferencesRow {
set_title: "Home Folder Path",
set_child = Some(&adw::ActionRow) {
set_title: "Home folder path",
add_suffix = &gtk::Box {
set_margin_all: 15,
add_css_class: "linked",
append = &gtk::Entry {
set_hexpand: true,
},
append = &gtk::Button::from_icon_name(FOLDER_PICKER_ICON) {
set_tooltip_text: Some(FOLDER_PICKER_TOOLTIP),
}
}
},
},
}
}
}
}
}
struct ToolboxSettingsDialogModel {
hidden: bool,
}
enum ToolboxSettingsDialogMsg {
Show,
Close,
}
impl Model for ToolboxSettingsDialogModel {
type Msg = ToolboxSettingsDialogMsg;
type Widgets = ToolboxSettingsDialogWidgets;
type Components = ();
}
impl ComponentUpdate<AppModel> for ToolboxSettingsDialogModel {
fn init_model(_parent_model: &AppModel) -> Self {
ToolboxSettingsDialogModel { hidden: true }
}
fn update(
&mut self,
msg: ToolboxSettingsDialogMsg,
_components: &(),
_sender: Sender<ToolboxSettingsDialogMsg>,
parent_sender: Sender<AppMsg>,
) {
match msg {
ToolboxSettingsDialogMsg::Show => self.hidden = false,
ToolboxSettingsDialogMsg::Close => self.hidden = true,
}
}
}
#[derive(relm4::Components)]
struct AppComponents {
dialog: RelmComponent<ToolboxSettingsDialogModel, AppModel>,
}
// \Components
impl Model for AppModel {
type Msg = AppMsg;
type Widgets = AppWidgets;
type Components = AppComponents;
}
impl AppUpdate for AppModel {
fn update(&mut self, msg: AppMsg, components: &AppComponents, _sender: Sender<AppMsg>) -> bool {
match msg {
AppMsg::ShowToolboxSettingsRequest => {
components.dialog.send(ToolboxSettingsDialogMsg::Show).unwrap();
}
}
true
}
}
#[relm4::widget]
impl Widgets<AppModel, ()> for AppWidgets {
view! {
main_window = adw::ApplicationWindow {
set_default_width: 800,
set_default_height: 600,
set_content: main_box = Some(&gtk::Box) {
set_orientation: gtk::Orientation::Vertical,
append = &adw::HeaderBar {
set_title_widget = Some(&gtk::Label) {
set_label: "Toolbox Tuner",
}
},
append = &gtk::ListBox {
set_selection_mode: SelectionMode::None,
set_margin_all: 30,
set_css_classes: &["boxed-list"],
factory!(model.toolboxes)
}
},
}
}
}
// FACTORY
enum ToolboxStatus {
Stopped,
Running,
}
impl Default for ToolboxStatus {
fn default() -> Self {
ToolboxStatus::Stopped
}
}
#[derive(Default)]
struct ToolboxContainer{
name: String,
status: ToolboxStatus,
update_available: bool,
}
#[derive(Debug)]
struct FactoryWidgets {
action_row: adw::ActionRow,
}
impl FactoryPrototype for ToolboxContainer {
type Factory = FactoryVec<Self>;
type Widgets = FactoryWidgets;
type Root = adw::ActionRow;
type View = gtk::ListBox;
type Msg = AppMsg;
fn init_view(
&self,
key: &<Self::Factory as relm4::factory::Factory<Self, Self::View>>::Key,
sender: Sender<Self::Msg>,
) -> Self::Widgets {
view!{
suffix_box = &gtk::Box{
append = &gtk::Button::from_icon_name(APP_ICON) {
set_margin_start: 10,
set_margin_top: 10,
set_margin_bottom: 10,
set_tooltip_text: Some(APP_TOOLTIP),
set_css_classes: &["flat"],
},
append = &gtk::Button::from_icon_name(TERMINAL_ICON) {
set_margin_start: 10,
set_margin_top: 10,
set_margin_bottom: 10,
set_tooltip_text: Some(TERMINAL_TOOLTIP),
set_css_classes: &["flat"],
},
append = &gtk::Button::from_icon_name(SETTINGS_ICON) {
set_margin_start: 10,set_margin_start: 10,
set_margin_top: 10,
set_margin_bottom: 10,
set_tooltip_text: Some(SETTINGS_TOOLTIP),
set_css_classes: &["circular"],
connect_clicked(sender) => move |btn| {
send!(sender, AppMsg::ShowToolboxSettingsRequest);
},
},
}
};
if self.update_available {
view!{
update_button = &gtk::Button::from_icon_name(UPDATE_ICON) {
set_margin_top: 10,
set_margin_bottom: 10,
set_margin_end: 10,
set_tooltip_text: Some(UPDATE_TOOLTIP),
set_css_classes: &["suggested-action"],
}
};
suffix_box.prepend(&update_button);
}
let is_on = true;
let mut status_button_tooltip = START_TOOLTIP;
let mut status_button_icon = START_ICON;
match &self.status {
&ToolboxStatus::Running => {
status_button_tooltip = SHUTDOWN_TOOLTIP;
status_button_icon = SHUTDOWN_ICON;
},
&ToolboxStatus::Stopped => {
status_button_tooltip = START_TOOLTIP;
status_button_icon = START_ICON;
}
}
view! {
action_row = &adw::ActionRow {
set_title: &self.name,
set_subtitle: "additional information",
add_prefix = &gtk::Box {
append = &gtk::Button::from_icon_name(status_button_icon) {
set_margin_top: 10,
set_margin_bottom: 10,
set_tooltip_text: Some(status_button_tooltip),
set_css_classes: &["circular"],
},
},
add_suffix: &suffix_box,
}
};
FactoryWidgets { action_row }
}
fn view(
&self,
key: &<Self::Factory as relm4::factory::Factory<Self, Self::View>>::Key,
widgets: &Self::Widgets,
) {
//widgets.action_row.set_label(&self.name.to_string());
}
fn root_widget(widgets: &Self::Widgets) -> &Self::Root {
&widgets.action_row
}
fn position(
&self,
key: &<Self::Factory as relm4::factory::Factory<Self, Self::View>>::Key,
) -> <Self::View as relm4::factory::FactoryView<Self::Root>>::Position { }
}
// END_FACTORY
mod ui;
fn main() {
let mut factory_vec = FactoryVec::new();
factory_vec.push(ToolboxContainer{name: "fedora-toolbox-35".to_string(), status: ToolboxStatus::Running, update_available: false});
factory_vec.push(ToolboxContainer{name: "Latex".to_string(), status: ToolboxStatus::Running, update_available: false});
factory_vec.push(ToolboxContainer{name: "Rust".to_string(), status: ToolboxStatus::Stopped, update_available: true});
factory_vec.push(ToolboxContainer {
name: "fedora-toolbox-35".to_string(),
status: ToolboxStatus::Running,
update_available: false,
});
factory_vec.push(ToolboxContainer {
name: "Latex".to_string(),
status: ToolboxStatus::Running,
update_available: false,
});
factory_vec.push(ToolboxContainer {
name: "Rust".to_string(),
status: ToolboxStatus::Stopped,
update_available: true,
});
let model = AppModel {
toolboxes: factory_vec
toolboxes: factory_vec,
};
let app = RelmApp::new(model);
app.run();

3
src/ui/app/messages.rs Normal file
View File

@@ -0,0 +1,3 @@
pub enum AppMsg {
ShowToolboxSettingsRequest,
}

5
src/ui/app/mod.rs Normal file
View File

@@ -0,0 +1,5 @@
pub mod messages;
pub mod model;
pub mod toolbox_list;
pub mod update;
pub mod widgets;

31
src/ui/app/model.rs Normal file
View File

@@ -0,0 +1,31 @@
use relm4::{factory::FactoryVec, Model};
use crate::ui::components::toolbox_settings::model::AppComponents;
use super::{messages::AppMsg, widgets::AppWidgets};
pub struct AppModel {
pub toolboxes: FactoryVec<ToolboxContainer>,
}
#[derive(Default)]
pub struct ToolboxContainer {
pub name: String,
pub status: ToolboxStatus,
pub update_available: bool,
}
impl Model for AppModel {
type Msg = AppMsg;
type Widgets = AppWidgets;
type Components = AppComponents;
}
pub enum ToolboxStatus {
Stopped,
Running,
}
impl Default for ToolboxStatus {
fn default() -> Self {
ToolboxStatus::Stopped
}
}

131
src/ui/app/toolbox_list.rs Normal file
View File

@@ -0,0 +1,131 @@
use relm4::{
adw::{
self,
prelude::{BoxExt, ButtonExt, WidgetExt},
traits::{ActionRowExt, PreferencesRowExt},
},
factory::{FactoryPrototype, FactoryVec},
gtk, send, view, Sender,
};
use crate::ui::{
app::model::ToolboxStatus,
ui_strings::{
APP_ICON, APP_TOOLTIP, SETTINGS_ICON, SETTINGS_TOOLTIP, SHUTDOWN_ICON, SHUTDOWN_TOOLTIP,
START_ICON, START_TOOLTIP, TERMINAL_ICON, TERMINAL_TOOLTIP, UPDATE_ICON, UPDATE_TOOLTIP,
},
};
use super::{messages::AppMsg, model::ToolboxContainer};
#[derive(Debug)]
pub struct FactoryWidgets {
pub action_row: adw::ActionRow,
}
impl FactoryPrototype for ToolboxContainer {
type Factory = FactoryVec<Self>;
type Widgets = FactoryWidgets;
type Root = adw::ActionRow;
type View = gtk::ListBox;
type Msg = AppMsg;
fn init_view(
&self,
key: &<Self::Factory as relm4::factory::Factory<Self, Self::View>>::Key,
sender: Sender<Self::Msg>,
) -> Self::Widgets {
view! {
suffix_box = &gtk::Box{
append = &gtk::Button::from_icon_name(APP_ICON) {
set_margin_start: 10,
set_margin_top: 10,
set_margin_bottom: 10,
set_tooltip_text: Some(APP_TOOLTIP),
set_css_classes: &["flat"],
},
append = &gtk::Button::from_icon_name(TERMINAL_ICON) {
set_margin_start: 10,
set_margin_top: 10,
set_margin_bottom: 10,
set_tooltip_text: Some(TERMINAL_TOOLTIP),
set_css_classes: &["flat"],
},
append = &gtk::Button::from_icon_name(SETTINGS_ICON) {
set_margin_start: 10,set_margin_start: 10,
set_margin_top: 10,
set_margin_bottom: 10,
set_tooltip_text: Some(SETTINGS_TOOLTIP),
set_css_classes: &["circular"],
connect_clicked(sender) => move |btn| {
send!(sender, AppMsg::ShowToolboxSettingsRequest);
},
},
}
};
if self.update_available {
view! {
update_button = &gtk::Button::from_icon_name(UPDATE_ICON) {
set_margin_top: 10,
set_margin_bottom: 10,
set_margin_end: 10,
set_tooltip_text: Some(UPDATE_TOOLTIP),
set_css_classes: &["suggested-action"],
}
};
suffix_box.prepend(&update_button);
}
let is_on = true;
let mut status_button_tooltip = START_TOOLTIP;
let mut status_button_icon = START_ICON;
match &self.status {
&ToolboxStatus::Running => {
status_button_tooltip = SHUTDOWN_TOOLTIP;
status_button_icon = SHUTDOWN_ICON;
}
&ToolboxStatus::Stopped => {
status_button_tooltip = START_TOOLTIP;
status_button_icon = START_ICON;
}
}
view! {
action_row = &adw::ActionRow {
set_title: &self.name,
set_subtitle: "additional information",
add_prefix = &gtk::Box {
append = &gtk::Button::from_icon_name(status_button_icon) {
set_margin_top: 10,
set_margin_bottom: 10,
set_tooltip_text: Some(status_button_tooltip),
set_css_classes: &["circular"],
},
},
add_suffix: &suffix_box,
}
};
FactoryWidgets { action_row }
}
fn view(
&self,
key: &<Self::Factory as relm4::factory::Factory<Self, Self::View>>::Key,
widgets: &Self::Widgets,
) {
//widgets.action_row.set_label(&self.name.to_string());
}
fn root_widget(widgets: &Self::Widgets) -> &Self::Root {
&widgets.action_row
}
fn position(
&self,
key: &<Self::Factory as relm4::factory::Factory<Self, Self::View>>::Key,
) -> <Self::View as relm4::factory::FactoryView<Self::Root>>::Position {
}
}

21
src/ui/app/update.rs Normal file
View File

@@ -0,0 +1,21 @@
use relm4::{AppUpdate, Sender};
use crate::ui::components::toolbox_settings::{
messages::ToolboxSettingsDialogMsg, model::AppComponents,
};
use super::{messages::AppMsg, model::AppModel};
impl AppUpdate for AppModel {
fn update(&mut self, msg: AppMsg, components: &AppComponents, _sender: Sender<AppMsg>) -> bool {
match msg {
AppMsg::ShowToolboxSettingsRequest => {
components
.toolbox_settings_dialog
.send(ToolboxSettingsDialogMsg::Show)
.unwrap();
}
}
true
}
}

39
src/ui/app/widgets.rs Normal file
View File

@@ -0,0 +1,39 @@
use relm4::{
adw::{
self,
prelude::{BoxExt, GtkWindowExt, OrientableExt, WidgetExt},
traits::AdwApplicationWindowExt,
},
gtk::{self, SelectionMode},
WidgetPlus, Widgets,
};
use super::model::AppModel;
#[relm4::widget(pub)]
impl Widgets<AppModel, ()> for AppWidgets {
view! {
main_window = adw::ApplicationWindow {
set_default_width: 800,
set_default_height: 600,
set_content: main_box = Some(&gtk::Box) {
set_orientation: gtk::Orientation::Vertical,
append = &adw::HeaderBar {
set_title_widget = Some(&gtk::Label) {
set_label: "Toolbox Tuner",
}
},
append = &gtk::ListBox {
set_selection_mode: SelectionMode::None,
set_margin_all: 30,
set_css_classes: &["boxed-list"],
factory!(model.toolboxes)
}
},
}
}
}

1
src/ui/components/mod.rs Normal file
View File

@@ -0,0 +1 @@
pub mod toolbox_settings;

View File

@@ -0,0 +1,4 @@
pub enum ToolboxSettingsDialogMsg {
Show,
Close,
}

View File

@@ -0,0 +1,4 @@
pub mod messages;
pub mod model;
pub mod update;
pub mod widgets;

View File

@@ -0,0 +1,20 @@
use relm4::Sender;
use relm4::{Model, RelmComponent};
use crate::ui::app::model::AppModel;
use super::{messages::ToolboxSettingsDialogMsg, widgets::ToolboxSettingsDialogWidgets};
#[derive(relm4::Components)]
pub struct AppComponents {
pub toolbox_settings_dialog: RelmComponent<ToolboxSettingsDialogModel, AppModel>,
}
pub struct ToolboxSettingsDialogModel {
pub hidden: bool,
}
impl Model for ToolboxSettingsDialogModel {
type Msg = ToolboxSettingsDialogMsg;
type Widgets = ToolboxSettingsDialogWidgets;
type Components = ();
}

View File

@@ -0,0 +1,24 @@
use relm4::{ComponentUpdate, Sender};
use crate::ui::app::{messages::AppMsg, model::AppModel};
use super::{messages::ToolboxSettingsDialogMsg, model::ToolboxSettingsDialogModel};
impl ComponentUpdate<AppModel> for ToolboxSettingsDialogModel {
fn init_model(_parent_model: &AppModel) -> Self {
ToolboxSettingsDialogModel { hidden: true }
}
fn update(
&mut self,
msg: ToolboxSettingsDialogMsg,
_components: &(),
_sender: Sender<ToolboxSettingsDialogMsg>,
parent_sender: Sender<AppMsg>,
) {
match msg {
ToolboxSettingsDialogMsg::Show => self.hidden = false,
ToolboxSettingsDialogMsg::Close => self.hidden = true,
}
}
}

View File

@@ -0,0 +1,89 @@
use relm4::adw;
use relm4::adw::prelude::BoxExt;
use relm4::adw::prelude::GtkWindowExt;
use relm4::adw::prelude::ListBoxRowExt;
use relm4::adw::prelude::WidgetExt;
use relm4::adw::traits::ActionRowExt;
use relm4::adw::traits::PreferencesGroupExt;
use relm4::adw::traits::PreferencesPageExt;
use relm4::adw::traits::PreferencesRowExt;
use relm4::adw::traits::PreferencesWindowExt;
use relm4::gtk;
use relm4::send;
use relm4::WidgetPlus;
use relm4::Widgets;
use crate::ui::app::model::AppModel;
use crate::ui::components::toolbox_settings::messages::ToolboxSettingsDialogMsg;
use crate::ui::ui_strings::FOLDER_PICKER_ICON;
use crate::ui::ui_strings::FOLDER_PICKER_TOOLTIP;
use super::model::ToolboxSettingsDialogModel;
#[relm4::widget(pub)]
impl Widgets<ToolboxSettingsDialogModel, AppModel> for ToolboxSettingsDialogWidgets {
view! {
adw::PreferencesWindow {
set_title: Some("Preferences: <Toolbox_name>"),
set_transient_for: parent!{Some(&parent_widgets.main_window)},
set_modal: true,
set_visible: watch!(!model.hidden),
connect_close_request(sender) => move |_| {
send!(sender, ToolboxSettingsDialogMsg::Close);
gtk::Inhibit(true)
},
add = &adw::PreferencesPage {
add = &adw::PreferencesGroup {
set_title: "Updates",
add = &adw::PreferencesRow {
set_title: "Update Policy",
set_child = Some(&adw::ActionRow) {
set_title: "Update Policy",
add_suffix = &gtk::Box {
append = &gtk::DropDown::from_strings(&[
"Update automatically",
"Notify about updates",
"Do nothing"
]) {
set_margin_all: 15,
},
}
},
},
},
add = &adw::PreferencesGroup {
set_title: "Home Folder",
add = &adw::PreferencesRow {
set_title: "Seperate Home Folder",
set_child = Some(&adw::ActionRow) {
set_title: "Use separate home folder",
add_suffix = &gtk::Box {
append = &gtk::Switch {
set_margin_all: 15,
set_tooltip_text: Some("Use separate home folder"),
},
}
},
},
add = &adw::PreferencesRow {
set_title: "Home Folder Path",
set_child = Some(&adw::ActionRow) {
set_title: "Home folder path",
add_suffix = &gtk::Box {
set_margin_all: 15,
add_css_class: "linked",
append = &gtk::Entry {
set_hexpand: true,
},
append = &gtk::Button::from_icon_name(FOLDER_PICKER_ICON) {
set_tooltip_text: Some(FOLDER_PICKER_TOOLTIP),
}
}
},
},
}
}
}
}
}

3
src/ui/mod.rs Normal file
View File

@@ -0,0 +1,3 @@
pub mod app;
pub mod components;
pub mod ui_strings;

20
src/ui/ui_strings.rs Normal file
View File

@@ -0,0 +1,20 @@
pub const START_ICON: &str = r#"media-playback-start-symbolic"#;
pub const START_TOOLTIP: &str = r#"Start toolbox"#;
pub const SHUTDOWN_ICON: &str = r#"system-shutdown-symbolic"#;
pub const SHUTDOWN_TOOLTIP: &str = r#"Stop toolbox"#;
pub const UPDATE_ICON: &str = r#"software-update-available-symbolic"#;
pub const UPDATE_TOOLTIP: &str = r#"Update all applications inside of the toolbox"#;
pub const APP_ICON: &str = r#"view-grid-symbolic"#;
pub const APP_TOOLTIP: &str = r#"Select applications to showup in the application menu"#;
pub const TERMINAL_ICON: &str = r#"utilities-terminal-symbolic"#;
pub const TERMINAL_TOOLTIP: &str = r#"Open terminal inside of toolbox"#;
pub const SETTINGS_ICON: &str = r#"applications-system-symbolic"#;
pub const SETTINGS_TOOLTIP: &str = r#"Open toolbox settings"#;
pub const FOLDER_PICKER_ICON: &str = r#"folder-open-symbolic"#;
pub const FOLDER_PICKER_TOOLTIP: &str = r#"Select folder dialogue"#;