Commit 36ca234b authored by Bilal Elmoussaoui's avatar Bilal Elmoussaoui
Browse files

subclass window

parent 24d4921e
......@@ -79,7 +79,6 @@ ui_config.set('version', meson.project_version() + version_suffix)
ui_config.set('name-prefix', name_prefix)
ui_preconfigured_files = files(
'resources/ui/about_dialog.ui.in',
'resources/ui/window.ui.in',
)
ui_dependencies = []
foreach ui_file: ui_preconfigured_files
......
......@@ -3,7 +3,6 @@
<gresource prefix="/org/gnome/design/Contrast/">
<file compressed="true" preprocess="xml-stripblanks">about_dialog.ui</file>
<file compressed="true" preprocess="xml-stripblanks" alias="shortcuts.ui">resources/ui/shortcuts.ui</file>
<file compressed="true" preprocess="xml-stripblanks">window.ui</file>
<file compressed="true" alias="style.css">resources/style.css</file>
</gresource>
<gresource prefix="/org/gnome/design/Contrast/icons">
......
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkPopoverMenu" id="popover">
<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="GtkModelButton">
<property name="visible">True</property>
<property name="can_focus">False</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">False</property>
<property name="receives_default">False</property>
<property name="action_name">app.about</property>
<property name="text" translatable="yes">_About Contrast</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>
<property name="position">1</property>
</packing>
</child>
</object>
<object class="GtkApplicationWindow" id="window_widget">
<property name="can_focus">False</property>
<property name="title" translatable="yes">@name-prefix@Contrast</property>
<property name="default_width">650</property>
<property name="default_height">500</property>
<property name="icon_name">@app-id@</property>
<child type="titlebar">
<object class="HdyHeaderBar" id="headerbar">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="title" translatable="yes">@name-prefix@Contrast</property>
<property name="show_close_button">True</property>
<property name="centering_policy">HDY_CENTERING_POLICY_LOOSE</property>
<child>
<object class="GtkMenuButton" id="appmenu_button">
<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>
<property name="can_focus">False</property>
<property name="icon_name">open-menu-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="pack_type">end</property>
</packing>
</child>
</object>
</child>
<child>
<placeholder/>
</child>
</object>
</interface>
......@@ -14,7 +14,7 @@ pub enum Action {
pub struct Application {
app: gtk::Application,
window: Rc<Window>,
window: Window,
provider: gtk::CssProvider,
receiver: RefCell<Option<Receiver<Action>>>,
}
......@@ -24,7 +24,7 @@ impl Application {
let app = gtk::Application::new(Some(config::APP_ID), gio::ApplicationFlags::FLAGS_NONE).unwrap();
let (sender, r) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
let receiver = RefCell::new(Some(r));
let window = Window::new(sender);
let window = Window::new(sender, &app);
let provider = gtk::CssProvider::new();
let application = Rc::new(Self { app, window, provider, receiver });
......@@ -48,7 +48,7 @@ impl Application {
action!(
self.app,
"about",
clone!(@weak self.window.widget as window => move |_, _| {
clone!(@weak self.window as window => move |_, _| {
let builder = gtk::Builder::new_from_resource("/org/gnome/design/Contrast/about_dialog.ui");
get_widget!(builder, gtk::AboutDialog, about_dialog);
about_dialog.set_transient_for(Some(&window));
......@@ -65,7 +65,7 @@ impl Application {
}
fn setup_signals(&self) {
self.app.connect_activate(clone!(@weak self.window.widget as window => move |app| {
self.app.connect_activate(clone!(@weak self.window as window => move |app| {
window.set_application(Some(app));
app.add_window(&window);
window.present();
......
......@@ -8,70 +8,161 @@ use super::window_state::WindowState;
use gettextrs::gettext;
use gio::prelude::*;
use glib::subclass;
use glib::subclass::prelude::*;
use glib::translate::*;
use glib::ObjectExt;
use gtk::prelude::*;
use gtk::subclass::prelude::{ApplicationWindowImpl, BinImpl, ContainerImpl, WidgetImpl, WindowImpl};
use libhandy::HeaderBarExt;
use std::{cell::RefCell, rc::Rc};
pub struct Window {
pub widget: gtk::ApplicationWindow,
builder: gtk::Builder,
pub struct WindowPrivate {
fg_entry: ColourEntry,
bg_entry: ColourEntry,
reverse_btn: gtk::Button,
pub state: Rc<RefCell<WindowState>>,
pub levelbar: RefCell<ContrastLevelBar>,
state: Rc<RefCell<WindowState>>,
levelbar: RefCell<ContrastLevelBar>,
pub preview: ContrastPreview,
sender: glib::Sender<Action>,
sender: RefCell<Option<glib::Sender<Action>>>,
}
impl Window {
pub fn new(sender: glib::Sender<Action>) -> Rc<Self> {
let settings = gio::Settings::new(APP_ID);
let builder = gtk::Builder::new_from_resource("/org/gnome/design/Contrast/window.ui");
get_widget!(builder, gtk::ApplicationWindow, window_widget);
impl ObjectSubclass for WindowPrivate {
const NAME: &'static str = "Window";
type ParentType = gtk::ApplicationWindow;
type Instance = subclass::simple::InstanceStruct<Self>;
type Class = subclass::simple::ClassStruct<Self>;
glib_object_subclass!();
fn new() -> Self {
let settings = gio::Settings::new(APP_ID);
let bg_entry = ColourEntry::new(&gettext("Background Colour"));
let fg_entry = ColourEntry::new(&gettext("Foreground Colour"));
let reverse_btn = gtk::Button::new();
let state = Rc::new(RefCell::new(WindowState::init(settings)));
let levelbar = RefCell::new(ContrastLevelBar::new());
let preview = ContrastPreview::new();
let window = Rc::new(Window {
widget: window_widget,
builder,
fg_entry,
Self {
bg_entry,
reverse_btn,
state,
fg_entry,
levelbar,
preview,
sender,
});
state,
sender: RefCell::new(None),
}
}
}
impl ObjectImpl for WindowPrivate {
glib_object_impl!();
fn constructed(&self, obj: &glib::Object) {
self.parent_constructed(obj);
let self_ = obj.downcast_ref::<Window>().unwrap();
if PROFILE == "Devel" {
self_.get_style_context().add_class("devel");
}
let builder = gtk::Builder::new_from_resource("/org/gnome/design/Contrast/shortcuts.ui");
get_widget!(builder, gtk::ShortcutsWindow, shortcuts_dialog);
self_.set_help_overlay(Some(&shortcuts_dialog));
let container = gtk::Box::new(gtk::Orientation::Vertical, 0);
container.get_style_context().add_class("main-container");
container.set_valign(gtk::Align::Center);
self.fg_entry.get_entry().get_style_context().add_class("fg-entry");
self.bg_entry.get_entry().get_style_context().add_class("bg-entry");
let reverse_btn = gtk::Button::new();
reverse_btn.set_halign(gtk::Align::Center);
reverse_btn.set_action_name(Some("window.reverse-colors"));
let reverse_img = gtk::Image::new_from_icon_name(Some("network-transmit-receive-symbolic"), gtk::IconSize::Button);
reverse_btn.set_image(Some(&reverse_img));
let entries_container = gtk::Box::new(gtk::Orientation::Horizontal, 0);
entries_container.pack_start(&self.bg_entry, false, false, 6);
entries_container.pack_start(&reverse_btn, false, false, 6);
entries_container.pack_start(&self.fg_entry, false, false, 6);
entries_container.set_halign(gtk::Align::Center);
let headerbar = libhandy::HeaderBar::new();
headerbar.set_show_close_button(true);
headerbar.set_title(Some(&gettext("Contrast")));
headerbar.set_custom_title(Some(&entries_container));
self_.set_titlebar(Some(&headerbar));
let primary_menu = gio::Menu::new();
primary_menu.append(Some(&gettext("_Keyboard Shortcuts")), Some("win.show-help-overlay"));
primary_menu.append(Some(&gettext("_About Contrast")), Some("app.about"));
let primary_btn = gtk::MenuButton::new();
let primary_popover = gtk::Popover::new_from_model(Some(&primary_btn), &primary_menu);
let primary_img = gtk::Image::new_from_icon_name(Some("open-menu-symbolic"), gtk::IconSize::Button);
primary_btn.add(&primary_img);
primary_btn.set_popover(Some(&primary_popover));
headerbar.pack_end(&primary_btn);
container.pack_start(&self.preview, true, true, 12);
container.pack_start(&self.levelbar.borrow().widget, true, true, 12);
self_.add(&container);
// Restore state
self.state.borrow_mut().load_into(self_, &self.bg_entry, &self.fg_entry);
self_.show_all();
}
}
impl WidgetImpl for WindowPrivate {}
impl ContainerImpl for WindowPrivate {}
impl BinImpl for WindowPrivate {}
impl WindowImpl for WindowPrivate {}
impl ApplicationWindowImpl for WindowPrivate {}
window.setup_widgets();
window.setup_signals(window.clone());
glib_wrapper! {
pub struct Window(
Object<subclass::simple::InstanceStruct<WindowPrivate>,
subclass::simple::ClassStruct<WindowPrivate>,
SwApplicationWindowClass>)
@extends gtk::Widget, gtk::Container, gtk::Bin, gtk::Window, gtk::ApplicationWindow;
match fn {
get_type => || WindowPrivate::get_type().to_glib(),
}
}
impl Window {
pub fn new(sender: glib::Sender<Action>, app: &gtk::Application) -> Self {
let window = glib::Object::new(Window::static_type(), &[("application", app)]).unwrap().downcast::<Window>().unwrap();
window.setup_signals(sender);
window
}
fn colour_changed(&self, bg_colour: String, fg_colour: String) {
let bg_rgba = self.state.borrow_mut().set_bg_colour(Some(&bg_colour));
let fg_rgba = self.state.borrow_mut().set_fg_colour(Some(&fg_colour));
let self_ = WindowPrivate::from_instance(self);
let bg_rgba = self_.state.borrow_mut().set_bg_colour(Some(&bg_colour));
let fg_rgba = self_.state.borrow_mut().set_fg_colour(Some(&fg_colour));
let contrast_level = colour::calc_contrast_level(&bg_rgba, &fg_rgba);
self.levelbar.borrow_mut().set_contrast_level(contrast_level);
self.preview.set_contrast_level(contrast_level);
colour::compile_styles(&self.sender, fg_rgba, bg_rgba);
self_.levelbar.borrow_mut().set_contrast_level(contrast_level);
self_.preview.set_contrast_level(contrast_level);
if let Some(sender) = self_.sender.borrow().clone() {
colour::compile_styles(&sender, fg_rgba, bg_rgba);
}
}
fn setup_signals(&self, window: Rc<Self>) {
fn setup_signals(&self, sender: glib::Sender<Action>) {
let self_ = WindowPrivate::from_instance(self);
self_.sender.borrow_mut().replace(sender);
// save window state on delete event
self.widget.connect_delete_event(clone!(@strong self.state as state => move |window, _| {
self.connect_delete_event(clone!(@strong self_.state as state, @strong self as window => move |_, _| {
if let Err(err) = state.borrow_mut().dump(&window) {
warn!("Failed to save window state {}", err);
}
......@@ -79,22 +170,23 @@ impl Window {
}));
let on_entry_changed = clone!(
@strong self.fg_entry as fg_entry,@strong self.bg_entry as bg_entry,
@weak window => move |_entry: &gtk::Entry| {
@strong self_.fg_entry as fg_entry, @strong self_.bg_entry as bg_entry,
@strong self as window => move |_entry: &gtk::Entry| {
let fg_colour = fg_entry.get_text();
let bg_colour = bg_entry.get_text();
window.colour_changed(bg_colour, fg_colour);
});
let bg_handle = self.bg_entry.get_entry().connect_changed(on_entry_changed.clone());
let fg_handle = self.fg_entry.get_entry().connect_changed(on_entry_changed);
let bg_handle = self_.bg_entry.get_entry().connect_changed(on_entry_changed.clone());
let fg_handle = self_.fg_entry.get_entry().connect_changed(on_entry_changed);
let actions = gio::SimpleActionGroup::new();
action!(
actions,
"reverse-colors",
clone!(@strong self.fg_entry as fg_entry, @strong self.bg_entry as bg_entry => move |_, _| {
clone!(@strong self_.fg_entry as fg_entry, @strong self_.bg_entry as bg_entry,
@strong self as window => move |_, _| {
fg_entry.get_entry().block_signal(&fg_handle);
bg_entry.get_entry().block_signal(&bg_handle);
let fg_colour = fg_entry.get_text();
......@@ -108,45 +200,8 @@ impl Window {
window.colour_changed(fg_colour, bg_colour);
})
);
self.widget.insert_action_group("window", Some(&actions));
}
fn setup_widgets(&self) {
if PROFILE == "Devel" {
self.widget.get_style_context().add_class("devel");
}
let builder = gtk::Builder::new_from_resource("/org/gnome/design/Contrast/shortcuts.ui");
get_widget!(builder, gtk::ShortcutsWindow, shortcuts_dialog);
self.widget.set_help_overlay(Some(&shortcuts_dialog));
let container = gtk::Box::new(gtk::Orientation::Vertical, 0);
container.get_style_context().add_class("main-container");
container.set_valign(gtk::Align::Center);
self.fg_entry.get_entry().get_style_context().add_class("fg-entry");
self.bg_entry.get_entry().get_style_context().add_class("bg-entry");
self.reverse_btn.set_halign(gtk::Align::Center);
self.reverse_btn.set_action_name(Some("window.reverse-colors"));
let reverse_img = gtk::Image::new_from_icon_name(Some("network-transmit-receive-symbolic"), gtk::IconSize::Button);
self.reverse_btn.set_image(Some(&reverse_img));
self.insert_action_group("window", Some(&actions));
let entries_container = gtk::Box::new(gtk::Orientation::Horizontal, 0);
entries_container.pack_start(&self.bg_entry, false, false, 6);
entries_container.pack_start(&self.reverse_btn, false, false, 6);
entries_container.pack_start(&self.fg_entry, false, false, 6);
entries_container.set_halign(gtk::Align::Center);
get_widget!(self.builder, libhandy::HeaderBar, headerbar);
headerbar.set_custom_title(Some(&entries_container));
container.pack_start(&self.preview, true, true, 12);
container.pack_start(&self.levelbar.borrow().widget, true, true, 12);
self.widget.add(&container);
// Restore state
self.state.borrow_mut().load_into(&self.widget, &self.bg_entry, &self.fg_entry);
self.colour_changed(self.bg_entry.get_text(), self.fg_entry.get_text());
self.widget.show_all();
self.colour_changed(self_.bg_entry.get_text(), self_.fg_entry.get_text());
}
}
use super::colour;
use super::colour_entry::ColourEntry;
use super::window::Window;
use gio::prelude::SettingsExt;
use gtk::prelude::GtkWindowExt;
use rand::Rng;
......@@ -67,7 +68,7 @@ impl WindowState {
}
}
pub fn load_into(&self, window: &gtk::ApplicationWindow, bg_entry: &ColourEntry, fg_entry: &ColourEntry) {
pub fn load_into(&self, window: &Window, bg_entry: &ColourEntry, fg_entry: &ColourEntry) {
window.resize(550, 250);
if self.x > -1 && self.y > -1 {
......@@ -107,7 +108,7 @@ impl WindowState {
self.fg_colour
}
pub fn dump(&self, window: &gtk::ApplicationWindow) -> Result<(), failure::Error> {
pub fn dump(&self, window: &Window) -> Result<(), failure::Error> {
let position = window.get_position();
self.settings.set_boolean("is-maximized", window.is_maximized())?;
self.settings.set_int("window-x", position.0)?;
......
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