window.rs 7.89 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
        let builder = gtk::Builder::from_resource("/org/gnome/design/Contrast/shortcuts.ui");
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
70
71
72
        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

        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();
83
        reverse_btn.set_tooltip_text(Some(&gettext("Reverse Colours")));
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
84
        reverse_btn.set_halign(gtk::Align::Center);
85
        reverse_btn.set_action_name(Some("win.reverse-colors"));
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
86
        let reverse_img = gtk::Image::from_icon_name(Some("network-transmit-receive-symbolic"), gtk::IconSize::Button);
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
87
88
89
90
91
92
93
94
95
96
97
98
99
100
        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();
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
101
        let primary_popover = gtk::Popover::from_model(Some(&primary_btn), &primary_menu);
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
102

Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
103
        let primary_img = gtk::Image::from_icon_name(Some("open-menu-symbolic"), gtk::IconSize::Button);
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
104
105
106
107
        primary_btn.add(&primary_img);
        primary_btn.set_popover(Some(&primary_popover));
        headerbar.pack_end(&primary_btn);

108
109
110
111
112
        // 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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
        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
132

Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
133
134
135
136
137
glib_wrapper! {
    pub struct Window(
        Object<subclass::simple::InstanceStruct<WindowPrivate>,
        subclass::simple::ClassStruct<WindowPrivate>,
        SwApplicationWindowClass>)
138
        @extends gtk::Widget, gtk::Container, gtk::Bin, gtk::Window, gtk::ApplicationWindow, gio::ActionMap;
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
139
140
141
142
143
144
145

    match fn {
        get_type => || WindowPrivate::get_type().to_glib(),
    }
}

impl Window {
146
    pub fn new(sender: glib::Sender<Action>, app: &Application) -> Self {
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
147
148
        let window = glib::Object::new(Window::static_type(), &[("application", app)]).unwrap().downcast::<Window>().unwrap();
        window.setup_signals(sender);
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

Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
188
        action!(
189
            self,
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
        self.colour_changed(self_.bg_entry.get_text(), self_.fg_entry.get_text());
Bilal Elmoussaoui's avatar
Bilal Elmoussaoui committed
207
    }
Bilal Elmoussaoui's avatar
Init  
Bilal Elmoussaoui committed
208
}