Commit 3ea0810a authored by Bilal Elmoussaoui's avatar Bilal Elmoussaoui

Rewrite parts of the logic

Ideally, we would use gio::ListStore for the ListBoxes we do use but that means no filters.
I would wait till we have at least subclasses to re-write it
parent d1223a80
Pipeline #108061 passed with stages
in 6 minutes and 46 seconds
......@@ -81,7 +81,6 @@ rustfmt:
# Create blank versions of our configured files
# so rustfmt does not yell about non-existent files or completely empty files
- echo -e "" >> src/config.rs
- echo -e "" >> src/static_resources.rs
- rustc -Vv && cargo -Vv
- cargo fmt --version
- cargo fmt --all -- --color=always --check
......
......@@ -300,7 +300,7 @@ dependencies = [
[[package]]
name = "icon-library"
version = "0.0.1"
version = "0.0.2"
dependencies = [
"gdk 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gettext-rs 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
......
[package]
name = "icon-library"
version = "0.0.1"
version = "0.0.2"
authors = ["Bilal Elmoussaoui <bilal.elmoussaoui@gnome.org>"]
edition = "2018"
[dependencies]
gtk = { version = "0.7.0", features = ["v3_24"] }
glib = "0.8.1"
gio = "0.7.0"
gdk = "0.11.0"
gtk = { version = "0.7", features = ["v3_24"] }
glib = "0.8"
gio = "0.7"
gdk = "0.11"
log = "0.4"
gettext-rs= { version = "0.4.1", features = ["gettext-system"] }
libhandy = "0.4.0"
serde = "1.0.97"
serde_json = "1.0.40"
serde_derive = "1.0.97"
pretty_env_logger = "0.3.0"
gettext-rs= { version = "0.4", features = ["gettext-system"] }
libhandy = "0.4"
serde = "1.0"
serde_json = "1.0"
serde_derive = "1.0"
pretty_env_logger = "0.3"
......@@ -95,7 +95,9 @@ resources = gnome.compile_resources(
'resources.gresource.xml',
gresource_bundle: true,
source_dir: meson.current_build_dir(),
dependencies: ui_dependencies
dependencies: ui_dependencies,
install: true,
install_dir: pkgdatadir,
)
install_subdir(
......
......@@ -2,9 +2,9 @@
<gresources>
<gresource prefix="/org/gnome/design/IconLibrary/">
<file compressed="true" preprocess="xml-stripblanks">about_dialog.ui</file>
<file compressed="true" preprocess="xml-stripblanks" alias="icons_view.ui">resources/ui/icons_view.ui</file>
<file compressed="true" preprocess="xml-stripblanks" alias="export_dialog.ui">resources/ui/export_dialog.ui</file>
<file compressed="true" preprocess="xml-stripblanks" alias="shortcuts.ui">resources/ui/shortcuts.ui</file>
<file compressed="true" preprocess="xml-stripblanks" alias="menu.ui">resources/ui/menu.ui</file>
<file compressed="true" preprocess="xml-stripblanks">window.ui</file>
<file compressed="true" alias="style.css">resources/style.css</file>
</gresource>
......
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<requires lib="libhandy" version="0.0"/>
<object class="GtkStack" id="icons_view">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="transition_type">slide-left-right</property>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<child>
<object class="GtkViewport">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="shadow_type">none</property>
<child>
<object class="HdyColumn">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_bottom">24</property>
<property name="maximum_width">900</property>
<property name="linear_growth_width">900</property>
<child>
<object class="GtkBox" id="icons_container">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<placeholder/>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="name">results</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="pixel_size">192</property>
<property name="icon_name">system-search-symbolic</property>
<property name="icon_size">5</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">No results found</property>
<style>
<class name="no-results-label"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<style>
<class name="dim-label"/>
</style>
</object>
<packing>
<property name="name">no-results</property>
<property name="position">1</property>
</packing>
</child>
</object>
</interface>
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<!-- interface-requires gtk+ 3.0 -->
<menu id="popover_menu">
<section>
<item>
<attribute name="label" translatable="yes">_Dark Mode</attribute>
<attribute name="action">app.dark-mode</attribute>
</item>
</section>
<section>
<item>
<attribute name="label" translatable="yes">_Keyboard Shortcuts</attribute>
<attribute name="action">win.show-help-overlay</attribute>
</item>
<item>
<attribute name="label" translatable="yes">_About Icon Library</attribute>
<attribute name="action">app.about</attribute>
</item>
</section>
</menu>
</interface>
......@@ -3,6 +3,75 @@
<interface>
<requires lib="gtk+" version="3.20"/>
<requires lib="libhandy" version="0.0"/>
<object class="GtkPopoverMenu" id="popover">
<property name="width_request">200</property>
<property name="can_focus">False</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="border_width">10</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkCheckButton">
<property name="label" translatable="yes">Dark Mode</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="action_name">app.dark-mode</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="action_name">win.show-help-overlay</property>
<property name="text" translatable="yes">_Keyboard Shortcuts</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="action_name">app.about</property>
<property name="text" translatable="yes">About Icon Library</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">7</property>
</packing>
</child>
</object>
<packing>
<property name="submenu">main</property>
</packing>
</child>
</object>
<object class="GtkApplicationWindow" id="window">
<property name="can_focus">False</property>
<property name="default_width">650</property>
......@@ -39,6 +108,7 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="popover">popover</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
......
project('icon-library',
'rust',
version: '0.0.1',
version: '0.0.2',
license: 'GPL3')
i18n = import('i18n')
......
......@@ -3,7 +3,8 @@ data/org.gnome.design.IconLibrary.desktop.in.in
data/org.gnome.design.IconLibrary.gschema.xml.in
data/resources/ui/about_dialog.ui.in
data/resources/ui/export_dialog.ui
data/resources/ui/menu.ui
data/resources/ui/icons_view.ui
data/resources/ui/shortcuts.ui
data/resources/ui/window.ui.in
src/widgets/export.rs
src/widgets/icons.rs
use crate::config;
use crate::icon::Icon;
use crate::widgets::ExportDialog;
use crate::window::Window;
use gio::prelude::*;
use glib::{Receiver, Sender};
use gtk::prelude::*;
use std::env;
use std::path::PathBuf;
use std::{cell::RefCell, rc::Rc};
pub enum Action {
Export(Icon),
}
pub struct Application {
app: gtk::Application,
window: Window,
sender: Sender<Action>,
receiver: RefCell<Option<Receiver<Action>>>,
}
impl Application {
pub fn new() -> Self {
let app = gtk::Application::new(Some(config::APP_ID), gio::ApplicationFlags::FLAGS_NONE).unwrap();
let window = Window::new();
let app = gtk::Application::new(Some(config::APP_ID), Default::default()).unwrap();
let (sender, r) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
let receiver = RefCell::new(Some(r));
let window = Window::new(sender.clone());
glib::set_application_name(&format!("{}Icon Library", config::NAME_PREFIX));
glib::set_prgname(Some("icon-library"));
let builder = gtk::Builder::new_from_resource("/org/gnome/design/IconLibrary/shortcuts.ui");
let dialog: gtk::ShortcutsWindow = builder.get_object("shortcuts").unwrap();
window.widget.set_help_overlay(Some(&dialog));
let application = Self { app, window };
let application = Self { app, window, sender, receiver };
application.setup_gactions();
application.setup_signals();
......@@ -38,6 +47,11 @@ impl Application {
self.app.add_action(&simple_action);
self.app.set_accels_for_action("app.quit", &["<primary>q"]);
let builder = gtk::Builder::new_from_resource("/org/gnome/design/IconLibrary/shortcuts.ui");
let dialog: gtk::ShortcutsWindow = builder.get_object("shortcuts").unwrap();
self.window.widget.set_help_overlay(Some(&dialog));
self.app.set_accels_for_action("win.show-help-overlay", &["<primary>question"]);
// About
let window = self.window.widget.clone();
let simple_action = gio::SimpleAction::new("about", None);
......@@ -79,6 +93,18 @@ impl Application {
});
}
fn do_action(&self, action: Action) -> glib::Continue {
match action {
Action::Export(icon) => {
let export_dialog = ExportDialog::new(&icon);
export_dialog.widget.set_transient_for(Some(&self.window.widget));
export_dialog.widget.show_all();
}
}
glib::Continue(true)
}
pub fn setup_css(&self) {
let p = gtk::CssProvider::new();
gtk::CssProvider::load_from_resource(&p, "/org/gnome/design/IconLibrary/style.css");
......@@ -89,11 +115,15 @@ impl Application {
theme.prepend_search_path(dev_kit_path);
}
pub fn run(&self) {
info!("Icon Library {} ({})", config::NAME_PREFIX, config::APP_ID);
pub fn run(&self, app: Rc<Self>) {
info!("{}Icon Library({})", config::NAME_PREFIX, config::APP_ID);
info!("Version: {} ({})", config::VERSION, config::PROFILE);
info!("Datadir: {}", config::PKGDATADIR);
let app = app.clone();
let receiver = self.receiver.borrow_mut().take().unwrap();
receiver.attach(None, move |action| app.do_action(action));
let args: Vec<String> = env::args().collect();
self.app.run(&args);
}
......
extern crate pretty_env_logger;
#[macro_use]
extern crate log;
extern crate gio;
extern crate glib;
extern crate gtk;
extern crate libhandy;
#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;
use gettextrs::*;
use std::rc::Rc;
mod application;
mod config;
mod icon;
mod static_resources;
mod widgets;
mod window;
mod window_state;
use application::Application;
use config::{GETTEXT_PACKAGE, LOCALEDIR};
use config::{GETTEXT_PACKAGE, LOCALEDIR, PKGDATADIR};
fn main() {
pretty_env_logger::init();
......@@ -34,8 +24,9 @@ fn main() {
bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
textdomain(GETTEXT_PACKAGE);
static_resources::init().expect("Failed to initialize the resource file.");
let res = gio::Resource::load(PKGDATADIR.to_owned() + "/resources.gresource").expect("Could not load resources");
gio::resources_register(&res);
let app = Application::new();
app.run();
let app = Rc::new(Application::new());
app.run(app.clone());
}
......@@ -19,22 +19,6 @@ run_command(
check: true
)
# include_bytes! only takes a string literal
resources_conf = configuration_data()
resources_conf.set_quoted('RESOURCEFILE', resources.full_path())
static_resources_output_file = configure_file(
input: 'static_resources.rs.in',
output: 'static_resources.rs',
configuration: resources_conf
)
run_command(
'cp',
static_resources_output_file,
meson.current_source_dir(),
check: true
)
sources = files(
'widgets/icons.rs',
'widgets/export.rs',
......@@ -43,7 +27,6 @@ sources = files(
'config.rs',
'icon.rs',
'main.rs',
'static_resources.rs',
'window.rs',
'window_state.rs',
)
......@@ -56,7 +39,6 @@ cargo_release = custom_target(
console: true,
install: true,
install_dir: bindir,
depends: resources,
command: [
cargo_script,
meson.build_root(),
......
// Source: https://gitlab.gnome.org/World/podcasts/blob/master/podcasts-gtk/src/static_resource.rs
use gio::{resources_register, Error, Resource};
use glib::Bytes;
pub(crate) fn init() -> Result<(), Error> {
// load the gresource binary at build time and include/link it into the final
// binary.
let res_bytes = include_bytes!(@RESOURCEFILE@);
// Create Resource it will live as long the value lives.
let gbytes = Bytes::from_static(res_bytes.as_ref());
let resource = Resource::new_from_data(&gbytes)?;
// Register the resource so it won't be dropped and will continue to live in
// memory.
resources_register(&resource);
Ok(())
}
use crate::icon::Icon;
use gdk;
use gettextrs::gettext;
use gio::prelude::*;
use gtk;
use gtk::prelude::*;
use std::path::Path;
pub struct ExportDialog {
pub widget: libhandy::Dialog,
icon_size_16: gtk::Image,
icon_size_32: gtk::Image,
icon_size_64: gtk::Image,
builder: gtk::Builder,
}
const TARGET_SVG: u32 = 0;
......@@ -22,20 +17,11 @@ impl ExportDialog {
let builder = gtk::Builder::new_from_resource("/org/gnome/design/IconLibrary/export_dialog.ui");
let widget: libhandy::Dialog = builder.get_object("export_dialog").unwrap();
let icon_size_16: gtk::Image = builder.get_object("icon_size_16").unwrap();
let icon_size_32: gtk::Image = builder.get_object("icon_size_32").unwrap();
let icon_size_64: gtk::Image = builder.get_object("icon_size_64").unwrap();
let is_system_infobar: gtk::InfoBar = builder.get_object("is_system_infobar").unwrap();
is_system_infobar.set_visible(icon.is_system);
is_system_infobar.set_no_show_all(!icon.is_system);
let export_dialog = ExportDialog {
widget,
icon_size_16,
icon_size_32,
icon_size_64,
};
let export_dialog = ExportDialog { widget, builder };
export_dialog.init(&icon);
export_dialog.setup_actions(&icon);
export_dialog
......@@ -43,9 +29,14 @@ impl ExportDialog {
fn init(&self, icon: &Icon) {
let icon_str = Some(icon.name.as_str());
self.icon_size_16.set_from_icon_name(icon_str, gtk::IconSize::Button);
self.icon_size_32.set_from_icon_name(icon_str, gtk::IconSize::Dnd);
self.icon_size_64.set_from_icon_name(icon_str, gtk::IconSize::Dialog);
let icon_size_16: gtk::Image = self.builder.get_object("icon_size_16").unwrap();
let icon_size_32: gtk::Image = self.builder.get_object("icon_size_32").unwrap();
let icon_size_64: gtk::Image = self.builder.get_object("icon_size_64").unwrap();
icon_size_16.set_from_icon_name(icon_str, gtk::IconSize::Button);
icon_size_32.set_from_icon_name(icon_str, gtk::IconSize::Dnd);
icon_size_64.set_from_icon_name(icon_str, gtk::IconSize::Dialog);
let window_title = icon.name.replace("-symbolic", "");
self.widget.set_title(window_title.as_str());
......@@ -77,6 +68,12 @@ impl ExportDialog {
Some(gettext("Export").as_str()),
Some(gettext("Cancel").as_str()),
);
let svg_filter = gtk::FileFilter::new();
svg_filter.set_name(Some(&gettext("SVG images")));
svg_filter.add_mime_type("image/svg+xml");
export_dialog.add_filter(&svg_filter);
let file_name = format!("{}.svg", icon_name.clone());
export_dialog.set_current_name(Path::new(&file_name));
let icon_name = icon_name.clone();
......
use crate::application::Action;
use crate::config;
use crate::icon::Icon;
use crate::widgets::export::ExportDialog;
use gettextrs::gettext;
use glib::Cast;
use gtk;
use gtk::prelude::{BoxExt, ContainerExt, FlowBoxExt, GtkWindowExt, IconThemeExt, ImageExt, StackExt, StyleContextExt, WidgetExt};
use libhandy::{Column, ColumnExt};
use gtk::prelude::*;
use std::cell::RefCell;
use std::collections::HashMap;
use std::fs::File;
......@@ -14,66 +11,28 @@ use std::path::PathBuf;
pub struct IconsView {
pub widget: gtk::Stack,
icons_container: gtk::Box,
builder: gtk::Builder,
icons_contexts: Vec<RefCell<IconsViewContext>>,
parent: gtk::ApplicationWindow,
sender: glib::Sender<Action>,
}
impl IconsView {
pub fn new(parent: &gtk::ApplicationWindow) -> IconsView {
let widget = gtk::Stack::new();
let icons_container = gtk::Box::new(gtk::Orientation::Vertical, 6);
icons_container.set_valign(gtk::Align::Start);
pub fn new(sender: glib::Sender<Action>) -> IconsView {
let builder = gtk::Builder::new_from_resource("/org/gnome/design/IconLibrary/icons_view.ui");
let widget: gtk::Stack = builder.get_object("icons_view").unwrap();
let icons_contexts: Vec<RefCell<IconsViewContext>> = Vec::new();
let parent = parent.clone();
let mut icons_view = IconsView {
widget,
icons_container,
builder,
icons_contexts,
parent,
sender,
};
icons_view.init();
icons_view.fill();
icons_view
}
fn init(&self) {
// No results view
let no_results_view = gtk::Box::new(gtk::Orientation::Vertical, 6);
no_results_view.get_style_context().add_class("dim-label");
no_results_view.set_valign(gtk::Align::Center);
no_results_view.set_halign(gtk::Align::Center);
let no_results_image = gtk::Image::new_from_icon_name(Some("system-search-symbolic"), gtk::IconSize::Dialog);
no_results_image.set_pixel_size(96);
let no_results_label = gtk::Label::new(Some(gettext("No results found").as_str()));
no_results_label.get_style_context().add_class("no-results-label");
no_results_view.pack_start(&no_results_image, false, false, 0);
no_results_view.pack_start(&no_results_label, false, false, 0);
self.widget.add_named(&no_results_view, "no-results");
let column = Column::new();
column.set_maximum_width(900);
// we can't call add as libhandy::Column is not seen as a gtkContainer
// Thanks to Jordan (Podcasts) for the tip :P
let column = column.upcast::<gtk::Widget>();
let column = column.downcast::<gtk::Container>().unwrap();