diff --git a/Cargo.lock b/Cargo.lock
index 1b7f638e..0873b493 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -563,6 +563,7 @@ dependencies = [
"gstreamer-pbutils",
"gstreamer-player",
"gtk",
+ "gtk-sys",
"html2pango",
"itertools 0.8.2",
"lazy_static",
diff --git a/fractal-gtk/Cargo.toml b/fractal-gtk/Cargo.toml
index 16d1302e..04b8b794 100644
--- a/fractal-gtk/Cargo.toml
+++ b/fractal-gtk/Cargo.toml
@@ -31,6 +31,7 @@ serde_json = "1.0.48"
letter-avatar = "1.3.0"
sourceview4 = "0.2.0"
gspell = "0.5.0"
+gtk-sys = "0.10.0"
[dependencies.gst]
version = "0.16.1"
diff --git a/fractal-gtk/res/ui/account_settings.ui b/fractal-gtk/res/ui/account_settings.ui
index 8e7730b1..bc550faa 100644
--- a/fractal-gtk/res/ui/account_settings.ui
+++ b/fractal-gtk/res/ui/account_settings.ui
@@ -5,12 +5,42 @@
-
diff --git a/fractal-gtk/res/ui/login_flow.ui b/fractal-gtk/res/ui/login_flow.ui
index ebaac45a..c504651f 100644
--- a/fractal-gtk/res/ui/login_flow.ui
+++ b/fractal-gtk/res/ui/login_flow.ui
@@ -2,63 +2,79 @@
-
+
False
+ True
True
- GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT
-
+
True
False
- center
- center
vertical
- 18
-
+
-
+
True
False
- Welcome to Fractal
- 48
- True
- PANGO_WRAP_WORD_CHAR
-
-
-
-
-
-
-
-
- True
- True
- _Log In
- login.server_chooser
- 48
-
-
-
-
-
- True
- True
- _Create Account
- 48
- login.create-account
+ center
+ center
+ True
+ vertical
+ 18
+
+
+ True
+ False
+ center
+ 18
+ 18
+ 18
+ 128
+ chat-icon
+
+
+
+
+ True
+ False
+ Welcome to Fractal
+ 48
+ True
+ PANGO_WRAP_WORD_CHAR
+
+
+
+
+
+
+
+
+ True
+ True
+ _Log In
+ login.server_chooser
+ 48
+
+
+
+
+
+ True
+ True
+ _Create Account
+ 48
+ login.create-account
+
+
@@ -67,315 +83,308 @@
-
+
True
False
- center
- center
vertical
- 18
+ True
-
+
-
-
-
- True
- False
- What is your Provider?
- 30
- True
- PANGO_WRAP_WORD_CHAR
-
-
-
-
+ True
+ 360
+ Choose Provider
+
+
+ True
+ True
+ login.back
+
+
+ True
+ go-previous-symbolic
+
+
+
+
+ start
+
+
+
+
+ True
+ True
+ True
+ login.credentials
+ _Next
+
+
+
+ end
+
+
-
+
True
False
+ center
+ center
+ True
vertical
- 6
+ 18
-
+
True
+ False
center
- -1
- 300
- GTK_INPUT_PURPOSE_URL
+ 18
+ 18
+ 128
+ network-server-symbolic
+
True
False
- Matrix provider domain, e.g. myserver.co
+ What is your Provider?
+ 30
+ True
+ PANGO_WRAP_WORD_CHAR
+
+
+
+
+
+
+
+
+ True
+ False
+ vertical
+ 6
+
+
+ True
+ center
+ -1
+ 300
+ GTK_INPUT_PURPOSE_URL
+
+
+
+
+ True
+ False
+ Matrix provider domain, e.g. myserver.co
+ True
+ PANGO_WRAP_WORD_CHAR
+
+
+
+
+
+
+
+ False
+ False
+ True
+ The domain may not be empty.
True
PANGO_WRAP_WORD_CHAR
-
-
- False
- False
- True
- The domain may not be empty.
- True
- PANGO_WRAP_WORD_CHAR
-
-
-
server-chooser
-
+
True
False
- center
- center
- 12
- 24
-
-
- True
- True
- False
- _User ID
- end
- end
- True
- PANGO_WRAP_WORD_CHAR
- username_entry
-
-
-
+ vertical
-
+
-
- 3
- 0
-
-
+
True
False
- vertical
- 6
+ center
+ center
+ True
+ 12
+ 24
-
+
True
- -1
- 232
- True
+ True
+ False
+ _User ID
+ end
+ end
+ True
+ PANGO_WRAP_WORD_CHAR
+ username_entry
+
True
+ True
False
- User name, email, or phone number
- start
+ _Password
+ end
True
PANGO_WRAP_WORD_CHAR
+ password_entry
+
+ 3
+ 0
+
-
-
- 1
- 2
-
-
-
-
- True
- -1
- 232
- True
- False
- GTK_INPUT_PURPOSE_PASSWORD
-
-
- 3
- 1
-
-
-
-
- True
- _Forgot Password?
- https://riot.im/app/#/login
- start
-
-
-
- 4
- 1
-
-
-
-
- False
- False
- True
- 0
- Invalid username or password
- True
- PANGO_WRAP_WORD_CHAR
-
-
-
- 5
- 1
-
-
-
-
- credentials
-
-
-
-
-
diff --git a/fractal-gtk/res/ui/room_settings.ui b/fractal-gtk/res/ui/room_settings.ui
index 5d91404b..1832290b 100644
--- a/fractal-gtk/res/ui/room_settings.ui
+++ b/fractal-gtk/res/ui/room_settings.ui
@@ -5,9 +5,39 @@
True
False
+ vertical
+
+
+
True
+ True
True
never
@@ -999,44 +1029,6 @@
-
- False
- True
- 0
-
-
-
-
diff --git a/fractal-gtk/src/actions/global.rs b/fractal-gtk/src/actions/global.rs
index 936e55cf..2db1ad22 100644
--- a/fractal-gtk/src/actions/global.rs
+++ b/fractal-gtk/src/actions/global.rs
@@ -13,8 +13,9 @@ use fractal_api::identifiers::{EventId, RoomId};
use gio::prelude::*;
use gio::SimpleAction;
use gtk::prelude::*;
+use libhandy::prelude::*;
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Copy, Clone, PartialEq)]
pub enum AppState {
Login,
Loading,
@@ -81,6 +82,7 @@ pub fn new(app: >k::Application, op: &Arc>) {
let main_menu = SimpleAction::new("main_menu", None);
let open_room = SimpleAction::new("open-room", glib::VariantTy::new("s").ok());
+ let deck_back = SimpleAction::new("deck-back", None);
let back = SimpleAction::new("back", None);
let media_viewer = SimpleAction::new("open-media-viewer", glib::VariantTy::new("s").ok());
let account = SimpleAction::new("open-account-settings", None);
@@ -115,6 +117,7 @@ pub fn new(app: >k::Application, op: &Arc>) {
app.add_action(&shortcuts);
app.add_action(&about);
app.add_action(&open_room);
+ app.add_action(&deck_back);
app.add_action(&back);
app.add_action(&directory);
app.add_action(&room_settings);
@@ -272,6 +275,13 @@ pub fn new(app: >k::Application, op: &Arc>) {
back.borrow_mut().push(AppState::MediaViewer);
}));
+ deck_back.connect_activate(clone!(@strong op => move |_, _| {
+ let deck = op.lock().unwrap().deck.clone();
+ if deck.get_can_swipe_back() {
+ deck.navigate(libhandy::NavigationDirection::Back);
+ }
+ }));
+
let mv = op.lock().unwrap().media_viewer.clone();
let back_weak = Rc::downgrade(&back_history);
back.connect_activate(clone!(@weak mv => move |_, _| {
@@ -344,6 +354,7 @@ pub fn new(app: >k::Application, op: &Arc>) {
app.set_accels_for_action("app.older-messages", &["Page_Up"]);
app.set_accels_for_action("app.newer-messages", &["Page_Down"]);
app.set_accels_for_action("app.back", &["Escape"]);
+ app.set_accels_for_action("app.deck-back", &["Escape"]);
app.set_accels_for_action("app.main_menu", &["F10"]);
// connect mouse back button to app.back action
diff --git a/fractal-gtk/src/actions/login.rs b/fractal-gtk/src/actions/login.rs
index 8bac5a90..72991c1d 100644
--- a/fractal-gtk/src/actions/login.rs
+++ b/fractal-gtk/src/actions/login.rs
@@ -1,6 +1,5 @@
-use log::{debug, warn};
-use std::cell::RefCell;
-use std::rc::Rc;
+use libhandy::prelude::*;
+use log::warn;
use gio::prelude::*;
use gio::SimpleAction;
@@ -41,8 +40,7 @@ impl ToString for LoginState {
}
pub fn new(
- stack: >k::Stack,
- headers: >k::Stack,
+ deck: &libhandy::Deck,
server_entry: >k::Entry,
err_label: >k::Label,
) -> SimpleActionGroup {
@@ -60,8 +58,8 @@ pub fn new(
actions.add_action(&back);
actions.add_action(&login);
- create_account.connect_activate(clone!(@weak stack => move |_, _| {
- let toplevel = stack
+ create_account.connect_activate(clone!(@weak deck => move |_, _| {
+ let toplevel = deck
.get_toplevel()
.expect("Could not grab toplevel widget")
.downcast::()
@@ -73,40 +71,25 @@ pub fn new(
}
}));
- let back_history: Rc>> = Rc::new(RefCell::new(vec![]));
-
- server_chooser.connect_activate(
- clone!(@weak stack, @weak back_history as back => move |_, _| {
- let state = LoginState::ServerChooser;
- stack.set_visible_child_name(&state.to_string());
- back.borrow_mut().push(state);
- }),
- );
+ server_chooser.connect_activate(clone!(@weak deck => move |_, _| {
+ deck.navigate(libhandy::NavigationDirection::Forward);
+ }));
credentials.connect_activate(clone!(
- @weak stack,
- @weak back_history as back,
+ @weak err_label,
@weak server_entry,
- @weak err_label
- => move |_, _| {
+ @weak deck => move |_, _| {
if server_entry.get_text().is_empty() {
err_label.show();
} else {
err_label.hide();
- let state = LoginState::Credentials;
- stack.set_visible_child_name(&state.to_string());
- back.borrow_mut().push(state);
+ deck.navigate(libhandy::NavigationDirection::Forward);
}
}));
- back.connect_activate(clone!(@weak stack => move |_, _| {
- back_history.borrow_mut().pop();
- if let Some(state) = back_history.borrow().last() {
- debug!("Go back to state {}", state.to_string());
- stack.set_visible_child_name(&state.to_string());
- } else {
- debug!("There is no state to go back to. Go back to state greeter");
- stack.set_visible_child_name(&LoginState::Greeter.to_string());
+ back.connect_activate(clone!(@weak deck => move |_, _| {
+ if let Some(_) = deck.get_adjacent_child(libhandy::NavigationDirection::Back) {
+ deck.navigate(libhandy::NavigationDirection::Back);
}
}));
@@ -123,8 +106,7 @@ pub fn new(
})
});
- stack.insert_action_group("login", Some(&actions));
- headers.insert_action_group("login", Some(&actions));
+ deck.insert_action_group("login", Some(&actions));
actions
}
diff --git a/fractal-gtk/src/actions/message.rs b/fractal-gtk/src/actions/message.rs
index a3234821..09aa4adc 100644
--- a/fractal-gtk/src/actions/message.rs
+++ b/fractal-gtk/src/actions/message.rs
@@ -162,7 +162,7 @@ pub fn new(
Continue(true)
},
Ok(Ok(fname)) => {
- if let Some(path) = save(&window, &name, &[]) {
+ if let Some(path) = save(&window.upcast_ref(), &name, &[]) {
// TODO use glib to copy file
if fs::copy(fname, path).is_err() {
ErrorDialog::new(false, &i18n("Couldn’t save file"));
diff --git a/fractal-gtk/src/app/connect/headerbar.rs b/fractal-gtk/src/app/connect/headerbar.rs
index 961d5615..99a3e06e 100644
--- a/fractal-gtk/src/app/connect/headerbar.rs
+++ b/fractal-gtk/src/app/connect/headerbar.rs
@@ -1,18 +1,19 @@
use glib::clone;
use gtk::prelude::*;
+use libhandy::HeaderBarExt;
use crate::app::App;
impl App {
pub fn connect_headerbars(&self) {
if let Some(set) = gtk::Settings::get_default() {
- let left_header: gtk::HeaderBar = self
+ let left_header: libhandy::HeaderBar = self
.ui
.builder
.get_object("left-header")
.expect("Can't find left-header in ui file.");
- let right_header: gtk::HeaderBar = self
+ let right_header: libhandy::HeaderBar = self
.ui
.builder
.get_object("room_header_bar")
diff --git a/fractal-gtk/src/app/connect/mod.rs b/fractal-gtk/src/app/connect/mod.rs
index a180abed..2a2514bb 100644
--- a/fractal-gtk/src/app/connect/mod.rs
+++ b/fractal-gtk/src/app/connect/mod.rs
@@ -11,6 +11,7 @@ mod markdown;
mod new_room;
mod roomlist_search;
mod send;
+mod swipeable_widgets;
use crate::app::App;
@@ -34,5 +35,6 @@ impl App {
self.connect_direct_chat();
self.connect_roomlist_search();
+ self.connect_swipeable_widgets();
}
}
diff --git a/fractal-gtk/src/app/connect/swipeable_widgets.rs b/fractal-gtk/src/app/connect/swipeable_widgets.rs
new file mode 100644
index 00000000..6514a205
--- /dev/null
+++ b/fractal-gtk/src/app/connect/swipeable_widgets.rs
@@ -0,0 +1,67 @@
+use gio::prelude::*;
+use gtk::prelude::*;
+use libhandy::prelude::*;
+
+use crate::app::App;
+
+impl App {
+ // Set up HdyDeck and HdyLeaflet so that swipes trigger the
+ // same behaviour as the back button.
+ pub fn connect_swipeable_widgets(&self) {
+ let deck: libhandy::Deck = self
+ .ui
+ .builder
+ .get_object("main_deck")
+ .expect("Can't find main_deck in UI file");
+ let leaflet: libhandy::Leaflet = self
+ .ui
+ .builder
+ .get_object("chat_page")
+ .expect("Can't find chat_page in UI file");
+
+ let app = gio::Application::get_default()
+ .expect("Could not get default application")
+ .downcast::()
+ .unwrap();
+ let global_back = app
+ .lookup_action("back")
+ .expect("Could not get back action");
+
+ deck.connect_property_transition_running_notify(
+ clone!(@weak app, @weak global_back => move |deck| {
+ let child: Option = deck.get_visible_child_name().map(|g| g.to_string());
+ if !deck.get_transition_running() && child == Some("chat".to_string()) {
+ // Re-enable global back when returning to main view
+ let _ = global_back.set_property("enabled", &true);
+ app.activate_action("back", None);
+ }
+ }),
+ );
+
+ deck.connect_property_visible_child_notify(
+ clone!(@weak app, @weak global_back => move |deck| {
+ let child: Option = deck.get_visible_child_name().map(|g| g.to_string());
+ if !deck.get_transition_running() && child == Some("chat".to_string()) {
+ let _ = global_back.set_property("enabled", &true);
+ app.activate_action("back", None);
+ }
+ }),
+ );
+
+ leaflet.connect_property_child_transition_running_notify(
+ clone!(@weak app => move |leaflet| {
+ let child: Option = leaflet.get_visible_child_name().map(|g| g.to_string());
+ if !leaflet.get_child_transition_running() && child == Some("sidebar".to_string()) {
+ app.activate_action("back", None);
+ }
+ }),
+ );
+
+ leaflet.connect_property_visible_child_notify(clone!(@weak app => move |leaflet| {
+ let child: Option = deck.get_visible_child_name().map(|g| g.to_string());
+ if !leaflet.get_child_transition_running() && child == Some("sidebar".to_string()) {
+ app.activate_action("back", None);
+ }
+ }));
+ }
+}
diff --git a/fractal-gtk/src/app/mod.rs b/fractal-gtk/src/app/mod.rs
index 68b7a983..2e9824ff 100644
--- a/fractal-gtk/src/app/mod.rs
+++ b/fractal-gtk/src/app/mod.rs
@@ -41,7 +41,7 @@ macro_rules! APPOP {
// Our application struct for containing all the state we have to carry around.
// TODO: subclass gtk::Application once possible
pub struct App {
- main_window: gtk::ApplicationWindow,
+ main_window: libhandy::ApplicationWindow,
/* Add widget directly here in place of uibuilder::UI*/
ui: uibuilder::UI,
@@ -71,7 +71,7 @@ impl App {
);
let ui = uibuilder::UI::new();
- let window: gtk::ApplicationWindow = ui
+ let window: libhandy::ApplicationWindow = ui
.builder
.get_object("main_window")
.expect("Couldn't find main_window in ui file.");
@@ -98,8 +98,8 @@ impl App {
let leaflet = ui
.builder
- .get_object::("chat_state_leaflet")
- .expect("Can't find chat_state_leaflet in ui file.");
+ .get_object::("chat_page")
+ .expect("Can't find chat_page in ui file.");
let container = ui
.builder
.get_object::("history_container")
@@ -124,33 +124,28 @@ impl App {
}
}));
- let stack = ui
+ let view_stack = ui
.builder
- .get_object::("main_content_stack")
- .expect("Can't find main_content_stack in ui file.");
- let stack_header = ui
- .builder
- .get_object::("headerbar_stack")
- .expect("Can't find headerbar_stack in ui file.");
+ .get_object::("subview_stack")
+ .expect("Can't find subview_stack in ui file.");
- /* Add account settings view to the main stack */
+ /* Add account settings view to the view stack */
let child = ui
.builder
.get_object::("account_settings_box")
.expect("Can't find account_settings_box in ui file.");
- let child_header = ui
- .builder
- .get_object::("account_settings_headerbar")
- .expect("Can't find account_settings_headerbar in ui file.");
- stack.add_named(&child, "account-settings");
- stack_header.add_named(&child_header, "account-settings");
+ view_stack.add_named(&child, "account-settings");
let op = Arc::new(Mutex::new(AppOp::new(ui.clone())));
+ let main_stack = ui
+ .builder
+ .get_object::("main_content_stack")
+ .expect("Can't find main_content_stack in ui file.");
+
// Add login view to the main stack
let login = widgets::LoginWidget::new(&op);
- stack.add_named(&login.container, "login");
- stack_header.add_named(&login.headers, "login");
+ main_stack.add_named(&login.container, "login");
gtk_app.set_accels_for_action("login.back", &["Escape"]);
@@ -175,6 +170,9 @@ impl App {
// Create application
let app = App::new(gtk_app);
+ // Initialize libhandy
+ libhandy::init();
+
gtk_app.connect_activate(clone!(@weak app => move |_| {
app.on_activate();
}));
@@ -186,7 +184,8 @@ impl App {
app.main_window.connect_delete_event(move |window, _| {
let settings: gio::Settings = gio::Settings::new("org.gnome.Fractal");
- let window_state = WindowState::from_window(window);
+ let w = window.upcast_ref();
+ let window_state = WindowState::from_window(w);
if let Err(err) = window_state.save_in_gsettings(&settings) {
error!("Can't save the window settings: {:?}", err);
}
diff --git a/fractal-gtk/src/appop/media_viewer.rs b/fractal-gtk/src/appop/media_viewer.rs
index 8a76fee1..786fe400 100644
--- a/fractal-gtk/src/appop/media_viewer.rs
+++ b/fractal-gtk/src/appop/media_viewer.rs
@@ -18,13 +18,8 @@ impl AppOp {
let stack = self
.ui
.builder
- .get_object::("main_content_stack")
- .expect("Can't find main_content_stack in ui file.");
- let stack_header = self
- .ui
- .builder
- .get_object::("headerbar_stack")
- .expect("Can't find headerbar_stack in ui file.");
+ .get_object::("subview_stack")
+ .expect("Can't find subview_stack in ui file.");
let main_window = self
.ui
@@ -66,12 +61,8 @@ impl AppOp {
if let Some(widget) = stack.get_child_by_name("media-viewer") {
stack.remove(&widget);
}
- if let Some(widget) = stack_header.get_child_by_name("media-viewer") {
- stack_header.remove(&widget);
- }
stack.add_named(&body, "media-viewer");
- stack_header.add_named(&header, "media-viewer");
}
self.set_state(AppState::MediaViewer);
diff --git a/fractal-gtk/src/appop/mod.rs b/fractal-gtk/src/appop/mod.rs
index 328d1094..97d895b8 100644
--- a/fractal-gtk/src/appop/mod.rs
+++ b/fractal-gtk/src/appop/mod.rs
@@ -116,6 +116,7 @@ pub struct AppOp {
pub directory: Vec,
pub leaflet: libhandy::Leaflet,
+ pub deck: libhandy::Deck,
pub thread_pool: ThreadPool,
pub user_info_cache: UserInfoCache,
@@ -127,8 +128,12 @@ impl AppOp {
pub fn new(ui: uibuilder::UI) -> AppOp {
let leaflet = ui
.builder
- .get_object::("header_leaflet")
- .expect("Couldn't find header_leaflet in ui file");
+ .get_object::("chat_page")
+ .expect("Couldn't find chat_page in ui file");
+ let deck = ui
+ .builder
+ .get_object::("main_deck")
+ .expect("Couldn't find main_deck in ui file");
AppOp {
ui,
@@ -159,6 +164,7 @@ impl AppOp {
directory: vec![],
leaflet,
+ deck,
thread_pool: ThreadPool::new(20),
user_info_cache: Arc::new(Mutex::new(
diff --git a/fractal-gtk/src/appop/room_settings.rs b/fractal-gtk/src/appop/room_settings.rs
index 841e109d..f961df67 100644
--- a/fractal-gtk/src/appop/room_settings.rs
+++ b/fractal-gtk/src/appop/room_settings.rs
@@ -16,13 +16,8 @@ impl AppOp {
let stack = self
.ui
.builder
- .get_object::("main_content_stack")
- .expect("Can't find main_content_stack in ui file.");
- let stack_header = self
- .ui
- .builder
- .get_object::("headerbar_stack")
- .expect("Can't find headerbar_stack in ui file.");
+ .get_object::("subview_stack")
+ .expect("Can't find subview_stack in ui file.");
{
let room = self.rooms.get(&self.active_room.clone()?)?;
@@ -33,18 +28,14 @@ impl AppOp {
login_data.server_url,
login_data.access_token,
);
- let (body, header) = panel.create()?;
+ let page = panel.create()?;
/* remove old panel */
if let Some(widget) = stack.get_child_by_name("room-settings") {
stack.remove(&widget);
}
- if let Some(widget) = stack_header.get_child_by_name("room-settings") {
- stack_header.remove(&widget);
- }
- stack.add_named(&body, "room-settings");
- stack_header.add_named(&header, "room-settings");
+ stack.add_named(&page, "room-settings");
self.room_settings = Some(panel);
}
diff --git a/fractal-gtk/src/appop/state.rs b/fractal-gtk/src/appop/state.rs
index 6a062a9b..aa7811bd 100644
--- a/fractal-gtk/src/appop/state.rs
+++ b/fractal-gtk/src/appop/state.rs
@@ -1,5 +1,6 @@
+use gio::prelude::*;
use gtk::prelude::*;
-use libhandy::LeafletExt;
+use libhandy::prelude::*;
use super::RoomSearchPagination;
use crate::actions::AppState;
@@ -8,6 +9,79 @@ use crate::appop::AppOp;
impl AppOp {
pub fn set_state(&mut self, state: AppState) {
self.state = state;
+
+ match self.state {
+ AppState::Login => self.set_stack_state("login"),
+ AppState::NoRoom | AppState::Room => {
+ self.set_stack_state("main_view");
+ self.set_chat_state(state);
+ }
+ AppState::Directory => self.set_deck_state(Some("directory"), state),
+ AppState::Loading => self.set_stack_state("loading"),
+ AppState::AccountSettings => self.set_deck_state(Some("account-settings"), state),
+ AppState::RoomSettings => self.set_deck_state(Some("room-settings"), state),
+ AppState::MediaViewer => self.set_deck_state(Some("media-viewer"), state),
+ };
+
+ //set focus for room directory
+ if let AppState::Directory = self.state {
+ self.ui
+ .builder
+ .get_object::("directory_search_entry")
+ .expect("Can't find widget to set focus in ui file.")
+ .grab_focus();
+ self.directory_pagination = RoomSearchPagination::Initial;
+ self.search_rooms();
+ }
+ }
+
+ fn set_deck_state(&self, view: Option<&str>, state: AppState) {
+ let deck = self
+ .ui
+ .builder
+ .get_object::("main_deck")
+ .expect("Could not find main_deck in ui file");
+ let stack = self
+ .ui
+ .builder
+ .get_object::("subview_stack")
+ .expect("Could not find subview_stack in ui file");
+ let app = gio::Application::get_default().unwrap();
+
+ let global_back = app.lookup_action("back");
+
+ let direction = match state {
+ AppState::Room | AppState::NoRoom => libhandy::NavigationDirection::Back,
+ _ => libhandy::NavigationDirection::Forward,
+ };
+
+ if let Some(v) = view {
+ stack.set_visible_child_name(v);
+ }
+
+ if deck.get_adjacent_child(direction).is_some() {
+ deck.navigate(direction);
+ if direction == libhandy::NavigationDirection::Forward {
+ // Disable global back while in a subview
+ global_back.map(|a| a.set_property("enabled", &false));
+ }
+ }
+ }
+
+ fn set_stack_state(&self, state: &str) {
+ self.ui
+ .builder
+ .get_object::("main_content_stack")
+ .expect("Can't find main_content_stack in ui file.")
+ .set_visible_child_name(state);
+ }
+
+ fn set_chat_state(&mut self, state: AppState) {
+ let deck = self
+ .ui
+ .builder
+ .get_object::("main_deck")
+ .expect("Could not find main_deck in ui file");
let stack = self
.ui
.builder
@@ -16,74 +90,33 @@ impl AppOp {
let headerbar = self
.ui
.builder
- .get_object::("room_header_bar")
+ .get_object::("room_header_bar")
.expect("Can't find room_header_bar in ui file.");
- let widget_name = match self.state {
- AppState::Login => "login",
+ match state {
AppState::NoRoom => {
self.set_state_no_room(&headerbar);
- self.leaflet.set_visible_child_name("sidebar");
+ self.leaflet.navigate(libhandy::NavigationDirection::Back);
stack.set_visible_child_name("noroom");
- "chat"
}
AppState::Room => {
self.set_state_room(&headerbar);
- self.leaflet.set_visible_child_name("content");
+ self.leaflet
+ .navigate(libhandy::NavigationDirection::Forward);
stack.set_visible_child_name("room_view");
- "chat"
}
- AppState::Directory => "directory",
- AppState::Loading => "loading",
- AppState::AccountSettings => "account-settings",
- AppState::RoomSettings => "room-settings",
- AppState::MediaViewer => "media-viewer",
- };
-
- self.ui
- .builder
- .get_object::("main_content_stack")
- .expect("Can't find main_content_stack in ui file.")
- .set_visible_child_name(widget_name);
-
- //setting headerbar
- let bar_name = match self.state {
- AppState::Login => "login",
- AppState::Directory => "back",
- AppState::Loading => "loading",
- AppState::AccountSettings => "account-settings",
- AppState::RoomSettings => "room-settings",
- AppState::MediaViewer => "media-viewer",
- _ => "normal",
- };
-
- self.ui
- .builder
- .get_object::("headerbar_stack")
- .expect("Can't find headerbar_stack in ui file.")
- .set_visible_child_name(bar_name);
-
- //set focus for views
- let widget_focus = match self.state {
- AppState::Directory => "directory_search_entry",
- _ => "",
- };
-
- if !widget_focus.is_empty() {
- self.ui
- .builder
- .get_object::(widget_focus)
- .expect("Can't find widget to set focus in ui file.")
- .grab_focus();
+ _ => (),
}
- if let AppState::Directory = self.state {
- self.directory_pagination = RoomSearchPagination::Initial;
- self.search_rooms();
+ if deck
+ .get_adjacent_child(libhandy::NavigationDirection::Back)
+ .is_some()
+ {
+ deck.navigate(libhandy::NavigationDirection::Back);
}
}
- fn set_state_room(&self, headerbar: >k::HeaderBar) {
+ fn set_state_room(&self, headerbar: &libhandy::HeaderBar) {
for ch in headerbar.get_children().iter() {
ch.show();
}
@@ -105,7 +138,7 @@ impl AppOp {
}
// WORKAROUND this is needed because NoRoom isn't a real app state
- fn set_state_no_room(&mut self, headerbar: >k::HeaderBar) {
+ fn set_state_no_room(&mut self, headerbar: &libhandy::HeaderBar) {
for ch in headerbar.get_children().iter() {
ch.hide();
diff --git a/fractal-gtk/src/main.rs b/fractal-gtk/src/main.rs
index 73326ab1..4d8244c2 100644
--- a/fractal-gtk/src/main.rs
+++ b/fractal-gtk/src/main.rs
@@ -1,4 +1,6 @@
#![deny(dead_code, unused_imports, unused_variables)]
+#[macro_use]
+extern crate glib;
mod backend;
mod client;
diff --git a/fractal-gtk/src/meson.build b/fractal-gtk/src/meson.build
index feb1696c..9c670fb9 100644
--- a/fractal-gtk/src/meson.build
+++ b/fractal-gtk/src/meson.build
@@ -66,6 +66,7 @@ app_sources = files(
'app/connect/new_room.rs',
'app/connect/roomlist_search.rs',
'app/connect/send.rs',
+ 'app/connect/swipeable_widgets.rs',
'app/mod.rs',
'app/windowstate.rs',
'appop/about.rs',
diff --git a/fractal-gtk/src/widgets/login.rs b/fractal-gtk/src/widgets/login.rs
index f9b22f89..8757982f 100644
--- a/fractal-gtk/src/widgets/login.rs
+++ b/fractal-gtk/src/widgets/login.rs
@@ -2,6 +2,7 @@ use fractal_api::url::Url;
use gio::prelude::*;
use glib::clone;
use gtk::prelude::*;
+use libhandy::prelude::*;
use log::info;
use crate::actions;
@@ -18,8 +19,7 @@ use std::sync::{Arc, Mutex};
#[derive(Debug, Clone)]
pub struct LoginWidget {
- pub container: gtk::Stack,
- pub headers: gtk::Stack,
+ pub container: libhandy::Deck,
pub server_entry: gtk::Entry,
pub username_entry: gtk::Entry,
pub password_entry: gtk::Entry,
@@ -157,8 +157,7 @@ impl Default for LoginWidget {
fn default() -> Self {
let builder = gtk::Builder::from_resource("/org/gnome/Fractal/ui/login_flow.ui");
- let container: gtk::Stack = builder.get_object("login_flow_stack").unwrap();
- let headers: gtk::Stack = builder.get_object("login_flow_headers").unwrap();
+ let container: libhandy::Deck = builder.get_object("login_flow_deck").unwrap();
let server_entry = builder.get_object("server_chooser_entry").unwrap();
let username_entry = builder.get_object("username_entry").unwrap();
let password_entry = builder.get_object("password_entry").unwrap();
@@ -166,14 +165,12 @@ impl Default for LoginWidget {
let server_err_label = builder.get_object("server_err_label").unwrap();
let credentials_err_label = builder.get_object("credentials_err_label").unwrap();
- let actions = actions::Login::new(&container, &headers, &server_entry, &server_err_label);
+ let actions = actions::Login::new(&container, &server_entry, &server_err_label);
container.show_all();
- headers.show_all();
LoginWidget {
container,
- headers,
server_entry,
username_entry,
password_entry,
diff --git a/fractal-gtk/src/widgets/media_viewer.rs b/fractal-gtk/src/widgets/media_viewer.rs
index b1475b01..359f253e 100644
--- a/fractal-gtk/src/widgets/media_viewer.rs
+++ b/fractal-gtk/src/widgets/media_viewer.rs
@@ -17,6 +17,7 @@ use glib::signal;
use glib::source::Continue;
use gtk::prelude::*;
use gtk::Overlay;
+use libhandy::HeaderBarExt;
use crate::types::Message;
use crate::types::Room;
@@ -157,7 +158,7 @@ impl Data {
.expect("Can't find media_viewer_headerbar_box in ui file.");
let media_viewer_headerbar = self
.builder
- .get_object::("media_viewer_headerbar")
+ .get_object::("media_viewer_headerbar")
.expect("Can't find media_viewer_headerbar in ui file.");
let headerbar_revealer = self
.builder
@@ -209,7 +210,7 @@ impl Data {
.expect("Can't find media_viewer_headerbar_box in ui file.");
let media_viewer_headerbar = self
.builder
- .get_object::("media_viewer_headerbar")
+ .get_object::("media_viewer_headerbar")
.expect("Can't find media_viewer_headerbar in ui file.");
let headerbar_revealer = self
.builder
@@ -225,7 +226,6 @@ impl Data {
}
media_viewer_headerbar.pack_start(&media_viewer_back_button);
- media_viewer_headerbar.set_child_position(&media_viewer_back_button, 0);
media_viewer_headerbar.set_show_close_button(true);
let full_screen_button_icon = self
@@ -991,7 +991,7 @@ impl MediaViewer {
fn set_header_title(ui: >k::Builder, title: &str) {
let media_viewer_headerbar = ui
- .get_object::("media_viewer_headerbar")
+ .get_object::("media_viewer_headerbar")
.expect("Cant find media_viewer_headerbar in ui file.");
media_viewer_headerbar.set_title(Some(title));
}
diff --git a/fractal-gtk/src/widgets/room_settings.rs b/fractal-gtk/src/widgets/room_settings.rs
index 681454cb..132ec785 100644
--- a/fractal-gtk/src/widgets/room_settings.rs
+++ b/fractal-gtk/src/widgets/room_settings.rs
@@ -68,15 +68,11 @@ impl RoomSettings {
/* creates a empty list with members.len() rows, the content will be loaded when the row is
* drawn */
- pub fn create(&mut self) -> Option<(gtk::Box, gtk::Box)> {
- let body = self
+ pub fn create(&mut self) -> Option {
+ let page = self
.builder
.get_object::("room_settings_box")
.expect("Can't find room_settings_box in ui file.");
- let header = self
- .builder
- .get_object::("room_settings_headerbar")
- .expect("Can't find room_settings_headerbar in ui file.");
let stack = self
.builder
.get_object::("room_settings_stack")
@@ -94,7 +90,7 @@ impl RoomSettings {
self.init_room_settings();
self.connect();
- Some((body, header))
+ Some(page)
}
pub fn connect(&mut self) {