Verified Commit f964eb44 authored by Katharina Fey's avatar Katharina Fey 🏴
Browse files

qaul-gtk: ui refactoring

parent 3bed0cec
......@@ -1938,6 +1938,7 @@ dependencies = [
name = "qaul-gtk"
version = "0.1.0"
dependencies = [
"async-std",
"gtk",
"libqaul-sdk",
"relm",
......
......@@ -8,6 +8,8 @@ license = "AGPL-3.0-or-later"
[dependencies]
libqaul-sdk = { version = "0.1", path = "../../sdk/libqaul-sdk" }
async-std = { version = "1.0", features = ["attributes"] }
gtk = "0.9"
relm = "0.21"
relm-derive = "0.21"
\ No newline at end of file
mod widgets;
use widgets::root_window as win;
#![allow(warnings)]
fn main() {
win::run();
pub(crate) mod msg;
mod ui;
#[async_std::main]
async fn main() {
// TODO: initialise libqaul-sdk
ui::start();
}
// mod widgets;
// use gtk::Orientation::Vertical;
// use gtk::{ButtonExt, EditableSignals, EntryExt, Inhibit, LabelExt, OrientableExt, WidgetExt};
// use relm::{Relm, StreamHandle, Widget};
// use relm_derive::{widget, Msg};
// use self::CounterMsg::*;
// use self::Msg::*;
// use widgets::TextMsg::*;
// pub struct CounterModel {
// counter: i32,
// }
// #[derive(Msg)]
// pub enum CounterMsg {
// Decrement,
// Increment,
// }
// #[widget]
// impl Widget for Counter {
// fn model() -> CounterModel {
// CounterModel { counter: 0 }
// }
// fn update(&mut self, event: CounterMsg) {
// match event {
// Decrement => self.model.counter -= 1,
// Increment => self.increment(),
// }
// }
// view! {
// gtk::Box {
// orientation: Vertical,
// gtk::Button {
// label: "+",
// widget_name: "inc_button",
// clicked => Increment,
// },
// gtk::Label {
// widget_name: "label",
// text: &self.model.counter.to_string(),
// },
// gtk::Button {
// label: "-",
// widget_name: "dec_button",
// clicked => Decrement,
// },
// }
// }
// fn increment(&mut self) {
// self.model.counter += 1;
// }
// }
// pub struct Model {
// counter: i32,
// relm: Relm<Win>,
// }
// #[derive(Msg)]
// pub enum Msg {
// TextChange(String),
// Quit,
// }
// #[widget]
// impl Widget for Win {
// fn model(relm: &Relm<Self>, _: ()) -> Model {
// Model {
// counter: 0,
// relm: relm.clone(),
// }
// }
// fn update(&mut self, event: Msg) {
// match event {
// TextChange(text) => {
// println!("{}", text);
// self.model.counter += 1
// }
// Quit => gtk::main_quit(),
// }
// }
// view! {
// gtk::Window {
// gtk::Box {
// #[name="dec_button"]
// gtk::Button {
// label: "Decrement",
// clicked => counter1@Decrement,
// },
// #[name="counter1"]
// Counter {
// Increment => counter2@Decrement,
// Increment => log_increment(),
// },
// #[name="counter2"]
// Counter,
// #[name="text"]
// Text(self.model.relm.stream().clone()) {
// Change(_) => counter1@Increment,
// },
// #[name="label"]
// gtk::Label {
// text: &self.model.counter.to_string(),
// }
// },
// delete_event(_, _) => (Quit, Inhibit(false)),
// }
// }
// }
// fn log_increment() {
// println!("Increment");
// }
use relm_derive::Msg;
// Prelude: get all enum variants into scope
pub use {Message::*, AppCmd::*, ScreenCmd::*};
#[derive(Debug, Msg)]
pub enum Message {
/// Basic app commands
App(AppCmd),
/// In-screen commands
Screen(ScreenCmd),
/// libqaul-sdk functions
Sdk(SdkCmd),
}
#[derive(Debug, Msg)]
pub enum AppCmd {
Quit,
}
impl From<AppCmd> for Message {
fn from(app: AppCmd) -> Self {
Message::App(app)
}
}
#[derive(Debug, Msg)]
pub enum ScreenCmd {
Change(String),
BtnLogin,
}
impl From<ScreenCmd> for Message {
fn from(s: ScreenCmd) -> Self {
Message::Screen(s)
}
}
#[derive(Debug, Msg)]
pub enum SdkCmd {
Login,
}
impl From<SdkCmd> for Message {
fn from(sdk: SdkCmd) -> Self {
Message::Sdk(sdk)
}
}
//! Main content area
//!
//! This section gets swapped according to ScreenCtl messages.
use super::screens::Login;
use crate::msg::*;
use gtk::{prelude::*, Align, Orientation::Vertical};
use relm::Widget;
use relm_derive::widget;
pub struct Model {}
#[widget]
impl Widget for Content {
view! {
#[name = "content"]
gtk::Box {
orientation: Vertical,
valign: Align::Center,
#[name = "login"]
Login {
// Capture screen change events to handle here
Screen(Change(ref name)) => Screen(Change(name.clone())),
},
}
}
fn update(&mut self, msg: Message) {
match msg {
Screen(Change(ref name)) => {
println!("Changing screen to {}", name);
}
_ => {}
}
}
fn model() -> Model {
Model {}
}
}
//! Main UI types
//!
//! qaul-gtk uses relm, a functional, and declarative wrapper around
//! the Gtk+ framework. Everything in the UI tree is fundamentally a
//! [widget](relm::Widget), which can emit and listen to signals.
//! Signalling types are encoded in the [Message](crate::msg::Message)
//! type hierarchy.
//!
//! ## Message flow
//!
//! Every event can emit a message, which is handled by the widget's
//! `update(...)` function. Events can also be handled by the
//! immediate parent, by matching for events in the widget's
//! constructing closure.
//!
//! ```rust
//! view! {
//! MyWidget {
//! InnerEvent => OuterEvent,
//! }
//! }
//! ```
//!
//! In that case, `OuterEvent` will be received by the current
//! widget's `update(...)` function.
mod content;
mod header;
mod root;
mod screens;
// Pull the main widget types into scope to shorten paths
pub(self) use content::Content;
pub(self) use root::Window;
pub(self) use screens::*;
use relm::Widget;
pub fn start() {
Window::run(()).unwrap();
}
//! Root window
use super::Content;
use crate::msg::*;
use gtk::{prelude::*, GtkWindowExt};
use relm::Widget;
use relm_derive::widget;
pub struct Model {}
#[widget]
impl Widget for Window {
view! {
#[name = "window"]
gtk::ApplicationWindow {
title: concat!("qaul GTK (v", env!("CARGO_PKG_VERSION"), ")"),
Content {
Screen(Change(ref name)) => Screen(Change(name.clone())),
},
delete_event(_, _) => (App(Quit), Inhibit(false)),
}
}
fn update(&mut self, msg: Message) {
match msg {
App(Quit) => gtk::main_quit(),
_ => {
dbg!(msg);
}
}
}
fn model() -> Model {
Model {}
}
}
use crate::msg::*;
use gtk::{
prelude::*,
Align::{Center, End},
InputPurpose::Password,
Orientation::Vertical,
};
use relm::{Relm, Widget};
use relm_derive::widget;
pub struct Model {}
const NO_USER: &'static str = "Create new ID";
#[widget]
impl Widget for Login {
view! {
gtk::Grid {
halign: Center,
row_spacing: 5,
column_spacing: 15,
gtk::Label {
text: "Welcome to qaul!",
margin_bottom: 15,
halign: Center,
cell: { left_attach: 0, top_attach: 0, width: 2 },
},
gtk::Label {
text: "Select user",
halign: End,
cell: { left_attach: 0, top_attach: 1}
},
#[name = "user_list"]
gtk::ComboBoxText {
cell: { left_attach: 1, top_attach: 1 },
},
gtk::Label {
text: "Password",
halign: End,
cell: { left_attach: 0, top_attach: 2 }
},
#[name = "password"]
gtk::Entry {
placeholder_text: Some("*********"),
input_purpose: Password,
visibility: false,
cell: { left_attach: 1, top_attach: 2 },
},
gtk::Button {
label: "Login",
clicked => Screen(BtnLogin),
cell: { left_attach: 0, top_attach: 3, width: 2 },
}
}
}
fn init_view(&mut self) {
// TODO: get available users
dbg!(self.widgets.password.get_input_purpose());
self.widgets.user_list.insert_text(0, NO_USER);
self.widgets.user_list.set_active(Some(0));
}
fn model() -> Model {
Model {}
}
fn update(&mut self, msg: Message) {
match msg {
Screen(BtnLogin) => {
let id = self
.widgets
.user_list
.get_active_text()
.unwrap()
.to_string();
let pw = self.widgets.password.to_string();
if id == NO_USER {
// We need to create a new user...
println!("Creating new user...");
// libqaul.users().create(pw).await?;
} else {
// We can login
println!("Logging in as existing user...");
// libqaul.users().login(id.into(), pw).await?;
}
// TODO: emit valid login event
// let update = Message::Screen(ScreenCmd::Change("main".into()));
// self.model.relm.stream().emit(update);
}
_ => {}
}
}
}
pub mod login;
pub use login::Login;
use crate::widgets::screens::Login;
use crate::messages::Message;
use crate::widgets::{Login, Window};
use gtk::{prelude::*, Align, Orientation::*};
use relm::Widget;
use relm_derive::{widget, Msg};
use relm::{Relm, Widget};
use relm_derive::widget;
pub struct Model {}
#[derive(Debug, Msg)]
pub enum Update {}
#[widget]
impl Widget for Content {
fn model() -> Model {
type ModelParam = Relm<Window>;
fn model(_: &Relm<Self>, _: ()) -> Model {
Model {}
}
fn update(&mut self, _: Update) {}
fn update(&mut self, _: Message) {}
view! {
#[name = "content"]
......@@ -23,11 +23,10 @@ impl Widget for Content {
orientation: Vertical,
valign: Align::Center,
Login {}
// gtk::Label {
// text: "Nothing to see here",
// }
#[name = "login"]
Login {
},
}
}
}
pub mod content;
pub mod header_bar;
pub mod root_window;
pub mod screens;
// mod content;
// pub use content::Content;
// mod header_bar;
// mod root_window;
// pub use root_window::{run, Window};
// mod screens;
// pub use screens::login::Login;
use crate::widgets::content::Content;
use crate::{
messages::{Command, Message},
widgets::Content,
};
use gtk::{prelude::*, GtkWindowExt};
use relm::Widget;
use relm_derive::{widget, Msg};
use relm_derive::widget;
pub struct Model {}
#[derive(Debug, Msg)]
pub enum Update {
Quit,
}
#[widget]
impl Widget for Window {
view! {
......@@ -20,7 +17,7 @@ impl Widget for Window {
Content {},
delete_event(_, _) => (Update::Quit, Inhibit(false)),
delete_event(_, _) => (Message::App(Command::Quit), Inhibit(false)),
}
}
......@@ -28,9 +25,15 @@ impl Widget for Window {
self.widgets.window.set_default_size(800, 600);
}
fn update(&mut self, event: Update) {
match dbg!(event) {
Update::Quit => gtk::main_quit(),
fn update(&mut self, event: Message) {
match event {
Message::App(_) => gtk::main_quit(),
Message::Screen(cmd) => {
dbg!(cmd);
}
event => {
dbg!(event);
}
}
}
......
use super::{Command, Model};
use crate::messages::{Message, ScreenCmd};
use crate::widgets::Window;
use gtk::{
prelude::*,
Align::{Center, End},
InputPurpose::Password,
};
use relm::Widget;
use relm::{Relm, Widget};
use relm_derive::widget;
const NO_USER: &'static str = "Create new ID";
pub struct Model {
relm: Relm<Login>,
}
#[widget]
impl Widget for Login {
view! {
......@@ -53,7 +58,7 @@ impl Widget for Login {
gtk::Button {
label: "Login",
clicked => Command::Login,
clicked => Message::Login,
cell: { left_attach: 0, top_attach: 3, width: 2 },
}
}
......@@ -68,9 +73,9 @@ impl Widget for Login {
self.widgets.user_list.set_active(Some(0));
}
fn update(&mut self, event: Command) {
fn update(&mut self, event: Message) {
match event {
Command::Login => {
Message::Login => {
let id = self
.widgets
.user_list
......@@ -87,14 +92,17 @@ impl Widget for Login {
// libqaul.users().login(id.into(), pw).await?;
}
let cmd = Command::ChangeScreen("main".into());
let update = Message::Screen(ScreenCmd::Change("main".into()));
self.model.relm.stream().emit(update);
}
event => {
dbg!(event);
todo!()
}
_ => todo!(),
}
}
fn model() -> Model {
Model {}
fn model(relm: &Relm<Self>, _: ()) -> Model {
Model { relm: relm.clone() }