Commit d7ed0114 authored by Bilal Elmoussaoui's avatar Bilal Elmoussaoui

Load the banners bg's from resource dir & handle errors

parent 9c17de23
Pipeline #103242 failed with stages
......@@ -12,6 +12,7 @@
],
"desktop-file-name-suffix" : " (Devel)",
"finish-args" : [
"--filesystem=home",
"--filesystem=xdg-run/dconf",
"--filesystem=~/.config/dconf:ro",
"--filesystem=xdg-music",
......@@ -20,8 +21,7 @@
"--share=ipc",
"--socket=x11",
"--socket=wayland",
"--device=dri",
"--filesystem=host"
"--device=dri"
],
"build-options" : {
"append-path" : "/usr/lib/sdk/rust-stable/bin",
......
.errors-count{
background-color: #FF7800;
color: white;
font-weight: bold;
font-size:12px;
padding: 2px 4px;
border-radius: 2px
}
button:disabled .errors-count {
background-color: #9A9996;
color: white;
}
button:disabled label {
color: #C0BFBC;
}
.head-title{
font-size: 24px;
font-weight: bold;
......@@ -6,3 +22,5 @@
.banner-widget{
border: 1px solid mix(@theme_fg_color, @theme_bg_color, 0.7);
}
......@@ -19,7 +19,7 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="action_name">win.open</property>
<property name="action_name">window.open</property>
</object>
<packing>
<property name="position">1</property>
......@@ -46,6 +46,7 @@
<child>
<object class="GtkToggleButton" id="errors_togglebtn">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<child>
......@@ -64,6 +65,24 @@
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="errors_count_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="label" translatable="yes">0</property>
<style>
<class name="errors-count"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="padding">6</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkImage">
<property name="visible">True</property>
......@@ -73,7 +92,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
<property name="position">2</property>
</packing>
</child>
</object>
......@@ -202,4 +221,22 @@ Software's source code can be found &lt;a href="https://gitlab.gnome.org/GNOME/g
</object>
</child>
</object>
<object class="GtkPopover" id="errors_popover">
<property name="can_focus">False</property>
<property name="relative_to">errors_togglebtn</property>
<child>
<object class="GtkBox" id="errors_container">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="border_width">12</property>
<property name="orientation">vertical</property>
<child>
<placeholder/>
</child>
<style>
<class name="errors-container"/>
</style>
</object>
</child>
</object>
</interface>
......@@ -3,6 +3,7 @@ use crate::config;
use crate::widgets::{View, Window};
use gio::prelude::*;
use gtk::prelude::*;
use std::collections::HashMap;
use std::env;
use std::{cell::RefCell, rc::Rc};
......@@ -11,7 +12,8 @@ pub enum Action {
OpenFile,
ViewEmpty,
ViewBanners,
StyleSheetError(String),
StyleSheetError(String, String), // (id, error)
StyleSheetFixed(String), // id
}
pub struct Application {
......@@ -20,6 +22,7 @@ pub struct Application {
sender: Sender<Action>,
receiver: RefCell<Option<Receiver<Action>>>,
banners: banners::Banners,
pub errors: RefCell<HashMap<String, String>>, // (id, the error)
}
impl Application {
......@@ -44,6 +47,7 @@ impl Application {
sender,
receiver,
banners,
errors: RefCell::new(HashMap::new()),
});
application.setup_gactions();
......@@ -104,7 +108,13 @@ impl Application {
}
fn open_file(&self) {
let open_dialog = gtk::FileChooserNative::new(Some("Select a file"), Some(&self.window.widget), gtk::FileChooserAction::Open, Some("Open"), Some("Close"));
let open_dialog = gtk::FileChooserDialog::with_buttons(
Some("Open File"),
Some(&self.window.widget),
gtk::FileChooserAction::Open,
&[("_Cancel", gtk::ResponseType::Cancel), ("_Open", gtk::ResponseType::Accept)],
);
let xml_filter = gtk::FileFilter::new();
xml_filter.set_name(Some("application/xml"));
xml_filter.add_mime_type("application/xml");
......@@ -112,8 +122,9 @@ impl Application {
if gtk::ResponseType::from(open_dialog.run()) == gtk::ResponseType::Accept {
let file = open_dialog.get_file().unwrap();
self.sender.send(Action::ViewBanners).unwrap();
self.sender.send(Action::ViewEmpty).unwrap();
self.banners.load_file(file);
self.sender.send(Action::ViewBanners).unwrap();
};
open_dialog.destroy();
}
......@@ -128,8 +139,13 @@ impl Application {
self.window.load_banners(&self.banners);
self.window.set_view(View::Banners);
}
Action::StyleSheetError(error) => {
println!("error {}", error);
Action::StyleSheetError(id, error) => {
self.errors.borrow_mut().insert(id, error);
self.window.set_errors(self.errors.borrow());
}
Action::StyleSheetFixed(id) => {
self.errors.borrow_mut().remove(&id);
self.window.set_errors(self.errors.borrow());
}
};
glib::Continue(true)
......
use libxml::tree::*;
pub struct Banner {
pub id: String,
pub css: String,
}
use crate::banner::Banner;
use gio::prelude::*;
use libxml::parser::Parser;
use libxml::tree::*;
use std::{cell::RefCell, rc::Rc};
#[derive(Clone)]
pub struct Banner {
pub id: String,
pub css: String,
pub resources_dir: String,
}
pub struct Banners {
file: Option<gio::File>,
pub elements: Rc<RefCell<Vec<Banner>>>,
}
impl Banners {
pub fn new() -> Self {
Banners {
file: None,
elements: Rc::new(RefCell::new(Vec::new())),
}
}
pub fn load_file(&self, file: gio::File) {
self.elements.borrow_mut().clear();
// TODO: monitor the file
let parser = Parser::default();
let filepath = file.get_path().unwrap();
let mut filepath = file.get_path().unwrap();
info!("Parsing {:?}", filepath);
let document = parser.parse_file(&filepath.to_str().unwrap()).unwrap();
let root = document.get_root_element().unwrap();
let components = root.findnodes("//components/component").unwrap();
filepath.pop(); // remove the filename for the PathBuf
let dirname = filepath.to_str().unwrap();
for component in components.iter() {
let id = component.findnodes("id").unwrap().get(0).unwrap().get_content();
let css = component.findnodes("*/value").unwrap().get(0).unwrap().get_content();
let banner = Banner { id, css };
let banner = Banner {
id,
css,
resources_dir: dirname.to_string(),
};
self.elements.borrow_mut().push(banner);
}
}
......
......@@ -10,7 +10,6 @@ extern crate sourceview;
use gettextrs::*;
mod application;
mod banner;
mod banners;
mod config;
mod static_resources;
......
......@@ -40,7 +40,6 @@ sources = files(
'widgets/mod.rs',
'widgets/window.rs',
'application.rs',
'banner.rs',
'banners.rs',
'config.rs',
'main.rs',
......
use crate::application::Action;
use crate::banner;
use crate::banners;
use glib::Sender;
use gtk::prelude::*;
use sourceview::prelude::*;
......@@ -9,29 +9,31 @@ pub struct Banner {
pub widget: gtk::Box,
builder: gtk::Builder,
sender: Sender<Action>,
banner: banners::Banner,
}
// Contains an EventBox & Revealer(SourceView)
impl Banner {
pub fn new(sender: Sender<Action>, banner: &banner::Banner) -> Rc<Self> {
pub fn new(sender: Sender<Action>, banner: banners::Banner) -> Rc<Self> {
let builder = gtk::Builder::new_from_resource("/org/gnome/design/BannerViewer/banner.ui");
let widget: gtk::Box = builder.get_object("banner").unwrap();
widget.get_style_context().add_class("banner-widget");
widget.show();
let b = Rc::new(Banner { widget, builder, sender });
let b = Rc::new(Banner { widget, builder, sender, banner });
b.init(b.clone(), banner);
b.init(b.clone());
b
}
fn init(&self, banner_widget: Rc<Self>, banner: &banner::Banner) {
fn init(&self, banner_widget: Rc<Self>) {
let event_box: gtk::EventBox = self.builder.get_object("banner_eventbox").unwrap();
let code_revealer: gtk::Revealer = self.builder.get_object("source_revealer").unwrap();
let source_view: sourceview::View = self.builder.get_object("source_view").unwrap();
let source_buffer: sourceview::Buffer = self.builder.get_object("source_buffer").unwrap();
let undo_manager = source_buffer.get_undo_manager().unwrap();
self.inject_css(&banner.css);
self.inject_css(&self.banner.css);
code_revealer.show();
source_view.show();
event_box.show();
......@@ -42,7 +44,7 @@ impl Banner {
source_buffer.set_highlight_syntax(true);
undo_manager.begin_not_undoable_action();
source_buffer.set_text(&banner.css);
source_buffer.set_text(&self.banner.css);
undo_manager.end_not_undoable_action();
source_view.set_monospace(true);
......@@ -68,14 +70,15 @@ impl Banner {
let event_box: gtk::EventBox = self.builder.get_object("banner_eventbox").unwrap();
// Inject the banner CSS
let provider = gtk::CssProvider::new();
let css = css.replace("@datadir@", "/var/run/host/usr/share");
let css = css.replace("@datadir@/gnome-software", &self.banner.resources_dir);
let css = format!(".banner{{ {} }}", css);
let banner_id = &self.banner.id;
match provider.load_from_data(css.as_bytes()) {
Ok(_) => (),
Ok(_) => self.sender.send(Action::StyleSheetFixed(banner_id.to_string())).unwrap(),
Err(err) => {
warn!("Couldn't load stylesheet of banner {}", err);
self.sender.send(Action::StyleSheetError(err.to_string())).unwrap();
self.sender.send(Action::StyleSheetError(banner_id.to_string(), err.to_string())).unwrap();
}
};
event_box.get_style_context().add_provider(&provider, 300);
......
......@@ -6,6 +6,8 @@ use crate::window_state;
use gio::prelude::*;
use glib::Sender;
use gtk::prelude::*;
use std::cell::Ref;
use std::collections::HashMap;
pub enum View {
Empty,
......@@ -26,7 +28,6 @@ impl Window {
if PROFILE == "Devel" {
widget.get_style_context().add_class("devel");
}
let window = Window { widget, sender, builder };
window.init(settings);
......@@ -42,7 +43,7 @@ impl Window {
}
for elem in banners.elements.borrow().iter() {
let sender = self.sender.clone();
let banner_widget = banner::Banner::new(sender, elem);
let banner_widget = banner::Banner::new(sender, elem.clone());
banner_widget.widget.show();
banners_container.pack_start(&banner_widget.widget, false, false, 18);
}
......@@ -50,9 +51,12 @@ impl Window {
pub fn set_view(&self, view: View) {
let main_stack: gtk::Stack = self.builder.get_object("main_stack").unwrap();
let banners_container: gtk::Box = self.builder.get_object("banners").unwrap();
match view {
View::Empty => {
for child in banners_container.get_children() {
banners_container.remove(&child);
}
main_stack.set_visible_child_name("empty_state");
}
View::Banners => {
......@@ -61,6 +65,41 @@ impl Window {
}
}
pub fn set_errors(&self, errors: Ref<HashMap<String, String>>) {
let errors_togglebtn: gtk::ToggleButton = self.builder.get_object("errors_togglebtn").unwrap();
let errors_count_label: gtk::Label = self.builder.get_object("errors_count_label").unwrap();
let errors_count = errors.len();
if errors_count != 0 {
let errors_container: gtk::Box = self.builder.get_object("errors_container").unwrap();
// Empty the old errors
for error_label in errors_container.get_children() {
errors_container.remove(&error_label);
}
for (id, error) in errors.iter() {
let error_box = gtk::Box::new(gtk::Orientation::Vertical, 6);
error_box.show();
let error_app = gtk::Label::new(Some(id));
error_app.set_halign(gtk::Align::Start);
error_app.get_style_context().add_class("dim-label");
error_app.show();
error_box.pack_start(&error_app, false, false, 0);
let error_label = gtk::Label::new(Some(error));
error_label.set_halign(gtk::Align::Start);
error_label.show();
error_box.pack_start(&error_label, false, false, 0);
errors_container.pack_start(&error_box, false, false, 12);
}
}
errors_count_label.set_text(&errors_count.to_string());
errors_togglebtn.set_sensitive(errors_count != 0);
}
fn init(&self, settings: gio::Settings) {
// setup app menu
let appmenu_btn: gtk::MenuButton = self.builder.get_object("appmenu_button").unwrap();
......@@ -69,12 +108,29 @@ impl Window {
appmenu_btn.set_menu_model(Some(&popover_menu));
// load latest window state
window_state::load(&self.widget, &settings);
// save window state on delete event
self.widget.connect_delete_event(move |window, _| {
window_state::save(&window, &settings);
Inhibit(false)
});
// Errors popover
let errors_togglebtn: gtk::ToggleButton = self.builder.get_object("errors_togglebtn").unwrap();
let errors_popover: gtk::Popover = self.builder.get_object("errors_popover").unwrap();
let toggle_btn = errors_togglebtn.clone();
errors_popover.connect_property_visible_notify(move |popover| {
if !popover.get_visible() {
toggle_btn.set_active(false);
}
});
errors_togglebtn.connect_toggled(move |togglebtn| {
if togglebtn.get_active() {
errors_popover.popup();
} else {
errors_popover.popdown();
}
});
}
fn setup_actions(&self) {
......@@ -85,6 +141,6 @@ impl Window {
sender.send(Action::OpenFile).unwrap();
});
actions.add_action(&open);
self.widget.insert_action_group("win", Some(&actions));
self.widget.insert_action_group("window", Some(&actions));
}
}
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