Commit 955d009a authored by Sébastien Blin's avatar Sébastien Blin

ui: improve dark theme handling

Change-Id: I143bf016c69f51672ad98f845c601f1a00b1b5f6
Gitlab: #718
Gitlab: #1084
parent 24fbf117
<svg fill="#ffffff" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M21 6h-2v9H6v2c0 .55.45 1 1 1h11l4 4V7c0-.55-.45-1-1-1zm-4 6V3c0-.55-.45-1-1-1H3c-.55 0-1 .45-1 1v14l4-4h10c.55 0 1-.45 1-1z"/>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
fill="#000000"
height="24"
viewBox="0 0 24 24"
width="24"
version="1.1"
id="svg6"
sodipodi:docname="ic_verified_user_black_24px_with_notification.svg"
inkscape:version="0.92.1 r">
<metadata
id="metadata12">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs10" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1600"
inkscape:window-height="842"
id="namedview8"
showgrid="false"
inkscape:zoom="9.8333333"
inkscape:cx="-19.322034"
inkscape:cy="12"
inkscape:window-x="0"
inkscape:window-y="29"
inkscape:window-maximized="1"
inkscape:current-layer="svg6" />
<path
d="M0 0h24v24H0z"
fill="none"
id="path2" />
<path
fill="#ffffff"
d="M 12 1 L 10.375 1.7226562 A 6.2377381 6.2377381 0 0 1 11.564453 5.3730469 A 6.2377381 6.2377381 0 0 1 5.3261719 11.611328 A 6.2377381 6.2377381 0 0 1 3.0078125 11.158203 C 3.0802611 16.64804 6.8893005 21.752038 12 23 C 17.16 21.74 21 16.55 21 11 L 21 5 L 12 1 z M 16.589844 7.5800781 L 18 9 L 10 17 L 6 13 L 7.4101562 11.589844 L 10 14.169922 L 16.589844 7.5800781 z "
id="path4" />
<circle
r="4.9901905"
cy="5.3728104"
cx="5.3271904"
style="fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:2.43000579;stroke-opacity:1"
id="path12471-2-3-6" />
</svg>
......@@ -30,7 +30,9 @@
<file alias="quality">ic_high_quality_white_24px.svg</file>
<file alias="contacts_list">ic_people_black_24px.svg</file>
<file alias="conversations_list">ic_question_answer_black_24px.svg</file>
<file alias="conversations_list_white">ic_question_answer_white_24px.svg</file>
<file alias="contact_requests_list">ic_verified_user_black_24px_with_notification.svg</file>
<file alias="contact_requests_list_white">ic_verified_user_white_24px_with_notification.svg</file>
<file alias="history_list">ic_history_black_24px.svg</file>
<file alias="add">add.svg</file>
<file alias="reject">reject.svg</file>
......@@ -54,6 +56,7 @@
<file alias="bottom_arrow">bottom_arrow.svg</file>
<file alias="up_arrow">up_arrow.svg</file>
<file alias="qrcode">qrcode.svg</file>
<file alias="qrcode-white">qrcode-white.png</file>
<file alias="retry">retry.svg</file>
</gresource>
</gresources>
......@@ -26,6 +26,7 @@
// std
#include <algorithm>
#include <fstream>
#include <sstream>
// GTK
#include <glib/gi18n.h>
......@@ -107,6 +108,8 @@ struct _ChatViewPrivate
bool ready_ {false};
bool readyToRecord_ {false};
bool useDarkTheme {false};
std::string background;
CppImpl* cpp;
bool video_started_by_settings;
......@@ -183,6 +186,34 @@ chat_view_dispose(GObject *object)
G_OBJECT_CLASS(chat_view_parent_class)->dispose(object);
}
void
chat_view_set_dark_mode(ChatView *self, gboolean darkMode, const std::string& background)
{
auto priv = CHAT_VIEW_GET_PRIVATE(self);
priv->useDarkTheme = darkMode;
priv->background = background;
if (!priv->ready_) return;
webkit_chat_set_dark_mode(WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container), darkMode, background);
}
gboolean
on_redraw(GtkWidget*, cairo_t*, ChatView* self)
{
g_return_val_if_fail(IS_CHAT_VIEW(self), G_SOURCE_REMOVE);
auto* priv = CHAT_VIEW_GET_PRIVATE(CHAT_VIEW(self));
auto color = get_ambient_color(gtk_widget_get_toplevel(GTK_WIDGET(self)));
bool current_theme = (color.red + color.green + color.blue) / 3 < .5;
if (priv->useDarkTheme != current_theme) {
std::stringstream background;
background << "#" << std::hex << (int)(color.red * 256)
<< (int)(color.green * 256)
<< (int)(color.blue * 256);
chat_view_set_dark_mode(self, current_theme, background.str());
}
return false;
}
static void
hide_chat_view(G_GNUC_UNUSED GtkWidget *widget, ChatView *self)
{
......@@ -713,6 +744,7 @@ webkit_chat_container_ready(ChatView* self)
display_links_toggled(self);
print_text_recording(self);
load_participants_images(self);
webkit_chat_set_dark_mode(WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container), priv->useDarkTheme, priv->background);
priv->ready_ = true;
for (const auto& interaction: priv->cpp->interactionsBuffer_) {
......@@ -993,6 +1025,14 @@ build_chat_view(ChatView* self)
G_CALLBACK(on_webkit_drag_drop),
self
);
g_signal_connect(
self,
"draw",
G_CALLBACK(on_redraw),
self
);
priv->new_interaction_connection = QObject::connect(
&*(*priv->accountInfo_)->conversationModel, &lrc::api::ConversationModel::newInteraction,
[self, priv](const std::string& uid, uint64_t interactionId, lrc::api::interaction::Info interaction) {
......
......@@ -63,6 +63,8 @@ struct _ConversationsViewPrivate
GtkWidget* popupMenu_;
bool useDarkTheme {false};
QMetaObject::Connection selection_updated;
QMetaObject::Connection layout_changed;
QMetaObject::Connection modelSortedConnection_;
......@@ -143,6 +145,8 @@ render_name_and_last_interaction(G_GNUC_UNUSED GtkTreeViewColumn *tree_column,
auto contactUri = conversation.participants.front();
auto contactInfo = (*priv->accountInfo_)->contactModel->getContact(contactUri);
auto grey = priv->useDarkTheme? "#bbb" : "#666";
gtk_tree_model_get (model, iter,
0 /* col# */, &uid /* data */,
1 /* col# */, &alias /* data */,
......@@ -161,23 +165,27 @@ render_name_and_last_interaction(G_GNUC_UNUSED GtkTreeViewColumn *tree_column,
} else if (std::string(alias).empty()) {
// If no alias to show, use the best id
text = g_markup_printf_escaped(
"<span font_weight=\"bold\">%s</span>\n<span size=\"smaller\" color=\"#666\">%s</span>",
"<span font_weight=\"bold\">%s</span>\n<span size=\"smaller\" color=\"%s\">%s</span>",
bestId,
grey,
lastInteraction
);
} else if (std::string(alias) == std::string(bestId)) {
// If the alias and the best id are identical, show only the alias
text = g_markup_printf_escaped(
"<span font_weight=\"bold\">%s</span>\n<span size=\"smaller\" color=\"#666\">%s</span>",
"<span font_weight=\"bold\">%s</span>\n<span size=\"smaller\" color=\"%s\">%s</span>",
alias,
grey,
lastInteraction
);
} else {
// If the alias is not empty and not equals to the best id, show both the alias and the best id
text = g_markup_printf_escaped(
"<span font_weight=\"bold\">%s</span>\n<span size=\"smaller\" color=\"#666\">%s</span>\n<span size=\"smaller\" color=\"#666\">%s</span>",
"<span font_weight=\"bold\">%s</span>\n<span size=\"smaller\" color=\"%s\">%s</span>\n<span size=\"smaller\" color=\"%s\">%s</span>",
alias,
grey,
bestId,
grey,
lastInteraction
);
}
......@@ -783,3 +791,11 @@ conversations_view_get_current_selected(ConversationsView *self)
}
return -1;
}
void
conversations_view_set_theme(ConversationsView *self, bool darkTheme) {
g_return_if_fail(IS_CONVERSATIONS_VIEW(self));
auto priv = CONVERSATIONS_VIEW_GET_PRIVATE(self);
priv->useDarkTheme = darkTheme;
}
......@@ -39,5 +39,6 @@ GType conversations_view_get_type (void) G_GNUC_CONST;
GtkWidget *conversations_view_new (AccountInfoPointer const & accountInfo);
void conversations_view_select_conversation (ConversationsView *self, const std::string& uid);
int conversations_view_get_current_selected(ConversationsView *self);
void conversations_view_set_theme(ConversationsView *self, bool darkTheme);
G_END_DECLS
......@@ -24,6 +24,7 @@
#include <glib/gi18n.h>
// std
#include <algorithm>
#include <sstream>
// Qt
#include <QSize>
......@@ -56,6 +57,7 @@
#include "mediasettingsview.h"
#include "models/gtkqtreemodel.h"
#include "ringwelcomeview.h"
#include "utils/drawing.h"
#include "utils/files.h"
#include "ringnotify.h"
#include "accountinfopointer.h"
......@@ -113,10 +115,13 @@ struct RingMainWindowPrivate
GtkWidget *treeview_contact_requests;
GtkWidget *scrolled_window_contact_requests;
GtkWidget *webkit_chat_container; ///< The webkit_chat_container is created once, then reused for all chat views
GtkWidget *image_contact_requests_list;
GtkWidget *image_conversations_list;
GtkWidget *notifier;
GSettings *settings;
bool useDarkTheme {false};
details::CppImpl* cpp; ///< Non-UI and C++ only code
......@@ -485,6 +490,47 @@ send_text_event(RingMainWindow* self)
return G_SOURCE_REMOVE;
}
gboolean
on_redraw(GtkWidget*, cairo_t*, RingMainWindow* self)
{
g_return_val_if_fail(IS_RING_MAIN_WINDOW(self), G_SOURCE_REMOVE);
auto* priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(self));
auto color = get_ambient_color(GTK_WIDGET(self));
bool current_theme = (color.red + color.green + color.blue) / 3 < .5;
conversations_view_set_theme(CONVERSATIONS_VIEW(priv->treeview_conversations), current_theme);
if (priv->useDarkTheme != current_theme) {
ring_welcome_set_theme(RING_WELCOME_VIEW(priv->welcome_view), current_theme);
ring_welcome_update_view(RING_WELCOME_VIEW(priv->welcome_view));
gtk_image_set_from_resource(GTK_IMAGE(priv->image_contact_requests_list), current_theme?
"/net/jami/JamiGnome/contact_requests_list_white" : "/net/jami/JamiGnome/contact_requests_list");
gtk_image_set_from_resource(GTK_IMAGE(priv->image_conversations_list), current_theme?
"/net/jami/JamiGnome/conversations_list_white" : "/net/jami/JamiGnome/conversations_list");
priv->useDarkTheme = current_theme;
std::stringstream background;
background << "#" << std::hex << (int)(color.red * 256)
<< (int)(color.green * 256)
<< (int)(color.blue * 256);
auto provider = gtk_css_provider_new();
std::string background_search_entry = "background: " + background.str() + ";";
std::string css_style = ".search-entry-style { border: 0; border-radius: 0; } \
.spinner-style { border: 0; background: white; } \
.new-conversation-style { border: 0; " + background_search_entry + " transition: all 0.3s ease; border-radius: 0; } \
.new-conversation-style:hover { background: " + (priv->useDarkTheme ? "#003b4e" : "#bae5f0") + "; }";
gtk_css_provider_load_from_data(provider,
css_style.c_str(),
-1, nullptr
);
gtk_style_context_add_provider_for_screen(gdk_display_get_default_screen(gdk_display_get_default()),
GTK_STYLE_PROVIDER(provider),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
}
}
static void
on_send_text_clicked(G_GNUC_UNUSED GtkWidget*, gchar* uid, gchar* body, RingMainWindow* self)
{
......@@ -1290,18 +1336,7 @@ CppImpl::init()
g_signal_connect_swapped(widgets->button_new_conversation, "clicked", G_CALLBACK(on_search_entry_activated), self);
g_signal_connect(widgets->search_entry, "search-changed", G_CALLBACK(on_search_entry_text_changed), self);
g_signal_connect(widgets->search_entry, "key-release-event", G_CALLBACK(on_search_entry_key_released), self);
auto provider = gtk_css_provider_new();
gtk_css_provider_load_from_data(provider,
".search-entry-style { border: 0; border-radius: 0; } \
.spinner-style { border: 0; background: white; } \
.new-conversation-style { border: 0; background: white; transition: all 0.3s ease; border-radius: 0; } \
.new-conversation-style:hover { background: #bae5f0; }",
-1, nullptr
);
gtk_style_context_add_provider_for_screen(gdk_display_get_default_screen(gdk_display_get_default()),
GTK_STYLE_PROVIDER(provider),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
g_signal_connect(widgets->search_entry, "draw", G_CALLBACK(on_redraw), self);
/* react to digit key press/release events */
......@@ -2842,6 +2877,8 @@ ring_main_window_class_init(RingMainWindowClass *klass)
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_new_account_settings);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, combobox_account_selector);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, scrolled_window_contact_requests);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, image_contact_requests_list);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, image_conversations_list);
}
GtkWidget *
......
......@@ -52,6 +52,8 @@ struct _RingWelcomeViewPrivate
GtkWidget *button_qrcode;
GtkWidget *revealer_qrcode;
bool useDarkTheme {false};
AccountInfoPointer const *accountInfo_;
QMetaObject::Connection nameRegistrationEnded_;
......@@ -80,6 +82,8 @@ ring_welcome_update_view(RingWelcomeView* self) {
return;
}
auto color = priv->useDarkTheme? "white" : "black";
// Get registeredName, else the ID
gchar *ring_id = nullptr;
if(! (*priv->accountInfo_)->registeredName.empty()){
......@@ -87,7 +91,7 @@ ring_welcome_update_view(RingWelcomeView* self) {
GTK_LABEL(priv->label_explanation),
_("This is your Jami username.\nCopy and share it with your friends!")
);
ring_id = g_markup_printf_escaped("<span fgcolor=\"black\">%s</span>",
ring_id = g_markup_printf_escaped("<span fgcolor=\"%s\">%s</span>", color,
(*priv->accountInfo_)->registeredName.c_str());
}
else if (!(*priv->accountInfo_)->profileInfo.uri.empty()) {
......@@ -95,7 +99,7 @@ ring_welcome_update_view(RingWelcomeView* self) {
GTK_LABEL(priv->label_explanation),
_("This is your ID.\nCopy and share it with your friends!")
);
ring_id = g_markup_printf_escaped("<span fgcolor=\"black\">%s</span>",
ring_id = g_markup_printf_escaped("<span fgcolor=\"%s\">%s</span>", color,
(*priv->accountInfo_)->profileInfo.uri.c_str());
} else {
gtk_label_set_text(GTK_LABEL(priv->label_explanation), NULL);
......@@ -112,6 +116,18 @@ ring_welcome_update_view(RingWelcomeView* self) {
g_free(ring_id);
GError *error = NULL;
GdkPixbuf *image_qr = gdk_pixbuf_new_from_resource_at_scale(priv->useDarkTheme? "/net/jami/JamiGnome/qrcode-white" : "/net/jami/JamiGnome/qrcode",
-1, 16, TRUE, &error);
if (!image_qr) {
g_warning("Could not load icon: %s", error->message);
g_clear_error(&error);
} else {
auto image = gtk_image_new_from_pixbuf(image_qr);
gtk_button_set_image(GTK_BUTTON(priv->button_qrcode), image);
}
priv->nameRegistrationEnded_ = QObject::connect(
(*priv->accountInfo_)->accountModel,
&lrc::api::NewAccountModel::nameRegistrationEnded,
......@@ -120,7 +136,7 @@ ring_welcome_update_view(RingWelcomeView* self) {
if (accountId == (*priv->accountInfo_)->id
&& status == lrc::api::account::RegisterNameStatus::SUCCESS)
{
gchar *markup = g_markup_printf_escaped("<span fgcolor=\"black\">%s</span>",
gchar *markup = g_markup_printf_escaped("<span fgcolor=\"%s\">%s</span>", color,
name.c_str());
gtk_label_set_markup(GTK_LABEL(priv->label_ringid), markup);
g_free(markup);
......@@ -128,10 +144,20 @@ ring_welcome_update_view(RingWelcomeView* self) {
});
}
void
ring_welcome_set_theme(RingWelcomeView* self, bool useDarkTheme)
{
g_return_if_fail(IS_RING_WELCOME_VIEW(self));
auto* priv = RING_WELCOME_VIEW_GET_PRIVATE(self);
priv->useDarkTheme = useDarkTheme;
}
static void
ring_welcome_view_init(RingWelcomeView *self)
{
RingWelcomeViewPrivate *priv = RING_WELCOME_VIEW_GET_PRIVATE(self);
g_return_if_fail(IS_RING_WELCOME_VIEW(self));
auto* priv = RING_WELCOME_VIEW_GET_PRIVATE(self);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(self), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
......@@ -229,7 +255,7 @@ ring_welcome_view_init(RingWelcomeView *self)
/* QR code button */
priv->button_qrcode = gtk_button_new();
GdkPixbuf *image_qr = gdk_pixbuf_new_from_resource_at_scale("/net/jami/JamiGnome/qrcode",
GdkPixbuf *image_qr = gdk_pixbuf_new_from_resource_at_scale(priv->useDarkTheme? "/net/jami/JamiGnome/qrcode-white" : "/net/jami/JamiGnome/qrcode",
-1, 16, TRUE, &error);
if (!image_qr) {
g_warning("Could not load icon: %s", error->message);
......
......@@ -41,5 +41,6 @@ typedef struct _RingWelcomeViewClass RingWelcomeViewClass;
GType ring_welcome_view_get_type (void) G_GNUC_CONST;
GtkWidget* ring_welcome_view_new (AccountInfoPointer const & accountInfo);
void ring_welcome_update_view (RingWelcomeView* self);
void ring_welcome_set_theme (RingWelcomeView* self, bool useDarkTheme);
G_END_DECLS
......@@ -373,3 +373,31 @@ ring_draw_unread_messages(const GdkPixbuf *avatar, int unread_count) {
return pixbuf;
}
GdkRGBA
get_ambient_color(GtkWidget* widget)
{
auto surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,4,4);
auto cr = cairo_create(surface);
if (widget) {
auto context = gtk_widget_get_style_context(widget);
gtk_render_background(context, cr, 0, 0, 1, 1);
cairo_surface_flush(surface);
auto content = cairo_image_surface_get_data(surface);
if(content[3] == 255) {
auto ret = GdkRGBA {
content[2]/255.0f,
content[1]/255.0f,
content[0]/255.0f,
content[3]/255.0f
};
cairo_destroy(cr);
cairo_surface_destroy(surface);
return ret;
}
widget=gtk_widget_get_parent(GTK_WIDGET(widget));
}
cairo_destroy(cr);
cairo_surface_destroy(surface);
return GdkRGBA{1.0f, 1.0f, 1.0f, 1.0f};
}
\ No newline at end of file
......@@ -44,4 +44,6 @@ enum class IconStatus {
};
GdkPixbuf *ring_draw_status(const GdkPixbuf *avatar, IconStatus status);
GdkRGBA get_ambient_color(GtkWidget* widget);
#endif /* _DRAWING */
......@@ -769,6 +769,34 @@ webkit_chat_set_record_visible(WebKitChatContainer *view, bool isVisible)
g_free(function_call);
}
void
webkit_chat_set_dark_mode(WebKitChatContainer *view, bool darkMode, const std::string& background)
{
std::string theme = "";
if (darkMode) {
theme = "\
--jami-light-blue: #003b4e;\
--jami-dark-blue: #28b1ed;\
--text-color: white;\
--timestamp-color: #bbb;\
--message-out-bg: #28b1ed;\
--message-out-txt: white;\
--message-in-bg: #616161;\
--message-in-txt: white;\
--file-in-timestamp-color: #999;\
--file-out-timestamp-color: #eee;\
--bg-color: " + background + ";\
--non-action-icon-color: white;\
--placeholder-text-color: #2b2b2b;\
--invite-hover-color: black;\
--hairline-color: #262626;\
";
}
gchar* function_call = g_strdup_printf("setTheme(\"%s\")", theme.c_str());
webkit_chat_container_execute_js(view, function_call);
g_free(function_call);
}
void
webkit_chat_update_chatview_frame(WebKitChatContainer *view, bool accountEnabled, bool isBanned, bool isTemporary, const gchar* alias, const gchar* bestId)
{
......
......@@ -56,6 +56,7 @@ void webkit_chat_container_set_display_links (WebKitChatContainer *view
void webkit_chat_container_set_invitation (WebKitChatContainer *view, bool show, const std::string& contactUri, const std::string& contactId);
void webkit_chat_set_header_visible (WebKitChatContainer *view, bool isVisible);
void webkit_chat_set_record_visible (WebKitChatContainer *view, bool isVisible);
void webkit_chat_set_dark_mode (WebKitChatContainer *view, bool darkMode, const std::string& background);
void webkit_chat_update_chatview_frame (WebKitChatContainer *view, bool accountEnabled, bool isBanned, bool isInvited, const gchar* alias, const gchar* bestId);
G_END_DECLS
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