Commit b4a246c4 authored by Bilal Elmoussaoui's avatar Bilal Elmoussaoui
Browse files

Make the contrast preview work

parent f673c412
Pipeline #99159 passed with stages
in 6 minutes and 34 seconds
......@@ -67,10 +67,11 @@ impl Application {
gtk::CssProvider::load_from_resource(&p, "/org/gnome/design/ColourContrast/style.css");
gtk::StyleContext::add_provider_for_screen(&gdk::Screen::get_default().unwrap(), &p, 500);
let fg_color = self.window.settings.get_string("foreground-colour").unwrap();
let bg_color = self.window.settings.get_string("background-colour").unwrap();
let stylesheet = format!("@define-color foreground_color {}; @define-color background_color {};", fg_color.as_str(), bg_color.as_str());
println!("style: {}", stylesheet);
let bg_colour = self.window.state.borrow_mut().bg_colour.to_hex_string();
let fg_colour = self.window.state.borrow_mut().fg_colour.to_hex_string();
let stylesheet = format!("@define-color background_color {}; @define-color foreground_color {};", bg_colour, fg_colour);
println!("stylesheet {}", stylesheet);
load_stylesheet(stylesheet);
}
......
use colorsys::{ParseError, Rgb};
use colorsys::Rgb;
use std::str::FromStr;
pub fn is_valid(colour: String) -> bool {
// wether it's a valid HEX color
true
#[derive(Debug)]
pub enum ColorError {
FailedToParse,
Invalid,
}
pub fn calc_contrast_level(bg_color: &Rgb, fg_color: &Rgb) -> f64 {
// TODO: calc the contrast level
let bg_luminance = get_luminance(bg_color) + 0.05;
let fg_luminance = get_luminance(fg_color) + 0.05;
bg_luminance.max(fg_luminance) / bg_luminance.min(fg_luminance)
}
pub fn parse(colour: &str) -> Result<Rgb, ColorError> {
if colour.starts_with("#") {
match Rgb::from_hex_str(colour) {
Ok(rgb) => Ok(rgb),
Err(_) => Err(ColorError::FailedToParse),
}
} else if colour.starts_with("rgb(") || colour.starts_with("rgba(") {
match Rgb::from_str(colour) {
Ok(rgb) => Ok(rgb),
Err(_) => Err(ColorError::FailedToParse),
}
} else {
Err(ColorError::Invalid)
}
}
fn get_luminance(color: &Rgb) -> f64 {
let red = color.get_red() / 255.0;
let blue = color.get_blue() / 255.0;
let green = color.get_green() / 255.0;
red * 0.2126 + blue * 0.0722 + green * 0.7152
}
use crate::colour;
use crate::gtk::{BoxExt, ButtonExt, ColorChooserExt, DialogExt, EditableSignals, EntryExt, GtkWindowExt, StyleContextExt, WidgetExt};
use crate::gtk::{BoxExt, ButtonExt, ColorChooserExt, DialogExt, EntryExt, GtkWindowExt, StyleContextExt, WidgetExt};
use std::borrow::Borrow;
#[derive(Clone)]
pub struct ColourEntry {
......@@ -9,8 +8,6 @@ pub struct ColourEntry {
impl ColourEntry {
pub fn new(label: String, parent: &gtk::ApplicationWindow) -> ColourEntry {
// Let's get started here
let entry = gtk::Entry::new();
let widget = gtk::Box::new(gtk::Orientation::Horizontal, 0);
......
......@@ -6,12 +6,6 @@ pub struct ContrastLevelBar {
level_label: gtk::Label,
}
pub enum ContrastLevel {
GOOD, // AAA
MEDIUM, // AA
BAD, // A
}
impl ContrastLevelBar {
pub fn new() -> ContrastLevelBar {
let widget = gtk::Box::new(gtk::Orientation::Vertical, 0);
......@@ -40,9 +34,6 @@ impl ContrastLevelBar {
self.levelbar.set_halign(gtk::Align::Center);
self.levelbar.show();
self.widget.pack_start(&self.levelbar, false, false, 0);
// fix me:
self.levelbar.set_value(8.9);
self.level_label.set_text("8.9 out of 21");
self.level_label.get_style_context().add_class("level-label");
self.level_label.show();
......@@ -51,7 +42,9 @@ impl ContrastLevelBar {
self.widget.show();
}
pub fn set_level(level: ContrastLevel) {
// TODO: implement level update here
pub fn set_contrast_level(&mut self, contrast_level: f64) {
self.levelbar.set_value(contrast_level);
let level_label = format!("{:.1} out of 21", contrast_level);
self.level_label.set_text(&level_label);
}
}
use crate::gtk::{BoxExt, ContainerExt, CssProviderExt, LabelExt, StyleContextExt, WidgetExt};
use crate::utils::load_stylesheet;
#[derive(Clone)]
use crate::gtk::{BoxExt, LabelExt, StyleContextExt, WidgetExt};
pub struct ContrastPreview {
pub widget: gtk::Box,
head: gtk::Label,
}
impl ContrastPreview {
pub fn new() -> ContrastPreview {
let widget = gtk::Box::new(gtk::Orientation::Vertical, 0);
let preview = ContrastPreview { widget };
let head = gtk::Label::new(Some("Contrast"));
let preview = ContrastPreview { widget, head };
preview.setup_widget();
preview
......@@ -20,35 +21,26 @@ impl ContrastPreview {
self.widget.set_valign(gtk::Align::Fill);
self.widget.get_style_context().add_class("preview");
let head = gtk::Label::new(Some("Contrast"));
head.get_style_context().add_class("preview-head");
head.set_halign(gtk::Align::Center);
head.set_valign(gtk::Align::Center);
head.set_vexpand(true);
head.show();
self.head.get_style_context().add_class("preview-head");
self.head.set_halign(gtk::Align::Center);
self.head.set_valign(gtk::Align::Center);
self.head.set_vexpand(true);
self.head.show();
self.widget.pack_start(&head, false, false, 0);
self.widget.pack_start(&self.head, false, false, 0);
self.widget.show();
}
pub fn update_bg_colour(&self, colour: Option<&str>) {
match (colour) {
Some(colour) => {
let stylesheet = format!("@define-color background_color {};", colour);
load_stylesheet(stylesheet);
}
_ => (),
}
}
pub fn update_fg_colour(&self, colour: Option<&str>) {
match (colour) {
Some(colour) => {
let stylesheet = format!("@define-color foreground_color {};", colour);
load_stylesheet(stylesheet);
}
_ => (),
pub fn set_contrast_level(&self, contrast_level: f64) {
if 0.0 <= contrast_level && contrast_level <= 3.0 {
self.head.set_text("FAIL");
} else if 3.0 < contrast_level && contrast_level <= 4.5 {
self.head.set_text("AA-LARGE");
} else if 4.5 < contrast_level && contrast_level <= 7.0 {
self.head.set_text("AA");
} else if 7.0 < contrast_level && contrast_level <= 22.0 {
self.head.set_text("AAA");
}
}
}
use gtk::{CssProviderExt, StyleContextExt};
use gtk::CssProviderExt;
pub fn load_stylesheet(stylesheet: String) {
let provider = gtk::CssProvider::new();
match (provider.load_from_data(stylesheet.as_bytes())) {
_ => {
match provider.load_from_data(stylesheet.as_bytes()) {
Ok(_) => {
gtk::StyleContext::add_provider_for_screen(&gdk::Screen::get_default().unwrap(), &provider, 500);
println!("added");
}
Err(err) => error!("Couldn't parse the stylesheet {}", stylesheet),
Err(_) => error!("Couldn't parse the stylesheet {}", stylesheet),
};
}
use crate::colour_entry::ColourEntry;
use crate::config::{APP_ID, PROFILE};
use crate::contrast_level::{ContrastLevel, ContrastLevelBar};
use crate::contrast_level::ContrastLevelBar;
use crate::contrast_preview::ContrastPreview;
use crate::gio::SettingsExt;
use crate::libhandy::{HeaderBar, HeaderBarExt};
use crate::window_state;
use gtk::prelude::*;
use crate::libhandy::HeaderBarExt;
use crate::window_state::WindowState;
use gtk::prelude::{BoxExt, BuilderExtManual, ButtonExt, ContainerExt, EditableSignals, EntryExt, MenuButtonExt, StyleContextExt, WidgetExt};
use gtk::Inhibit;
use std::cell::RefCell;
use std::rc::Rc;
pub struct Window {
pub widget: gtk::ApplicationWindow,
......@@ -13,7 +15,9 @@ pub struct Window {
fg_entry: ColourEntry,
bg_entry: ColourEntry,
headerbar: libhandy::HeaderBar,
pub settings: gio::Settings,
pub state: Rc<RefCell<WindowState>>,
levelbar: Rc<RefCell<ContrastLevelBar>>,
preview: Rc<RefCell<ContrastPreview>>,
}
impl Window {
......@@ -25,7 +29,15 @@ impl Window {
let appmenu_button: gtk::MenuButton = builder.get_object("appmenu_button").unwrap();
let bg_entry = ColourEntry::new("Background Colour".to_string(), &widget);
bg_entry.entry.set_name("bg");
let fg_entry = ColourEntry::new("Foreground Colour".to_string(), &widget);
fg_entry.entry.set_name("fg");
let state = Rc::new(RefCell::new(WindowState::init(settings)));
let levelbar = Rc::new(RefCell::new(ContrastLevelBar::new()));
levelbar.borrow_mut().set_contrast_level(state.borrow_mut().contrast_level);
let preview = Rc::new(RefCell::new(ContrastPreview::new()));
preview.borrow_mut().set_contrast_level(state.borrow_mut().contrast_level);
if PROFILE == "Devel" {
widget.get_style_context().add_class("devel");
......@@ -37,11 +49,14 @@ impl Window {
fg_entry,
bg_entry,
headerbar,
settings,
state,
levelbar,
preview,
};
window.init();
window.setup_widgets();
window.setup_signals();
window
}
......@@ -51,26 +66,39 @@ impl Window {
let menu_builder = gtk::Builder::new_from_resource("/org/gnome/design/ColourContrast/menu.ui");
let popover_menu: gio::MenuModel = menu_builder.get_object("popover_menu").unwrap();
self.appmenu_button.set_menu_model(Some(&popover_menu));
// load latest window state
window_state::load(&self.widget, &self.settings);
// save window state on delete event
let settings = self.settings.clone();
let state = self.state.clone();
self.widget.connect_delete_event(move |window, _| {
window_state::save(&window, &settings);
state.borrow_mut().dump(&window);
Inhibit(false)
});
// Init the widgets
self.widget.show();
}
fn setup_signals(&self) {
let state = self.state.clone();
let level_bar = self.levelbar.clone();
let preview = self.preview.clone();
let on_entry_changed = move |entry: &gtk::Entry| {
let colour = entry.get_text().unwrap();
let colour = colour.as_str();
if entry.get_name().unwrap() == "bg" {
state.borrow_mut().set_bg_colour(Some(colour));
} else {
state.borrow_mut().set_fg_colour(Some(colour));
}
let contarst_level = state.borrow_mut().contrast_level;
level_bar.borrow_mut().set_contrast_level(contarst_level);
preview.borrow_mut().set_contrast_level(contarst_level);
};
self.bg_entry.entry.connect_changed(on_entry_changed.clone());
self.fg_entry.entry.connect_changed(on_entry_changed.clone());
}
fn setup_widgets(&self) {
let fg_colour = self.settings.get_string("foreground-colour").unwrap();
let bg_colour = self.settings.get_string("background-colour").unwrap();
self.fg_entry.set_text(Some(fg_colour.as_str().to_string()));
self.bg_entry.set_text(Some(bg_colour.as_str().to_string()));
let container = gtk::Box::new(gtk::Orientation::Vertical, 0);
container.show();
......@@ -82,7 +110,7 @@ impl Window {
reverse_btn.show();
let fg_entry = self.fg_entry.clone();
let bg_entry = self.bg_entry.clone();
reverse_btn.connect_clicked(move |button| {
reverse_btn.connect_clicked(move |_| {
let fg_colour = fg_entry.get_text();
let bg_colour = bg_entry.get_text();
bg_entry.set_text(Some(fg_colour));
......@@ -97,28 +125,11 @@ impl Window {
entries_container.show();
self.headerbar.set_custom_title(Some(&entries_container));
let preview = ContrastPreview::new();
let levelbar = ContrastLevelBar::new();
container.pack_start(&preview.widget, true, true, 0);
container.pack_start(&levelbar.widget, false, false, 0);
let previewer = preview.clone();
self.bg_entry.entry.connect_changed(move |entry| {
let colour = entry.get_text().unwrap();
let colour = colour.as_str();
previewer.update_bg_colour(Some(colour));
});
let previewer = preview.clone();
self.fg_entry.entry.connect_changed(move |entry| {
let colour = entry.get_text().unwrap();
let colour = colour.as_str();
previewer.update_fg_colour(Some(colour));
});
container.pack_start(&self.preview.borrow_mut().widget, true, true, 0);
container.pack_start(&self.levelbar.borrow_mut().widget, false, false, 0);
self.widget.add(&container);
// Restore state
self.state.borrow_mut().load_into(&self.widget, &self.bg_entry, &self.fg_entry);
}
fn setup_signals(&self) {}
}
use crate::colour;
use crate::colour_entry::ColourEntry;
use crate::utils::load_stylesheet;
use colorsys::Rgb;
use gio::prelude::SettingsExt;
use gtk::prelude::GtkWindowExt;
pub fn load(window: &gtk::ApplicationWindow, settings: &gio::Settings) {
let width = settings.get_int("window-width");
let height = settings.get_int("window-height");
pub struct WindowState {
settings: gio::Settings,
width: i32,
height: i32,
x: i32,
y: i32,
is_maximized: bool,
pub contrast_level: f64,
pub bg_colour: Rgb,
pub fg_colour: Rgb,
}
impl WindowState {
pub fn init(settings: gio::Settings) -> WindowState {
let width = settings.get_int("window-width");
let height = settings.get_int("window-height");
let x = settings.get_int("window-x");
let y = settings.get_int("window-y");
let is_maximized = settings.get_boolean("is-maximized");
let fg_colour = settings.get_string("foreground-colour").unwrap();
let bg_colour = settings.get_string("background-colour").unwrap();
println!("bg_colour: {}", bg_colour);
let bg_colour: Rgb = match colour::parse(bg_colour.as_str()) {
Ok(rgb) => rgb,
Err(err) => Rgb::default(),
};
let fg_colour: Rgb = match colour::parse(fg_colour.as_str()) {
Ok(rgb) => rgb,
Err(_) => Rgb::default(),
};
let contrast_level = colour::calc_contrast_level(&bg_colour, &fg_colour);
if width > -1 && height > -1 {
window.resize(width, height);
WindowState {
settings,
width,
height,
x,
y,
is_maximized,
bg_colour,
fg_colour,
contrast_level,
}
}
let x = settings.get_int("window-x");
let y = settings.get_int("window-y");
let is_maximized = settings.get_boolean("is-maximized");
pub fn load_into(&self, window: &gtk::ApplicationWindow, bg_entry: &ColourEntry, fg_entry: &ColourEntry) {
if self.width > -1 && self.height > -1 {
window.resize(self.width, self.height);
}
if x > -1 && y > -1 {
window.move_(x, y);
} else if is_maximized {
window.maximize();
if self.x > -1 && self.y > -1 {
window.move_(self.x, self.y);
} else if self.is_maximized {
window.maximize();
}
let bg_colour = self.bg_colour.to_hex_string();
let fg_colour = self.fg_colour.to_hex_string();
bg_entry.set_text(Some(bg_colour));
fg_entry.set_text(Some(fg_colour));
}
}
pub fn save(window: &gtk::ApplicationWindow, settings: &gio::Settings) {
let size = window.get_size();
let position = window.get_position();
pub fn set_bg_colour(&mut self, colour: Option<&str>) {
// update bg color property
match colour {
Some(colour) => match colour::parse(colour) {
Ok(rgb) => {
self.bg_colour = rgb.clone();
let stylesheet = format!("@define-color background_color {};", rgb.to_hex_string());
load_stylesheet(stylesheet);
self.contrast_level = colour::calc_contrast_level(&self.bg_colour, &self.fg_colour);
}
Err(_) => error!("Failed to parse colour {}", colour),
},
_ => (),
}
}
pub fn set_fg_colour(&mut self, colour: Option<&str>) {
// update bg color property
match colour {
Some(colour) => match colour::parse(colour) {
Ok(rgb) => {
self.fg_colour = rgb.clone();
let stylesheet = format!("@define-color foreground_color {};", rgb.to_hex_string());
load_stylesheet(stylesheet);
self.contrast_level = colour::calc_contrast_level(&self.bg_colour, &self.fg_colour);
}
Err(_) => error!("Failed to parse colour {}", colour),
},
_ => (),
}
}
settings.set_int("window-width", size.0);
settings.set_int("window-height", size.1);
pub fn dump(&self, window: &gtk::ApplicationWindow) {
let size = window.get_size();
let position = window.get_position();
let bg_colour = self.bg_colour.to_hex_string();
let fg_colour = self.fg_colour.to_hex_string();
settings.set_boolean("is-maximized", window.is_maximized());
self.settings.set_int("window-width", size.0);
self.settings.set_int("window-height", size.1);
settings.set_int("window-x", position.0);
settings.set_int("window-y", position.1);
self.settings.set_boolean("is-maximized", window.is_maximized());
self.settings.set_int("window-x", position.0);
self.settings.set_int("window-y", position.1);
self.settings.set_string("background-colour", &bg_colour);
self.settings.set_string("foreground-colour", &fg_colour);
}
}
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