Commit 3bd8fc1e authored by Bilal Elmoussaoui's avatar Bilal Elmoussaoui
Browse files

Use color picker for selecting colors

This also removes our dependency of colorsys and uses gdk::RGBA
It also cleans few things a bit
parent 67027cb9
Pipeline #101989 passed with stages
in 21 minutes and 49 seconds
......@@ -96,16 +96,11 @@ dependencies = [
"time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "colorsys"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "contrast"
version = "0.0.1"
dependencies = [
"colorsys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"dbus 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
"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)",
"gio 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
......@@ -118,6 +113,15 @@ dependencies = [
"sass-rs 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "dbus"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)",
"libdbus-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "env_logger"
version = "0.6.2"
......@@ -330,6 +334,14 @@ name = "libc"
version = "0.2.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libdbus-sys"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "libhandy"
version = "0.4.0"
......@@ -590,7 +602,7 @@ dependencies = [
"checksum cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)" = "39f75544d7bbaf57560d2168f28fd649ff9c76153874db88bdbdfd839b1a7e7d"
"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33"
"checksum chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "77d81f58b7301084de3b958691458a53c3f7e0b1d702f77e550b6a88e3a88abe"
"checksum colorsys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6a0089b7f73ef0f49a1562033951fc9ad150b84a1ecc3cea806843d4baaa8335"
"checksum dbus 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "48b5f0f36f1eebe901b0e6bee369a77ed3396334bf3f09abd46454a576f71819"
"checksum env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3"
"checksum fragile 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f8140122fa0d5dcb9fc8627cfce2b37cc1500f752636d46ea28bc26785c2f9"
"checksum gdk 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6243e995f41f3a61a31847e54cc719edce93dd9140c89dca3b9919be1cfe22d5"
......@@ -609,6 +621,7 @@ dependencies = [
"checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114"
"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14"
"checksum libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)" = "3262021842bf00fe07dbd6cf34ff25c99d7a7ebef8deea84db72be3ea3bb0aff"
"checksum libdbus-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "69382cb3be797cfe62ba89ac5e594a0e3022914ff44867ffbcfc03daa24eebd7"
"checksum libhandy 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7195919b261667e13240c14004c3fdc1a07a77ee865a9e0da5c2a4e134171c25"
"checksum libhandy-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "17019593005fd387b0d2f6e5b29533796e3206e9f433dbf74846a609ceeb5fbf"
"checksum locale_config 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "73ac19ebe45489e5d53b4346d8b90bb3dd03275c5fdf2ce22a982516d86b535c"
......
......@@ -13,6 +13,6 @@ log = "0.4"
gettext-rs= { version = "0.4.1", features = ["gettext-system"] }
libhandy = "0.4.0"
pango = "0.7.0"
colorsys = "0.5.1"
sass-rs = "0.2.2"
pretty_env_logger = "0.3.0"
dbus = "0.6.5"
......@@ -19,7 +19,8 @@
"--filesystem=xdg-run/dconf",
"--filesystem=~/.config/dconf:ro",
"--talk-name=ca.desrt.dconf",
"--env=DCONF_USER_CONFIG_DIR=.config/dconf"
"--env=DCONF_USER_CONFIG_DIR=.config/dconf",
"--talk-name=org.gnome.Shell.Screenshot"
],
"build-options": {
"append-path": "/usr/lib/sdk/rust-stable/bin",
......
......@@ -51,5 +51,3 @@ entry selection{
.chart-bar-marker separator {
background-color: @foreground_color;
}
use crate::colour;
use crate::config;
use crate::styles::update_styles;
use crate::window::Window;
......@@ -7,6 +8,7 @@ use std::env;
pub enum Message {
ApplyStylesheet(String),
ColorChanged,
}
pub struct Application {
......@@ -30,22 +32,36 @@ impl Application {
let builder = gtk::Builder::new_from_resource("/org/gnome/design/Contrast/shortcuts.ui");
let dialog: gtk::ShortcutsWindow = builder.get_object("shortcuts").unwrap();
window.widget.set_help_overlay(Some(&dialog));
let provider = gtk::CssProvider::new();
let provider_for_cb = provider.clone();
let sender_rec = sender.clone();
let provider_rec = provider.clone();
let levelbar = window.levelbar.clone();
let preview = window.preview.clone();
let window_state = window.state.clone();
receiver.attach(None, move |msg| {
match msg {
Message::ApplyStylesheet(text) => match provider_for_cb.load_from_data(text.as_bytes()) {
Ok(_) => (),
Err(err) => error!("Stylesheet couldn't be updated {}", err),
},
Message::ApplyStylesheet(text) => {
match provider_rec.load_from_data(text.as_bytes()) {
Ok(_) => (),
Err(err) => error!("Stylesheet couldn't be updated {}", err),
};
}
Message::ColorChanged => {
let fg_colour = window_state.borrow().fg_colour;
let bg_colour = window_state.borrow().bg_colour;
let contrast_level = colour::calc_contrast_level(&bg_colour, &fg_colour);
levelbar.borrow_mut().set_contrast_level(contrast_level);
preview.borrow_mut().set_contrast_level(contrast_level);
update_styles(&sender_rec, fg_colour, bg_colour);
}
}
glib::Continue(true)
});
let application = Self { app, window, provider, sender };
application.setup_gactions();
application.setup_signals();
application.setup_css();
......@@ -91,10 +107,7 @@ impl Application {
gtk::CssProvider::load_from_resource(&p, "/org/gnome/design/Contrast/style.css");
gtk::StyleContext::add_provider_for_screen(&gdk::Screen::get_default().unwrap(), &p, 500);
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();
update_styles(&self.sender, fg_colour, bg_colour);
self.sender.send(Message::ColorChanged).ok(); // never fails
}
pub fn run(&self) {
......
use colorsys::Rgb;
use std::str::FromStr;
#[derive(Debug)]
pub enum ColorError {
FailedToParse,
Invalid,
}
pub fn calc_contrast_level(bg_color: &Rgb, fg_color: &Rgb) -> f64 {
pub fn calc_contrast_level(bg_color: &gdk::RGBA, fg_color: &gdk::RGBA) -> f64 {
let bg_luminance = get_luminance(bg_color);
let fg_luminance = get_luminance(fg_color);
(bg_luminance.max(fg_luminance) + 0.05) / (bg_luminance.min(fg_luminance) + 0.05)
}
pub fn parse(colour: &str) -> Result<Rgb, ColorError> {
// TODO: We only need this to convert GBA to HEX.
// I should replace that ASAP
// This could allow me to ditch colorsys dependency.
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_sRGB(c: f64) -> f64 {
let c = c / 255.0;
fn get_srgb(c: f64) -> f64 {
if c <= 0.03928 {
return c / 12.92;
} else {
......@@ -42,9 +12,13 @@ fn get_sRGB(c: f64) -> f64 {
}
}
pub fn get_luminance(color: &Rgb) -> f64 {
let red = get_sRGB(color.get_red());
let blue = get_sRGB(color.get_blue());
let green = get_sRGB(color.get_green());
pub fn get_luminance(color: &gdk::RGBA) -> f64 {
let red = get_srgb(color.red);
let blue = get_srgb(color.blue);
let green = get_srgb(color.green);
red * 0.2126 + blue * 0.0722 + green * 0.7152
}
pub fn rgba_to_hex(rgb: &gdk::RGBA) -> String {
format!("#{:X}{:X}{:X}", (rgb.red * 255.0) as i32, (rgb.green * 255.0) as i32, (rgb.blue * 255.0) as i32)
}
use crate::colour;
use crate::gtk::{BoxExt, ButtonExt, ColorChooserExt, DialogExt, EntryExt, GtkWindowExt, StyleContextExt, WidgetExt};
use std::str::FromStr;
use crate::dbus::{BusType, Connection, Message};
use crate::gtk::{BoxExt, ButtonExt, EntryExt, StyleContextExt, WidgetExt};
use std::collections::HashMap;
#[derive(Clone)]
pub struct ColourEntry {
pub widget: gtk::Box,
......@@ -9,17 +9,17 @@ pub struct ColourEntry {
}
impl ColourEntry {
pub fn new(label: String, parent: &gtk::ApplicationWindow) -> ColourEntry {
pub fn new(label: String) -> ColourEntry {
let entry = gtk::Entry::new();
let widget = gtk::Box::new(gtk::Orientation::Horizontal, 0);
let colour_entry = ColourEntry { widget, entry };
colour_entry.init(label, parent);
colour_entry.init(label);
colour_entry
}
fn init(&self, label: String, parent: &gtk::ApplicationWindow) {
fn init(&self, label: String) {
self.widget.set_halign(gtk::Align::Center);
self.widget.set_valign(gtk::Align::Center);
self.widget.get_style_context().add_class("linked");
......@@ -41,27 +41,25 @@ impl ColourEntry {
colour_selector.show();
self.widget.pack_start(&colour_selector, false, false, 0);
let parent_window = parent.clone();
let entry = self.entry.clone();
colour_selector.connect_clicked(move |_| {
let colour_chooser = gtk::ColorChooserDialog::new(Some(&label), Some(&parent_window));
colour_chooser.set_modal(true);
let colour_str = entry.get_text().unwrap();
match gdk::RGBA::from_str(&colour_str) {
Ok(rgba) => colour_chooser.set_rgba(&rgba),
Err(_) => error!("Failed to parse color {}", colour_str),
}
colour_selector.connect_clicked(move |_| match Connection::get_private(BusType::Session) {
Ok(conn) => {
let msg = Message::new_method_call("org.gnome.Shell.Screenshot", "/org/gnome/Shell/Screenshot", "org.gnome.Shell.Screenshot", "PickColor").unwrap();
match conn.send_with_reply_and_block(msg, 60000) {
Ok(response) => {
let result: HashMap<String, dbus::arg::Variant<Box<dbus::arg::RefArg>>, _> = response.get1().unwrap();
let mut color = result.get("color").unwrap().0.as_iter().unwrap();
let red = color.next().unwrap().as_f64().unwrap();
let green = color.next().unwrap().as_f64().unwrap();
let blue = color.next().unwrap().as_f64().unwrap();
let entry = entry.clone();
colour_chooser.connect_response(move |dialog, res_type| {
if res_type == gtk::ResponseType::Ok {
let colour = colour::parse(&dialog.get_rgba().to_string()).unwrap();
entry.set_text(&colour.to_hex_string());
let rgb = gdk::RGBA { red, green, blue, alpha: 1.0 };
entry.set_text(&colour::rgba_to_hex(&rgb));
}
Err(err) => error!("Failed to call PickColor {}", err),
}
dialog.destroy();
});
colour_chooser.show();
}
Err(err) => error!("Failed to create a session Bus {}", err),
});
self.widget.show();
}
......
......@@ -2,6 +2,7 @@ extern crate pretty_env_logger;
#[macro_use]
extern crate log;
extern crate dbus;
extern crate gio;
extern crate glib;
extern crate gtk;
......
use crate::application::Message;
use crate::colour;
use colorsys::Rgb;
use sass_rs::{compile_string, Options};
use std::thread;
pub fn update_styles(sender: &glib::Sender<Message>, fg: String, bg: String) {
pub fn update_styles(sender: &glib::Sender<Message>, fg: gdk::RGBA, bg: gdk::RGBA) {
let sender = sender.clone();
let res = thread::Builder::new().name("style worker".to_string()).spawn(move || {
let colours = format!(include_str!("Adwaita/_colors.scss"), fg = fg, bg = bg);
let colours = format!(include_str!("Adwaita/_colors.scss"), fg = fg.to_string(), bg = bg.to_string());
let colours_public = include_str!("Adwaita/_colors-public.scss");
let common = include_str!("Adwaita/_common.scss");
let drawing = include_str!("Adwaita/_drawing.scss");
let bg_colour = colour::parse(&bg).unwrap_or(Rgb::default());
let use_light = colour::get_luminance(&bg_colour) > 160.0 / 255.0;
let use_light = colour::get_luminance(&bg) > 160.0 / 255.0;
let variant = if use_light { "light" } else { "dark" };
let mut scss = format!("$variant: '{}'; {} {} {} {}", variant, colours, drawing, common, colours_public);
......@@ -24,8 +22,8 @@ pub fn update_styles(sender: &glib::Sender<Message>, fg: String, bg: String) {
} else {
scss.push_str("window:not(.main-window) label, dialog label{ color: white;} *:link {color: darker(white);}");
}
scss.push_str(&format!("@define-color background_color {};", bg));
scss.push_str(&format!("@define-color foreground_color {};", fg));
scss.push_str(&format!("@define-color background_color {};", bg.to_string()));
scss.push_str(&format!("@define-color foreground_color {};", fg.to_string()));
match compile_string(&scss, Options::default()) {
Ok(styles) => {
if let Err(err) = sender.send(Message::ApplyStylesheet(styles)) {
......
......@@ -4,7 +4,7 @@ use crate::config::{APP_ID, PROFILE};
use crate::contrast_level::ContrastLevelBar;
use crate::contrast_preview::ContrastPreview;
use crate::libhandy::HeaderBarExt;
use crate::styles::update_styles;
use crate::window_state::WindowState;
use glib::ObjectExt;
use gtk::prelude::{BoxExt, BuilderExtManual, ButtonExt, ContainerExt, EditableSignals, MenuButtonExt, StyleContextExt, WidgetExt};
......@@ -20,8 +20,8 @@ pub struct Window {
reverse_btn: gtk::Button,
headerbar: libhandy::HeaderBar,
pub state: Rc<RefCell<WindowState>>,
levelbar: Rc<RefCell<ContrastLevelBar>>,
preview: Rc<RefCell<ContrastPreview>>,
pub levelbar: Rc<RefCell<ContrastLevelBar>>,
pub preview: Rc<RefCell<ContrastPreview>>,
sender: glib::Sender<Message>,
}
......@@ -33,18 +33,15 @@ impl Window {
let headerbar: libhandy::HeaderBar = builder.get_object("headerbar").unwrap();
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 bg_entry = ColourEntry::new("Background Colour".to_string());
let fg_entry = ColourEntry::new("Foreground Colour".to_string());
let reverse_btn = gtk::Button::new();
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");
......@@ -87,8 +84,6 @@ impl Window {
}
fn setup_signals(&self) {
let state = self.state.clone();
let level_bar = self.levelbar.clone();
let preview = self.preview.clone();
let fg_entry = self.fg_entry.clone();
let bg_entry = self.bg_entry.clone();
let sender = self.sender.clone();
......@@ -99,19 +94,13 @@ impl Window {
state.borrow_mut().set_bg_colour(Some(&bg_colour));
state.borrow_mut().set_fg_colour(Some(&fg_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);
update_styles(&sender, fg_colour, bg_colour);
sender.send(Message::ColorChanged).ok(); // never fails
};
let bg_handle = self.bg_entry.entry.connect_changed(on_entry_changed.clone());
let fg_handle = self.fg_entry.entry.connect_changed(on_entry_changed.clone());
let state = self.state.clone();
let level_bar = self.levelbar.clone();
let preview = self.preview.clone();
let fg_entry = self.fg_entry.clone();
let bg_entry = self.bg_entry.clone();
let sender = self.sender.clone();
......@@ -125,14 +114,10 @@ impl Window {
fg_entry.entry.unblock_signal(&fg_handle);
bg_entry.entry.unblock_signal(&bg_handle);
state.borrow_mut().set_bg_colour(Some(&bg_colour));
state.borrow_mut().set_fg_colour(Some(&fg_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);
state.borrow_mut().set_bg_colour(Some(&fg_colour));
state.borrow_mut().set_fg_colour(Some(&bg_colour));
update_styles(&sender, bg_colour, fg_colour);
sender.send(Message::ColorChanged).ok(); // never fails
});
}
......@@ -155,8 +140,8 @@ impl Window {
entries_container.show();
self.headerbar.set_custom_title(Some(&entries_container));
container.pack_start(&self.preview.borrow_mut().widget, true, true, 0);
container.pack_start(&self.levelbar.borrow_mut().widget, false, false, 0);
container.pack_start(&self.preview.borrow().widget, true, true, 0);
container.pack_start(&self.levelbar.borrow().widget, false, false, 0);
self.widget.add(&container);
// Restore state
......
use crate::colour;
use crate::colour_entry::ColourEntry;
use colorsys::Rgb;
use gio::prelude::SettingsExt;
use gtk::prelude::GtkWindowExt;
use std::str::FromStr;
pub struct WindowState {
settings: gio::Settings,
x: i32,
y: i32,
is_maximized: bool,
pub contrast_level: f64,
pub bg_colour: Rgb,
pub fg_colour: Rgb,
pub bg_colour: gdk::RGBA,
pub fg_colour: gdk::RGBA,
}
impl WindowState {
......@@ -23,15 +22,18 @@ impl WindowState {
let fg_colour = settings.get_string("foreground-colour").unwrap();
let bg_colour = settings.get_string("background-colour").unwrap();
let bg_colour: Rgb = match colour::parse(bg_colour.as_str()) {
Ok(rgb) => rgb,
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);
let bg_colour = gdk::RGBA::from_str(bg_colour.as_str()).unwrap_or(gdk::RGBA {
red: 0.0,
green: 0.0,
blue: 0.0,
alpha: 1.0,
});
let fg_colour = gdk::RGBA::from_str(fg_colour.as_str()).unwrap_or(gdk::RGBA {
red: 1.0,
green: 1.0,
blue: 1.0,
alpha: 1.0,
});
WindowState {
settings,
......@@ -40,7 +42,6 @@ impl WindowState {
is_maximized,
bg_colour,
fg_colour,
contrast_level,
}
}
......@@ -53,38 +54,38 @@ impl WindowState {
window.maximize();
}
let bg_colour = self.bg_colour.to_hex_string();
let fg_colour = self.fg_colour.to_hex_string();
let bg_colour = colour::rgba_to_hex(&self.bg_colour);
let fg_colour = colour::rgba_to_hex(&self.fg_colour);
bg_entry.set_text(Some(bg_colour));
fg_entry.set_text(Some(fg_colour));
}
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();
self.contrast_level = colour::calc_contrast_level(&self.bg_colour, &self.fg_colour);
}
Err(_) => error!("Failed to parse colour {}", colour),
},
_ => (),
Some(colour) => {
self.bg_colour = gdk::RGBA::from_str(colour).unwrap_or(gdk::RGBA {
red: 0.0,
green: 0.0,
blue: 0.0,
alpha: 1.0,
});
}
None => (),
}
}
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();
self.contrast_level = colour::calc_contrast_level(&self.bg_colour, &self.fg_colour);
}
Err(_) => error!("Failed to parse colour {}", colour),
},
_ => (),
Some(colour) => {
self.fg_colour = gdk::RGBA::from_str(colour).unwrap_or(gdk::RGBA {
red: 1.0,
green: 1.0,
blue: 1.0,
alpha: 1.0,
});
}
None => (),
}
}
......
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