From 59f6173282e9b7cbd4db372750d27173d1296ffb Mon Sep 17 00:00:00 2001 From: Dorota Czaplejewicz Date: Mon, 18 Nov 2019 20:16:29 +0000 Subject: [PATCH 1/3] theme: Use a matching layout theme for any widget theme Dedicated styling is now possible for themes which have a corresponding style-theme.css file. Adwaita:dark gets one, whereas other themes use the new generic fallback theme. --- data/squeekboard.gresources.xml | 1 + data/style-Adwaita:dark.css | 46 +++++++++++ data/style.css | 35 ++++---- eek/eek-renderer.c | 6 +- src/lib.rs | 1 + src/style.h | 7 ++ src/style.rs | 139 ++++++++++++++++++++++++++++++++ 7 files changed, 215 insertions(+), 20 deletions(-) create mode 100644 data/style-Adwaita:dark.css create mode 100644 src/style.h create mode 100644 src/style.rs diff --git a/data/squeekboard.gresources.xml b/data/squeekboard.gresources.xml index 85805783..8e98c977 100644 --- a/data/squeekboard.gresources.xml +++ b/data/squeekboard.gresources.xml @@ -2,6 +2,7 @@ style.css + style-Adwaita:dark.css popup.ui icons/key-enter.svg icons/key-shift.svg diff --git a/data/style-Adwaita:dark.css b/data/style-Adwaita:dark.css new file mode 100644 index 00000000..815c278e --- /dev/null +++ b/data/style-Adwaita:dark.css @@ -0,0 +1,46 @@ +sq_view { + background-color: rgba(0, 0, 0, 255); + color: #ffffff; + font-family: cantarell, sans-serif; +} + +sq_view sq_button { + color: #deddda; + background: #464448; + border-style: solid; + border-width: 1px; + border-color: #5e5c64; + border-radius: 3px; + margin: 4px 2px 4px 2px; +} + +sq_view.wide sq_button { + margin: 1px 1px 1px 1px; +} + +sq_button:active { + background: #747077; + border-color: #96949d; +} + +sq_button.altline, +sq_button.special, +sq_button.wide { + background: #2b292f; + border-color: #3e3a44; +} + +sq_button.locked { + background: #ffffff; + color: #2b292f; +} + +#Return { + background: #1c71d8; + border-color: #1a5fb4; +} + +#Return:active { + background: #1c71d8; + border-color: #3584e4; +} diff --git a/data/style.css b/data/style.css index 815c278e..3b47faa5 100644 --- a/data/style.css +++ b/data/style.css @@ -1,15 +1,15 @@ sq_view { - background-color: rgba(0, 0, 0, 255); - color: #ffffff; + background-color: @theme_base_color; /*rgba(0, 0, 0, 255);*/ + color: @theme_text_color; /*#ffffff;*/ font-family: cantarell, sans-serif; } sq_view sq_button { - color: #deddda; - background: #464448; + color: @theme_fg_color; /*#deddda;*/ + background: mix(@theme_bg_color, @theme_base_color, -0.5); /* #464448; */ border-style: solid; border-width: 1px; - border-color: #5e5c64; + border-color: @borders; /* #5e5c64;*/ border-radius: 3px; margin: 4px 2px 4px 2px; } @@ -18,29 +18,32 @@ sq_view.wide sq_button { margin: 1px 1px 1px 1px; } -sq_button:active { - background: #747077; - border-color: #96949d; +sq_button:active, +sq_button.altline:active, +sq_button.special:active, +sq_button.wide:active { + background: mix(@theme_bg_color, @theme_selected_bg_color, 0.4);/* #747077; */ + border-color: mix(@borders, @theme_selected_fg_color, 0.5);/* #96949d; */ } sq_button.altline, sq_button.special, sq_button.wide { - background: #2b292f; - border-color: #3e3a44; + background: mix(@theme_bg_color, @theme_base_color, 0.5); /*#2b292f;*/ + border-color: @borders; /* #3e3a44; */ } sq_button.locked { - background: #ffffff; - color: #2b292f; + background: @theme_fg_color; /*#ffffff;*/ + color: @theme_bg_color; /*#2b292f;*/ } #Return { - background: #1c71d8; - border-color: #1a5fb4; + background: @theme_selected_bg_color; /* #1c71d8; */ + border-color: @borders; /*#1a5fb4;*/ } #Return:active { - background: #1c71d8; - border-color: #3584e4; + background: mix(@theme_selected_bg_color, @theme_bg_color, 0.4); /*#1c71d8;*/ + border-color: @borders; /*#3584e4;*/ } diff --git a/eek/eek-renderer.c b/eek/eek-renderer.c index f76bcedf..1bfaf672 100644 --- a/eek/eek-renderer.c +++ b/eek/eek-renderer.c @@ -26,6 +26,7 @@ #include "eek-keyboard.h" #include "eek-renderer.h" +#include "src/style.h" enum { PROP_0, @@ -623,10 +624,7 @@ eek_renderer_init (EekRenderer *self) gtk_icon_theme_add_resource_path (theme, "/sm/puri/squeekboard/icons"); - /* Create a default CSS provider and load a style sheet */ - priv->css_provider = gtk_css_provider_new (); - gtk_css_provider_load_from_resource (priv->css_provider, - "/sm/puri/squeekboard/style.css"); + priv->css_provider = squeek_load_style(); } static void diff --git a/src/lib.rs b/src/lib.rs index d1b7f1b2..3795b257 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,6 +24,7 @@ mod outputs; mod popover; mod resources; mod submission; +mod style; pub mod tests; pub mod util; mod xdg; diff --git a/src/style.h b/src/style.h new file mode 100644 index 00000000..6fd7c8b7 --- /dev/null +++ b/src/style.h @@ -0,0 +1,7 @@ +#ifndef __STYLE_H +#define __STYLE_H +#include "gtk/gtk.h" + +GtkCssProvider *squeek_load_style(); + +#endif diff --git a/src/style.rs b/src/style.rs new file mode 100644 index 00000000..bead76da --- /dev/null +++ b/src/style.rs @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2000 Red Hat, Inc. + * Copyright (C) 2019 Purism, SPC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see .Free + */ + +/*! CSS data loading */ + +use std::env; + +use glib::object::ObjectExt; + +/// Gathers stuff defined in C or called by C +pub mod c { + use super::*; + use gio; + use gtk; + use gtk_sys; + + use gtk::CssProviderExt; + use glib::translate::ToGlibPtr; + + /// Loads the layout style based on current theme + /// without having to worry about string allocation + #[no_mangle] + pub extern "C" + fn squeek_load_style() -> *const gtk_sys::GtkCssProvider { + unsafe { gtk::set_initialized() }; + let theme = gtk::Settings::get_default() + .map(|settings| get_theme_name(&settings)); + + let css_name = path_from_theme(theme); + + let resource_name = if gio::resources_get_info( + &css_name, + gio::ResourceLookupFlags::NONE + ).is_ok() { + css_name + } else { // use default if this path doesn't exist + path_from_theme(None) + }; + + let provider = gtk::CssProvider::new(); + provider.load_from_resource(&resource_name); + provider.to_glib_full() + } +} + +// not Adwaita, but rather fall back to default +const DEFAULT_THEME_NAME: &str = ""; + +/// Sugar for logging errors in results +trait Warn { + type Value; + fn ok_warn(self, msg: &str) -> Option; +} + +impl Warn for Result { + type Value = T; + fn ok_warn(self, msg: &str) -> Option { + self.map_err(|e| { + eprintln!("{}: {}", msg, e); + e + }).ok() + } +} + +struct GtkTheme { + name: String, + variant: Option, +} + +/// Gets theme as determined by the toolkit +/// Ported from GTK's gtksettings.c +fn get_theme_name(settings: >k::Settings) -> GtkTheme { + let env_theme = env::var("GTK_THEME") + .map(|theme| { + let mut parts = theme.splitn(2, ":"); + GtkTheme { + // guaranteed at least empty string + // as the first result from splitting a string + name: parts.next().unwrap().into(), + variant: parts.next().map(String::from) + } + }) + .map_err(|e| { + match &e { + env::VarError::NotPresent => {}, + e => eprintln!("GTK_THEME variable invalid: {}", e), + }; + e + }).ok(); + + match env_theme { + Some(theme) => theme, + None => GtkTheme { + name: { + settings.get_property("gtk-theme-name") + .ok_warn("No theme name") + .and_then(|value| value.get::()) + .unwrap_or(DEFAULT_THEME_NAME.into()) + }, + variant: { + settings.get_property("gtk-application-prefer-dark-theme") + .ok_warn("No settings key") + .and_then(|value| value.get::()) + .and_then(|dark_preferred| match dark_preferred { + true => Some("dark".into()), + false => None, + }) + }, + }, + } +} + +fn path_from_theme(theme: Option) -> String { + format!( + "/sm/puri/squeekboard/style{}.css", + match theme { + Some(GtkTheme { name, variant: Some(variant) }) => { + format!("-{}:{}", name, variant) + }, + Some(GtkTheme { name, variant: None }) => format!("-{}", name), + None => "".into(), + } + ) +} From 6d5f793718fa4513ded105d33bfc6f080105aa7b Mon Sep 17 00:00:00 2001 From: Dorota Czaplejewicz Date: Mon, 18 Nov 2019 20:30:17 +0000 Subject: [PATCH 2/3] util: Include Result logger --- src/style.rs | 17 +---------------- src/util.rs | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/style.rs b/src/style.rs index bead76da..b8f147a7 100644 --- a/src/style.rs +++ b/src/style.rs @@ -21,6 +21,7 @@ use std::env; use glib::object::ObjectExt; +use util::Warn; /// Gathers stuff defined in C or called by C pub mod c { @@ -61,22 +62,6 @@ pub mod c { // not Adwaita, but rather fall back to default const DEFAULT_THEME_NAME: &str = ""; -/// Sugar for logging errors in results -trait Warn { - type Value; - fn ok_warn(self, msg: &str) -> Option; -} - -impl Warn for Result { - type Value = T; - fn ok_warn(self, msg: &str) -> Option { - self.map_err(|e| { - eprintln!("{}: {}", msg, e); - e - }).ok() - } -} - struct GtkTheme { name: String, variant: Option, diff --git a/src/util.rs b/src/util.rs index 1a86330d..891e9d28 100644 --- a/src/util.rs +++ b/src/util.rs @@ -177,6 +177,22 @@ impl Borrow> for Pointer { } } +/// Sugar for logging errors in results +pub trait Warn { + type Value; + fn ok_warn(self, msg: &str) -> Option; +} + +impl Warn for Result { + type Value = T; + fn ok_warn(self, msg: &str) -> Option { + self.map_err(|e| { + eprintln!("{}: {}", msg, e); + e + }).ok() + } +} + pub trait WarningHandler { /// Handle a warning fn handle(&mut self, warning: &str); From 4ee8a91dfeda5f324714ab8edb7d82a32cf11c8f Mon Sep 17 00:00:00 2001 From: Dorota Czaplejewicz Date: Mon, 18 Nov 2019 20:43:09 +0000 Subject: [PATCH 3/3] build: Bring back squeekboard as a first class executable With styles no longer being inconsistent, there's no need to override styles by default. The override script remains for PureOS packaging purposes. --- debian/squeekboard.install | 2 +- debian/squeekboard.lintian-overrides | 2 +- src/meson.build | 17 +++-------------- tools/{squeekboard.in => squeekboard-restyled} | 2 +- 4 files changed, 6 insertions(+), 17 deletions(-) rename tools/{squeekboard.in => squeekboard-restyled} (92%) diff --git a/debian/squeekboard.install b/debian/squeekboard.install index c87f05b7..7f2df323 100644 --- a/debian/squeekboard.install +++ b/debian/squeekboard.install @@ -1,2 +1,2 @@ -usr/bin/squeekboard-real /usr/bin +tools/squeekboard-restyled usr/bin usr/bin/squeekboard /usr/bin diff --git a/debian/squeekboard.lintian-overrides b/debian/squeekboard.lintian-overrides index 0167bdc4..5be12589 100644 --- a/debian/squeekboard.lintian-overrides +++ b/debian/squeekboard.lintian-overrides @@ -1,2 +1,2 @@ # yaml-rust 0.4.3 shares some roots with libyaml, including the string which lintian checks, creating a false positive -squeekboard binary: embedded-library usr/bin/squeekboard-real: libyaml +squeekboard binary: embedded-library usr/bin/squeekboard: libyaml diff --git a/src/meson.build b/src/meson.build index 36f817bb..fc1f5f4e 100644 --- a/src/meson.build +++ b/src/meson.build @@ -97,20 +97,7 @@ libsqueekboard = static_library('libsqueekboard', '-DEEK_COMPILATION=1'], ) -# the straight binary needs to be demoted in favor of the wrapper script -# due to styling being inconsistent -bindir = join_paths(prefix, get_option('bindir')) -wrapper_conf = configuration_data() -wrapper_conf.set('bindir', bindir) -configure_file( - input: '../tools/squeekboard.in', - output: 'squeekboard', - install_dir: bindir, - configuration: wrapper_conf, - install: true, -) - -squeekboard = executable('squeekboard-real', +squeekboard = executable('squeekboard', 'server-main.c', wl_proto_sources, squeekboard_resources, @@ -125,6 +112,8 @@ squeekboard = executable('squeekboard-real', '-DEEK_COMPILATION=1'], ) +bindir = join_paths(prefix, get_option('bindir')) + test_layout = custom_target('squeekboard-test-layout', build_by_default: true, # meson doesn't track all inputs, cargo does diff --git a/tools/squeekboard.in b/tools/squeekboard-restyled similarity index 92% rename from tools/squeekboard.in rename to tools/squeekboard-restyled index ea5d239c..ca6a6281 100755 --- a/tools/squeekboard.in +++ b/tools/squeekboard-restyled @@ -12,4 +12,4 @@ for DIR in ${DIRS}; do fi; done; -exec @bindir@/squeekboard-real +exec $(which squeekboard)