Commit 4a1a7b18 authored by Bilal Elmoussaoui's avatar Bilal Elmoussaoui

use hdy Deck/Carousel to simplify the code

parent ce7b9a4e
...@@ -10,4 +10,7 @@ gdk = "0.13" ...@@ -10,4 +10,7 @@ gdk = "0.13"
gtk = { version = "0.9", features= ["v3_16"] } gtk = { version = "0.9", features= ["v3_16"] }
gio = "0.9" gio = "0.9"
log = "0.4" log = "0.4"
gettext-rs= { version = "0.4", features = ["gettext-system"] } gettext-rs = { version = "0.4", features = ["gettext-system"] }
libhandy = { git = "https://gitlab.gnome.org/World/Rust/libhandy-rs.git" }
pretty_env_logger = "0.4"
anyhow = "1.0"
...@@ -14,7 +14,11 @@ ...@@ -14,7 +14,11 @@
"--share=ipc", "--share=ipc",
"--socket=fallback-x11", "--socket=fallback-x11",
"--socket=wayland", "--socket=wayland",
"--device=dri" "--device=dri",
"--filesystem=xdg-run/dconf",
"--filesystem=~/.config/dconf:ro",
"--talk-name=ca.desrt.dconf",
"--env=DCONF_USER_CONFIG_DIR=.config/dconf"
], ],
"build-options" : { "build-options" : {
"append-path" : "/usr/lib/sdk/rust-stable/bin", "append-path" : "/usr/lib/sdk/rust-stable/bin",
...@@ -26,7 +30,27 @@ ...@@ -26,7 +30,27 @@
"RUST_BACKTRACE" : "1" "RUST_BACKTRACE" : "1"
} }
}, },
"modules" : [ "modules" : [{
"name": "libhandy",
"buildsystem": "meson",
"config-opts": [
"-Dintrospection=disabled",
"-Dgtk_doc=false",
"-Dtests=false",
"-Dexamples=false",
"-Dvapi=false",
"-Dglade_catalog=disabled"
],
"cleanup": [
"/include",
"/lib/pkgconfig"
],
"sources": [{
"type": "git",
"url": "https://gitlab.gnome.org/GNOME/libhandy.git",
"branch": "0.83.0"
}]
},
{ {
"name" : "gnome-tour", "name" : "gnome-tour",
"buildsystem" : "meson", "buildsystem" : "meson",
......
...@@ -58,7 +58,9 @@ impl Application { ...@@ -58,7 +58,9 @@ impl Application {
"next-page", "next-page",
clone!(@strong application => move |_, _| { clone!(@strong application => move |_, _| {
if let Some(window) = &*application.window.borrow().clone() { if let Some(window) = &*application.window.borrow().clone() {
window.next_page(); if window.paginator.borrow_mut().next().is_err() {
window.widget.close();
}
} }
}), }),
); );
...@@ -67,7 +69,9 @@ impl Application { ...@@ -67,7 +69,9 @@ impl Application {
"previous-page", "previous-page",
clone!(@strong application => move |_, _| { clone!(@strong application => move |_, _| {
if let Some(window) = &*application.window.borrow().clone() { if let Some(window) = &*application.window.borrow().clone() {
window.previous_page(); if window.paginator.borrow_mut().previous().is_err() {
window.stop_tour();
}
} }
}), }),
); );
......
...@@ -15,6 +15,7 @@ use application::Application; ...@@ -15,6 +15,7 @@ use application::Application;
use config::{GETTEXT_PACKAGE, LOCALEDIR}; use config::{GETTEXT_PACKAGE, LOCALEDIR};
fn main() { fn main() {
pretty_env_logger::init();
// Prepare i18n // Prepare i18n
setlocale(LocaleCategory::LcAll, ""); setlocale(LocaleCategory::LcAll, "");
bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR); bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
......
...@@ -39,7 +39,6 @@ sources = files( ...@@ -39,7 +39,6 @@ sources = files(
'widgets/pages/image.rs', 'widgets/pages/image.rs',
'widgets/pages/mod.rs', 'widgets/pages/mod.rs',
'widgets/pages/welcome.rs', 'widgets/pages/welcome.rs',
'widgets/headerbar.rs',
'widgets/mod.rs', 'widgets/mod.rs',
'widgets/paginator.rs', 'widgets/paginator.rs',
'widgets/window.rs', 'widgets/window.rs',
......
use gettextrs::gettext;
use gtk::prelude::*;
pub struct HeaderBar {
pub widget: gtk::Stack,
headerbar: gtk::HeaderBar,
title: gtk::Label,
next_btn: gtk::Button,
}
impl HeaderBar {
pub fn new() -> Self {
let widget = gtk::Stack::new();
let headerbar = gtk::HeaderBar::new();
let title = gtk::Label::new(None);
let next_btn = gtk::Button::new();
let headerbar = Self { widget, headerbar, title, next_btn };
headerbar.init();
headerbar
}
pub fn start_tour(&self) {
self.widget.set_visible_child_name("pages");
self.headerbar.set_show_close_button(false);
}
pub fn set_page_nr(&self, page_nr: i32, total_pages: i32) {
if page_nr == total_pages {
self.next_btn.set_label(&gettext("Close"));
} else {
self.next_btn.set_label(&gettext("Next"));
}
}
pub fn set_page_title(&self, title: &str) {
self.title.set_label(title);
}
pub fn end_tour(&self) {
self.widget.set_visible_child_name("welcome");
self.headerbar.set_show_close_button(true);
}
fn init(&self) {
self.headerbar.set_show_close_button(true);
self.headerbar.set_custom_title(Some(&self.title));
self.title.get_style_context().add_class("title");
self.widget.set_hexpand(true);
self.widget.set_transition_type(gtk::StackTransitionType::SlideLeftRight);
self.widget.set_transition_duration(300);
self.widget.get_style_context().add_class("titlebar");
let container = gtk::HeaderBar::new();
container.set_show_close_button(true);
container.set_title(Some(&gettext("Welcome Tour")));
self.widget.add_named(&container, "welcome");
let previous_btn = gtk::Button::new();
previous_btn.add(&gtk::Label::new(Some("Previous")));
previous_btn.set_halign(gtk::Align::Start);
previous_btn.set_action_name(Some("app.previous-page"));
previous_btn.set_hexpand(true);
previous_btn.set_property_width_request(60);
self.next_btn.add(&gtk::Label::new(Some(&gettext("Next"))));
self.next_btn.get_style_context().add_class("suggested-action");
self.next_btn.set_action_name(Some("app.next-page"));
self.next_btn.set_halign(gtk::Align::End);
self.next_btn.set_hexpand(true);
self.next_btn.set_property_width_request(60);
self.headerbar.pack_start(&previous_btn);
self.headerbar.pack_end(&self.next_btn);
self.widget.add_named(&self.headerbar, "pages");
}
}
mod headerbar;
mod pages; mod pages;
mod paginator; mod paginator;
mod window; mod window;
......
use gettextrs::gettext; use gettextrs::gettext;
use gtk::prelude::*; use gtk::prelude::*;
use libhandy::prelude::HeaderBarExt;
pub struct WelcomePageWidget { pub struct WelcomePageWidget {
pub widget: gtk::Box, pub widget: gtk::Box,
...@@ -16,10 +17,14 @@ impl WelcomePageWidget { ...@@ -16,10 +17,14 @@ impl WelcomePageWidget {
} }
fn init(&self) { fn init(&self) {
self.widget.set_valign(gtk::Align::Center); self.widget.set_property_expand(true);
self.widget.set_halign(gtk::Align::Center);
self.widget.set_margin_top(24); let container = gtk::Box::new(gtk::Orientation::Vertical, 0);
self.widget.set_margin_bottom(24); container.set_property_expand(true);
container.set_valign(gtk::Align::Center);
container.set_halign(gtk::Align::Center);
container.set_margin_top(24);
container.set_margin_bottom(24);
let name = glib::get_os_info("NAME").unwrap_or("GNOME".into()); let name = glib::get_os_info("NAME").unwrap_or("GNOME".into());
let version = glib::get_os_info("VERSION").unwrap_or("3.36".into()); let version = glib::get_os_info("VERSION").unwrap_or("3.36".into());
...@@ -27,17 +32,17 @@ impl WelcomePageWidget { ...@@ -27,17 +32,17 @@ impl WelcomePageWidget {
let logo = gtk::Image::from_icon_name(Some(&icon), gtk::IconSize::Dialog); let logo = gtk::Image::from_icon_name(Some(&icon), gtk::IconSize::Dialog);
logo.set_pixel_size(196); logo.set_pixel_size(196);
self.widget.add(&logo); container.add(&logo);
let title = gtk::Label::new(Some(&gettext(format!("Welcome to {} {}", name, version)))); let title = gtk::Label::new(Some(&gettext(format!("Welcome to {} {}", name, version))));
title.set_margin_top(36); title.set_margin_top(36);
title.get_style_context().add_class("large-title"); title.get_style_context().add_class("large-title");
self.widget.add(&title); container.add(&title);
let text = gtk::Label::new(Some(&gettext("Hi there! If you are new to GNOME, you can take the tour to learn some essential features."))); let text = gtk::Label::new(Some(&gettext("Hi there! If you are new to GNOME, you can take the tour to learn some essential features.")));
text.get_style_context().add_class("body"); text.get_style_context().add_class("body");
text.set_margin_top(12); text.set_margin_top(12);
self.widget.add(&text); container.add(&text);
let actions_container = gtk::Box::new(gtk::Orientation::Horizontal, 12); let actions_container = gtk::Box::new(gtk::Orientation::Horizontal, 12);
actions_container.set_halign(gtk::Align::Center); actions_container.set_halign(gtk::Align::Center);
...@@ -60,6 +65,13 @@ impl WelcomePageWidget { ...@@ -60,6 +65,13 @@ impl WelcomePageWidget {
actions_container.add(&start_tour_btn); actions_container.add(&start_tour_btn);
actions_container.set_focus_child(Some(&start_tour_btn)); actions_container.set_focus_child(Some(&start_tour_btn));
self.widget.add(&actions_container); container.add(&actions_container);
let headerbar = libhandy::HeaderBar::new();
headerbar.set_show_close_button(true);
headerbar.set_title(Some(&gettext("Welcome Tour")));
self.widget.add(&headerbar);
self.widget.add(&container);
} }
} }
use anyhow::Result;
use gettextrs::gettext;
use gtk::prelude::*; use gtk::prelude::*;
use std::cell::RefCell; use std::cell::RefCell;
use std::convert::TryInto; use std::rc::Rc;
use super::pages::Pageable; use super::pages::Pageable;
use libhandy::prelude::{CarouselExt, HeaderBarExt};
pub struct PaginatorWidget { pub struct PaginatorWidget {
pub widget: gtk::Stack, pub widget: gtk::Box,
pages: Vec<Box<dyn Pageable>>, carousel: libhandy::Carousel,
current_page: RefCell<i32>, headerbar: libhandy::HeaderBar,
pages: RefCell<Vec<Box<dyn Pageable>>>,
current_page: RefCell<u32>,
next_btn: gtk::Button,
} }
impl PaginatorWidget { impl PaginatorWidget {
pub fn new() -> Self { pub fn new() -> Rc<Self> {
let widget = gtk::Stack::new(); let widget = gtk::Box::new(gtk::Orientation::Vertical, 0);
let paginator = Self { let paginator = Rc::new(Self {
widget, widget,
pages: Vec::new(), carousel: libhandy::Carousel::new(),
current_page: RefCell::new(1), headerbar: libhandy::HeaderBar::new(),
}; next_btn: gtk::Button::new(),
paginator.init(); pages: RefCell::new(Vec::new()),
current_page: RefCell::new(0),
});
paginator.init(paginator.clone());
paginator paginator
} }
pub fn get_total_pages(&self) -> i32 { pub fn next(&self) -> Result<()> {
self.pages.len().try_into().unwrap_or(1) let p = *self.current_page.borrow() + 1;
if p == self.carousel.get_n_pages() {
anyhow::bail!("Already at the latest page");
}
self.set_page(p);
Ok(())
} }
pub fn get_current_page_nr(&self) -> i32 { pub fn previous(&self) -> Result<()> {
*self.current_page.borrow() let p = *self.current_page.borrow();
if p == 0 {
anyhow::bail!("Already at the first page");
}
self.set_page(p - 1);
Ok(())
} }
pub fn get_current_page(&self) -> Option<&Box<dyn Pageable>> { pub fn add_page(&self, page: Box<dyn Pageable>) {
let current_page_idx: usize = (self.get_current_page_nr() - 1).try_into().unwrap_or(0); let page_nr = self.pages.borrow().len();
self.pages.get(current_page_idx) self.carousel.insert(&page.get_widget(), page_nr as i32);
self.pages.borrow_mut().push(page);
} }
pub fn next(&self) { fn init(&self, p: Rc<Self>) {
let next_page = *self.current_page.borrow() + 1; self.carousel.set_property_expand(true);
self.go_to(next_page); self.carousel.set_animation_duration(300);
}
pub fn previous(&self) { self.carousel.connect_page_changed(clone!(@weak p => move |carousel, page_nr| {
let previous_page = *self.current_page.borrow() - 1; let pages = &p.pages.borrow();
self.go_to(previous_page); let page = pages.get(page_nr as usize).unwrap();
} p.headerbar.set_title(Some(&page.get_title()));
pub fn add_page(&mut self, page: Box<dyn Pageable>) { if page_nr == carousel.get_n_pages() - 1 {
let page_nr = self.pages.len() + 1; p.next_btn.set_label(&gettext("Close"));
let page_name = format!("page-{}", page_nr); } else {
p.next_btn.set_label(&gettext("Next"));
}
p.current_page.replace(page_nr);
}));
self.widget.add_named(&page.get_widget(), &page_name); let previous_btn = gtk::Button::new();
self.pages.push(page); previous_btn.add(&gtk::Label::new(Some("Previous")));
} previous_btn.set_halign(gtk::Align::Start);
previous_btn.set_action_name(Some("app.previous-page"));
previous_btn.set_hexpand(true);
previous_btn.set_property_width_request(60);
fn init(&self) { self.next_btn.add(&gtk::Label::new(Some(&gettext("Next"))));
self.widget.set_transition_type(gtk::StackTransitionType::SlideLeftRight); self.next_btn.get_style_context().add_class("suggested-action");
self.widget.set_transition_duration(300); self.next_btn.set_action_name(Some("app.next-page"));
} self.next_btn.set_halign(gtk::Align::End);
self.next_btn.set_hexpand(true);
self.next_btn.set_property_width_request(60);
fn go_to(&self, page_nr: i32) { self.headerbar.pack_start(&previous_btn);
let page_name = format!("page-{}", page_nr); self.headerbar.pack_end(&self.next_btn);
let total_pages: i32 = self.pages.len().try_into().unwrap_or(0); self.headerbar.set_show_close_button(false);
self.widget.add(&self.headerbar);
self.widget.add(&self.carousel);
}
if page_nr <= total_pages && self.widget.get_child_by_name(&page_name).is_some() { pub fn set_page(&self, page_nr: u32) {
self.current_page.replace(page_nr); if page_nr < self.carousel.get_n_pages() {
self.widget.set_visible_child_name(&page_name); let pages = &self.pages.borrow();
let page = pages.get(page_nr as usize).unwrap();
self.carousel.scroll_to(&page.get_widget());
} }
} }
} }
use gettextrs::gettext; use gettextrs::gettext;
use gtk::prelude::*; use gtk::prelude::*;
use std::cell::RefCell;
use std::rc::Rc;
use super::headerbar::HeaderBar;
use super::pages::{ImagePageWidget, WelcomePageWidget}; use super::pages::{ImagePageWidget, WelcomePageWidget};
use super::paginator::PaginatorWidget; use super::paginator::PaginatorWidget;
use crate::config::PROFILE; use crate::config::PROFILE;
use libhandy::prelude::DeckExt;
pub struct Window { pub struct Window {
pub widget: gtk::ApplicationWindow, pub widget: libhandy::ApplicationWindow,
container: gtk::Stack, deck: libhandy::Deck,
headerbar: HeaderBar, pub paginator: RefCell<Rc<PaginatorWidget>>,
paginator: PaginatorWidget, welcome_page: WelcomePageWidget,
} }
impl Window { impl Window {
pub fn new(app: &gtk::Application) -> Self { pub fn new(app: &gtk::Application) -> Self {
let widget = gtk::ApplicationWindow::new(app); let widget = libhandy::ApplicationWindow::new();
let container = gtk::Stack::new(); widget.set_application(Some(app));
let headerbar = HeaderBar::new();
let paginator = PaginatorWidget::new(); let deck = libhandy::Deck::new();
let paginator = RefCell::new(PaginatorWidget::new());
let mut window_widget = Window { let mut window_widget = Window {
widget, widget,
container, deck,
headerbar, welcome_page: WelcomePageWidget::new(),
paginator, paginator,
}; };
...@@ -32,91 +35,55 @@ impl Window { ...@@ -32,91 +35,55 @@ impl Window {
} }
pub fn start_tour(&self) { pub fn start_tour(&self) {
if let Some(page) = self.paginator.get_current_page() { self.deck.set_visible_child(&self.paginator.borrow().widget);
self.headerbar.set_page_title(&page.get_title()); self.paginator.borrow_mut().set_page(0);
}
self.container.set_visible_child_name("pages");
self.headerbar.start_tour();
}
fn end_tour(&self) {
self.container.set_visible_child_name("welcome");
self.headerbar.end_tour();
}
pub fn next_page(&self) {
let total_pages = self.paginator.get_total_pages();
let current_page = self.paginator.get_current_page_nr();
self.headerbar.set_page_nr(current_page + 1, total_pages);
if current_page == total_pages {
self.widget.close();
} else {
self.paginator.next();
}
if let Some(page) = self.paginator.get_current_page() {
self.headerbar.set_page_title(&page.get_title());
}
} }
pub fn previous_page(&self) { pub fn stop_tour(&self) {
let total_pages = self.paginator.get_total_pages(); self.paginator.borrow_mut().set_page(0);
let current_page = self.paginator.get_current_page_nr(); self.deck.set_visible_child(&self.welcome_page.widget);
self.headerbar.set_page_nr(current_page - 1, total_pages);
match current_page {
1 => self.end_tour(),
_ => self.paginator.previous(),
}
if let Some(page) = self.paginator.get_current_page() {
self.headerbar.set_page_title(&page.get_title());
}
} }
fn init(&mut self) { fn init(&mut self) {
self.widget.set_default_size(920, 640); self.widget.set_default_size(920, 640);
self.container.set_transition_type(gtk::StackTransitionType::SlideLeftRight); self.deck.set_transition_type(libhandy::DeckTransitionType::Slide);
self.container.set_transition_duration(300); self.deck.set_transition_duration(300);
// Devel Profile // Devel Profile
if PROFILE == "Devel" { if PROFILE == "Devel" {
self.widget.get_style_context().add_class("devel"); self.widget.get_style_context().add_class("devel");
} }
self.widget.set_titlebar(Some(&self.headerbar.widget)); self.deck.add(&self.welcome_page.widget);
let welcome_page = WelcomePageWidget::new();
self.container.add_named(&welcome_page.widget, "welcome");
self.paginator.add_page(Box::new(ImagePageWidget::new( self.paginator.borrow().add_page(Box::new(ImagePageWidget::new(
"/org/gnome/Tour/activities.svg", "/org/gnome/Tour/activities.svg",
gettext("Activities Overview"), gettext("Activities Overview"),
gettext("Open Activities to start apps"), gettext("Open Activities to start apps"),
gettext("You can also view open windows, search and use workspaces."), gettext("You can also view open windows, search and use workspaces."),
))); )));
self.paginator.add_page(Box::new(ImagePageWidget::new( self.paginator.borrow_mut().add_page(Box::new(ImagePageWidget::new(
"/org/gnome/Tour/search.svg", "/org/gnome/Tour/search.svg",
gettext("Search"), gettext("Search"),
gettext("In the Activities Overview, just start typing to search"), gettext("In the Activities Overview, just start typing to search"),
gettext("Search can be used to launch apps, find settings, do calculations and much more."), gettext("Search can be used to launch apps, find settings, do calculations and much more."),
))); )));
self.paginator.add_page(Box::new(ImagePageWidget::new( self.paginator.borrow_mut().add_page(Box::new(ImagePageWidget::new(
"/org/gnome/Tour/calendar.svg", "/org/gnome/Tour/calendar.svg",
gettext("Date & Time"), gettext("Date & Time"),
gettext("Click the time to see your now and next"), gettext("Click the time to see your now and next"),
gettext("This includes notifications, media controls, calendar events, the weather and world clocks."), gettext("This includes notifications, media controls, calendar events, the weather and world clocks."),
))); )));
self.paginator.add_page(Box::new(ImagePageWidget::new( self.paginator.borrow_mut().add_page(Box::new(ImagePageWidget::new(
"/org/gnome/Tour/status-menu.svg", "/org/gnome/Tour/status-menu.svg",
gettext("System Menu"), gettext("System Menu"),
gettext("View system information and settings"), gettext("View system information and settings"),
gettext("Get an overview of the system status and quickly change settings."), gettext("Get an overview of the system status and quickly change settings."),
))); )));
self.paginator.add_page(Box::new(ImagePageWidget::new( self.paginator.borrow_mut().add_page(Box::new(ImagePageWidget::new(
"/org/gnome/Tour/software.svg", "/org/gnome/Tour/software.svg",
gettext("Software"), gettext("Software"),
gettext("Find and install apps"), gettext("Find and install apps"),
...@@ -130,9 +97,9 @@ impl Window { ...@@ -130,9 +97,9 @@ impl Window {
gettext("The help app contains information, tips and tricks."), gettext("The help app contains information, tips and tricks."),
); );
last_page.widget.get_style_context().add_class("last-page"); last_page.widget.get_style_context().add_class("last-page");
self.paginator.add_page(Box::new(last_page)); self.paginator.borrow_mut().add_page(Box::new(last_page));
self.container.add_named(&self.paginator.widget, "pages"); self.deck.add(&self.paginator.borrow().widget);
self.widget.add(&self.container); self.widget.add(&self.deck);
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment