window.rs 8.74 KB
Newer Older
1
use super::application::{Action, Application};
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
2
use super::colour;
3
use super::colour_entry::ColourEntry;
4
use super::config::{APP_ID, PROFILE};
5
6
7
8
use super::contrast_level::ContrastLevelBar;
use super::contrast_preview::ContrastPreview;
use super::window_state::WindowState;

9
use gettextrs::gettext;
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
10
use gio::prelude::*;
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
11
12
13
use glib::subclass;
use glib::subclass::prelude::*;
use glib::translate::*;
14
use glib::ObjectExt;
15
use gtk::prelude::*;
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
16
use gtk::subclass::prelude::{ApplicationWindowImpl, BinImpl, ContainerImpl, WidgetImpl, WindowImpl};
17
use libhandy::HeaderBarExt;
18
use std::{cell::RefCell, rc::Rc};
Bilal Elmoussaoui's avatar
Init  
Bilal Elmoussaoui committed
19

Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
20
pub struct WindowPrivate {
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
21
22
    fg_entry: ColourEntry,
    bg_entry: ColourEntry,
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
23
24
    state: Rc<RefCell<WindowState>>,
    levelbar: RefCell<ContrastLevelBar>,
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
25
    pub preview: ContrastPreview,
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
26
    sender: RefCell<Option<glib::Sender<Action>>>,
Bilal Elmoussaoui's avatar
Init  
Bilal Elmoussaoui committed
27
28
}

Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
29
30
31
32
33
34
35
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!();
Bilal Elmoussaoui's avatar
Init  
Bilal Elmoussaoui committed
36

Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
37
    fn new() -> Self {
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
38
39
        let bg_entry = ColourEntry::new(&gettext("Background Colour"));
        let fg_entry = ColourEntry::new(&gettext("Foreground Colour"));
40

41
        let state = Rc::new(RefCell::new(WindowState::init()));
42

Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
43
        let levelbar = RefCell::new(ContrastLevelBar::new());
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
44
        let preview = ContrastPreview::new();
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
45
        Self {
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
46
            bg_entry,
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
47
            fg_entry,
48
49
            levelbar,
            preview,
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
            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");
        }
67
68
        self_.set_icon_name(Some(APP_ID));
        // Keyboard Shortcuts
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
69
70
71
72
        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));

73
74
75
76
77
        // Header Bar
        let headerbar = libhandy::HeaderBar::new();
        headerbar.set_show_close_button(true);
        headerbar.set_title(Some(&gettext("Contrast")));
        self_.set_titlebar(Some(&headerbar));
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106

        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);
        headerbar.set_custom_title(Some(&entries_container));

        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);

107
108
109
110
111
        // Main Container: Composed of ContrastPreview & ContrastLevelBar
        let container = gtk::Box::new(gtk::Orientation::Vertical, 0);
        container.get_style_context().add_class("main-container");
        container.set_valign(gtk::Align::Center);

Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
        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 {}
Bilal Elmoussaoui's avatar
Init  
Bilal Elmoussaoui committed
131

Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
132
133
134
135
136
137
138
139
140
141
142
143
144
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 {
145
    pub fn new(sender: glib::Sender<Action>, app: &Application) -> Self {
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
146
147
        let window = glib::Object::new(Window::static_type(), &[("application", app)]).unwrap().downcast::<Window>().unwrap();
        window.setup_signals(sender);
148
        window.setup_actions(app);
Bilal Elmoussaoui's avatar
Init  
Bilal Elmoussaoui committed
149
150
151
        window
    }

Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
152
    fn colour_changed(&self, bg_colour: String, fg_colour: String) {
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
153
154
155
        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));
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
156
157
        let contrast_level = colour::calc_contrast_level(&bg_rgba, &fg_rgba);

Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
158
159
160
161
162
        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);
        }
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
163
164
    }

Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
165
166
167
    fn setup_signals(&self, sender: glib::Sender<Action>) {
        let self_ = WindowPrivate::from_instance(self);
        self_.sender.borrow_mut().replace(sender);
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
168
        // save window state on delete event
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
169
        self.connect_delete_event(clone!(@strong self_.state as state, @strong self as window => move |_, _| {
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
170
171
172
            if let Err(err) = state.borrow_mut().dump(&window) {
                warn!("Failed to save window state {}", err);
            }
173
            gtk::Inhibit(false)
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
174
        }));
Bilal Elmoussaoui's avatar
Init  
Bilal Elmoussaoui committed
175

Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
176
        let on_entry_changed = clone!(
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
177
178
                    @strong self_.fg_entry as fg_entry, @strong self_.bg_entry as bg_entry,
                    @strong self as window => move |_entry: &gtk::Entry| {
179
180
181
            let fg_colour = fg_entry.get_text();
            let bg_colour = bg_entry.get_text();

Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
182
183
            window.colour_changed(bg_colour, fg_colour);
        });
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
184

Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
185
186
        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);
187
        let actions = gio::SimpleActionGroup::new();
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
188
189
        action!(
            actions,
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
190
            "reverse-colors",
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
191
192
            clone!(@strong self_.fg_entry as fg_entry, @strong self_.bg_entry as bg_entry,
                    @strong self as window  => move |_, _| {
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
193
194
                fg_entry.get_entry().block_signal(&fg_handle);
                bg_entry.get_entry().block_signal(&bg_handle);
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
195
196
197
198
199
                let fg_colour = fg_entry.get_text();
                let bg_colour = bg_entry.get_text();
                fg_entry.set_text(Some(bg_colour.clone()));
                bg_entry.set_text(Some(fg_colour.clone()));

Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
200
201
                fg_entry.get_entry().unblock_signal(&fg_handle);
                bg_entry.get_entry().unblock_signal(&bg_handle);
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
202
203

                window.colour_changed(fg_colour, bg_colour);
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
204
            })
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
205
        );
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
206
207
        self.insert_action_group("window", Some(&actions));
        self.colour_changed(self_.bg_entry.get_text(), self_.fg_entry.get_text());
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
208
    }
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233

    fn setup_actions(&self, app: &Application) {
        // Quit
        action!(
            self.get_application().unwrap(),
            "quit",
            clone!(@strong app => move |_, _| {
                app.quit();
            })
        );

        // About
        action!(
            self.get_application().unwrap(),
            "about",
            clone!(@strong self 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));

                about_dialog.connect_response(|dialog, _| dialog.destroy());
                about_dialog.show();
            })
        );
    }
Bilal Elmoussaoui's avatar
Init  
Bilal Elmoussaoui committed
234
}