Port from GTK3 to GTK4

This commit is contained in:
fiaxh 2022-02-14 14:55:59 +01:00
parent f25bfb0096
commit 7e7dcedaf3
160 changed files with 4626 additions and 5550 deletions

38
cmake/FindGDK4.cmake Normal file
View file

@ -0,0 +1,38 @@
include(PkgConfigWithFallback)
find_pkg_config_with_fallback(GDK4
PKG_CONFIG_NAME gdk-4.0
LIB_NAMES gdk-4
INCLUDE_NAMES gdk/gdk.h
INCLUDE_DIR_SUFFIXES gtk-4.0 gtk-4.0/include gtk+-4.0 gtk+-4.0/include
DEPENDS Pango Cairo GDKPixbuf2
)
if(GDK4_FOUND AND NOT GDK4_VERSION)
find_file(GDK4_VERSION_HEADER "gdk/gdkversionmacros.h" HINTS ${GDK4_INCLUDE_DIRS})
mark_as_advanced(GDK4_VERSION_HEADER)
if(GDK4_VERSION_HEADER)
file(STRINGS "${GDK4_VERSION_HEADER}" GDK4_MAJOR_VERSION REGEX "^#define GDK_MAJOR_VERSION +\\(?([0-9]+)\\)?$")
string(REGEX REPLACE "^#define GDK_MAJOR_VERSION \\(?([0-9]+)\\)?$" "\\1" GDK4_MAJOR_VERSION "${GDK4_MAJOR_VERSION}")
file(STRINGS "${GDK4_VERSION_HEADER}" GDK4_MINOR_VERSION REGEX "^#define GDK_MINOR_VERSION +\\(?([0-9]+)\\)?$")
string(REGEX REPLACE "^#define GDK_MINOR_VERSION \\(?([0-9]+)\\)?$" "\\1" GDK4_MINOR_VERSION "${GDK4_MINOR_VERSION}")
file(STRINGS "${GDK4_VERSION_HEADER}" GDK4_MICRO_VERSION REGEX "^#define GDK_MICRO_VERSION +\\(?([0-9]+)\\)?$")
string(REGEX REPLACE "^#define GDK_MICRO_VERSION \\(?([0-9]+)\\)?$" "\\1" GDK4_MICRO_VERSION "${GDK4_MICRO_VERSION}")
set(GDK4_VERSION "${GDK4_MAJOR_VERSION}.${GDK4_MINOR_VERSION}.${GDK4_MICRO_VERSION}")
unset(GDK4_MAJOR_VERSION)
unset(GDK4_MINOR_VERSION)
unset(GDK4_MICRO_VERSION)
endif()
endif()
if (GDK4_FOUND)
find_file(GDK4_WITH_X11 "gdk/gdkx.h" HINTS ${GDK4_INCLUDE_DIRS})
if (GDK4_WITH_X11)
set(GDK4_WITH_X11 yes CACHE INTERNAL "Does GDK4 support X11")
endif (GDK4_WITH_X11)
endif ()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GDK4
REQUIRED_VARS GDK4_LIBRARY
VERSION_VAR GDK4_VERSION)

30
cmake/FindGTK4.cmake Normal file
View file

@ -0,0 +1,30 @@
include(PkgConfigWithFallback)
find_pkg_config_with_fallback(GTK4
PKG_CONFIG_NAME gtk4
LIB_NAMES gtk-4
INCLUDE_NAMES gtk/gtk.h
INCLUDE_DIR_SUFFIXES gtk-4.0 gtk-4.0/include gtk+-4.0 gtk+-4.0/include gtk4 gtk4/include
)
if(GTK4_FOUND AND NOT GTK4_VERSION)
find_file(GTK4_VERSION_HEADER "gtk/gtkversion.h" HINTS ${GTK4_INCLUDE_DIRS})
mark_as_advanced(GTK4_VERSION_HEADER)
if(GTK4_VERSION_HEADER)
file(STRINGS "${GTK4_VERSION_HEADER}" GTK4_MAJOR_VERSION REGEX "^#define GTK_MAJOR_VERSION +\\(?([0-9]+)\\)?$")
string(REGEX REPLACE "^#define GTK_MAJOR_VERSION \\(?([0-9]+)\\)?$" "\\1" GTK4_MAJOR_VERSION "${GTK4_MAJOR_VERSION}")
file(STRINGS "${GTK4_VERSION_HEADER}" GTK4_MINOR_VERSION REGEX "^#define GTK_MINOR_VERSION +\\(?([0-9]+)\\)?$")
string(REGEX REPLACE "^#define GTK_MINOR_VERSION \\(?([0-9]+)\\)?$" "\\1" GTK4_MINOR_VERSION "${GTK4_MINOR_VERSION}")
file(STRINGS "${GTK4_VERSION_HEADER}" GTK4_MICRO_VERSION REGEX "^#define GTK_MICRO_VERSION +\\(?([0-9]+)\\)?$")
string(REGEX REPLACE "^#define GTK_MICRO_VERSION \\(?([0-9]+)\\)?$" "\\1" GTK4_MICRO_VERSION "${GTK4_MICRO_VERSION}")
set(GTK4_VERSION "${GTK4_MAJOR_VERSION}.${GTK4_MINOR_VERSION}.${GTK4_MICRO_VERSION}")
unset(GTK4_MAJOR_VERSION)
unset(GTK4_MINOR_VERSION)
unset(GTK4_MICRO_VERSION)
endif()
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GTK4
REQUIRED_VARS GTK4_LIBRARY
VERSION_VAR GTK4_VERSION)

View file

@ -4,7 +4,7 @@ find_pkg_config_with_fallback(Gspell
LIB_NAMES gspell-1 LIB_NAMES gspell-1
INCLUDE_NAMES gspell.h INCLUDE_NAMES gspell.h
INCLUDE_DIR_SUFFIXES gspell-1 gspell-1/gspell INCLUDE_DIR_SUFFIXES gspell-1 gspell-1/gspell
DEPENDS Gtk DEPENDS GTK3
) )
include(FindPackageHandleStandardArgs) include(FindPackageHandleStandardArgs)

View file

@ -1,12 +1,16 @@
namespace Dino.Entities { namespace Dino.Entities {
public enum Encryption { public enum Encryption {
NONE, NONE,
PGP, PGP,
OMEMO, OMEMO,
DTLS_SRTP, DTLS_SRTP,
SRTP, SRTP,
UNKNOWN, UNKNOWN;
}
public bool is_some() {
return this != NONE;
}
}
} }

View file

@ -12,7 +12,8 @@ public enum Priority {
} }
public enum WidgetType { public enum WidgetType {
GTK GTK3,
GTK4
} }
public interface RootInterface : Object { public interface RootInterface : Object {
@ -27,6 +28,8 @@ public interface EncryptionListEntry : Object {
public abstract void encryption_activated(Entities.Conversation conversation, Plugins.SetInputFieldStatus callback); public abstract void encryption_activated(Entities.Conversation conversation, Plugins.SetInputFieldStatus callback);
public abstract Object? get_encryption_icon(Entities.Conversation conversation, ContentItem content_item); public abstract Object? get_encryption_icon(Entities.Conversation conversation, ContentItem content_item);
public abstract string? get_encryption_icon_name(Entities.Conversation conversation, ContentItem content_item);
} }
public interface CallEncryptionEntry : Object { public interface CallEncryptionEntry : Object {
@ -45,15 +48,11 @@ public abstract class AccountSettingsEntry : Object {
public abstract string name { get; } public abstract string name { get; }
public virtual int16 label_top_padding { get { return -1; } } public virtual int16 label_top_padding { get { return -1; } }
public abstract AccountSettingsWidget? get_widget(WidgetType type);
}
public interface AccountSettingsWidget : Object {
public abstract void set_account(Account account);
public abstract signal void activated(); public abstract signal void activated();
public abstract void deactivate(); public abstract void deactivate();
public abstract void set_account(Account account);
public abstract Object? get_widget(WidgetType type);
} }
public interface ContactDetailsProvider : Object { public interface ContactDetailsProvider : Object {
@ -76,10 +75,8 @@ public interface TextCommand : Object {
public interface ConversationTitlebarEntry : Object { public interface ConversationTitlebarEntry : Object {
public abstract string id { get; } public abstract string id { get; }
public abstract double order { get; } public abstract double order { get; }
public abstract ConversationTitlebarWidget? get_widget(WidgetType type); public abstract Object? get_widget(WidgetType type);
}
public interface ConversationTitlebarWidget : Object {
public abstract void set_conversation(Conversation conversation); public abstract void set_conversation(Conversation conversation);
public abstract void unset_conversation(); public abstract void unset_conversation();
} }
@ -146,10 +143,14 @@ public abstract class MetaConversationItem : Object {
public bool requires_header { get; set; default=false; } public bool requires_header { get; set; default=false; }
public bool in_edit_mode { get; set; default=false; } public bool in_edit_mode { get; set; default=false; }
public abstract Object? get_widget(WidgetType type); public abstract Object? get_widget(ConversationItemWidgetInterface outer, WidgetType type);
public abstract Gee.List<MessageAction>? get_item_actions(WidgetType type); public abstract Gee.List<MessageAction>? get_item_actions(WidgetType type);
} }
public interface ConversationItemWidgetInterface: Object {
public abstract void set_widget(Object object, WidgetType type);
}
public delegate void MessageActionEvoked(Object button, Plugins.MetaConversationItem evoked_on, Object widget); public delegate void MessageActionEvoked(Object button, Plugins.MetaConversationItem evoked_on, Object widget);
public class MessageAction : Object { public class MessageAction : Object {
public string icon_name; public string icon_name;

View file

@ -44,37 +44,11 @@ public class ContentItemStore : StreamInteractionModule, Object {
Gee.TreeSet<ContentItem> items = new Gee.TreeSet<ContentItem>(ContentItem.compare_func); Gee.TreeSet<ContentItem> items = new Gee.TreeSet<ContentItem>(ContentItem.compare_func);
foreach (var row in select) { foreach (var row in select) {
int provider = row[db.content_item.content_type]; int id = row[db.content_item.id];
int content_type = row[db.content_item.content_type];
int foreign_id = row[db.content_item.foreign_id]; int foreign_id = row[db.content_item.foreign_id];
DateTime time = new DateTime.from_unix_utc(row[db.content_item.time]); DateTime time = new DateTime.from_unix_utc(row[db.content_item.time]);
switch (provider) { items.add(get_item(conversation, id, content_type, foreign_id, time));
case 1:
Message? message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_id(foreign_id, conversation);
if (message != null) {
var message_item = new MessageItem(message, conversation, row[db.content_item.id]);
message_item.time = time; // In case of message corrections, the original time should be used
items.add(message_item);
}
break;
case 2:
FileTransfer? file_transfer = stream_interactor.get_module(FileTransferStorage.IDENTITY).get_file_by_id(foreign_id, conversation);
if (file_transfer != null) {
Message? message = null;
if (file_transfer.provider == 0 && file_transfer.info != null) {
message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_id(int.parse(file_transfer.info), conversation);
}
var file_item = new FileItem(file_transfer, conversation, row[db.content_item.id], message);
items.add(file_item);
}
break;
case 3:
Call? call = stream_interactor.get_module(CallStore.IDENTITY).get_call_by_id(foreign_id, conversation);
if (call != null) {
var call_item = new CallItem(call, conversation, row[db.content_item.id]);
items.add(call_item);
}
break;
}
} }
Gee.List<ContentItem> ret = new ArrayList<ContentItem>(); Gee.List<ContentItem> ret = new ArrayList<ContentItem>();
@ -84,7 +58,42 @@ public class ContentItemStore : StreamInteractionModule, Object {
return ret; return ret;
} }
public ContentItem? get_item(Conversation conversation, int type, int foreign_id) { public ContentItem get_item(Conversation conversation, int id, int content_type, int foreign_id, DateTime time) throws Error {
switch (content_type) {
case 1:
Message? message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_id(foreign_id, conversation);
if (message != null) {
var message_item = new MessageItem(message, conversation, id);
message_item.time = time; // In case of message corrections, the original time should be used
return message_item;
}
break;
case 2:
FileTransfer? file_transfer = stream_interactor.get_module(FileTransferStorage.IDENTITY).get_file_by_id(foreign_id, conversation);
if (file_transfer != null) {
Message? message = null;
if (file_transfer.provider == 0 && file_transfer.info != null) {
message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_id(int.parse(file_transfer.info), conversation);
}
var file_item = new FileItem(file_transfer, conversation, id, message);
return file_item;
}
break;
case 3:
Call? call = stream_interactor.get_module(CallStore.IDENTITY).get_call_by_id(foreign_id, conversation);
if (call != null) {
var call_item = new CallItem(call, conversation, id);
return call_item;
}
break;
default:
warning("Unknown content item type: %i", content_type);
break;
}
throw new Error(-1, 0, "Bad content type %i or non existing content item %i", content_type, foreign_id);
}
public ContentItem? get_item_by_foreign(Conversation conversation, int type, int foreign_id) {
QueryBuilder select = db.content_item.select() QueryBuilder select = db.content_item.select()
.with(db.content_item.content_type, "=", type) .with(db.content_item.content_type, "=", type)
.with(db.content_item.foreign_id, "=", foreign_id); .with(db.content_item.foreign_id, "=", foreign_id);
@ -122,6 +131,26 @@ public class ContentItemStore : StreamInteractionModule, Object {
return get_items_from_query(select, conversation); return get_items_from_query(select, conversation);
} }
// public Gee.List<ContentItemMeta> get_latest_meta(Conversation conversation, int count) {
// QueryBuilder select = db.content_item.select()
// .with(db.content_item.conversation_id, "=", conversation.id)
// .with(db.content_item.hide, "=", false)
// .order_by(db.content_item.time, "DESC")
// .order_by(db.content_item.id, "DESC")
// .limit(count);
//
// var ret = new ArrayList<ContentItemMeta>();
// foreach (var row in select) {
// var item_meta = new ContentItemMeta() {
// id = row[db.content_item.id],
// content_type = row[db.content_item.content_type],
// foreign_id = row[db.content_item.foreign_id],
// time = new DateTime.from_unix_utc(row[db.content_item.time])
// };
// }
// return ret;
// }
public Gee.List<ContentItem> get_before(Conversation conversation, ContentItem item, int count) { public Gee.List<ContentItem> get_before(Conversation conversation, ContentItem item, int count) {
long time = (long) item.time.to_unix(); long time = (long) item.time.to_unix();
QueryBuilder select = db.content_item.select() QueryBuilder select = db.content_item.select()

View file

@ -154,7 +154,7 @@ public class CounterpartInteractionManager : StreamInteractionModule, Object {
conversation.read_up_to = message; conversation.read_up_to = message;
// TODO: This only marks messages as read, not http file transfers. // TODO: This only marks messages as read, not http file transfers.
ContentItem? content_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_item(conversation, 1, message.id); ContentItem? content_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_item_by_foreign(conversation, 1, message.id);
if (content_item == null) return; if (content_item == null) return;
ContentItem? read_up_to_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_item_by_id(conversation, conversation.read_up_to_item); ContentItem? read_up_to_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_item_by_id(conversation, conversation.read_up_to_item);
if (read_up_to_item != null && read_up_to_item.compare(content_item) > 0) return; if (read_up_to_item != null && read_up_to_item.compare(content_item) > 0) return;

View file

@ -144,7 +144,7 @@ public class MessageCorrection : StreamInteractionModule, MessageListener {
} }
private void on_received_correction(Conversation conversation, int message_id) { private void on_received_correction(Conversation conversation, int message_id) {
ContentItem? content_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_item(conversation, 1, message_id); ContentItem? content_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_item_by_foreign(conversation, 1, message_id);
if (content_item != null) { if (content_item != null) {
received_correction(content_item); received_correction(content_item);
} }

View file

@ -8,48 +8,50 @@ find_packages(MAIN_PACKAGES REQUIRED
GLib GLib
GModule GModule
GObject GObject
GTK3 GTK4
ICU ICU
Gspell
) )
set(RESOURCE_LIST set(RESOURCE_LIST
icons/dino-account-plus-symbolic.svg icons/dino-account-plus-symbolic.svg
icons/dino-changes-allowed-symbolic.svg
icons/dino-changes-prevent-symbolic.svg
icons/dino-conversation-list-placeholder-arrow.svg icons/dino-conversation-list-placeholder-arrow.svg
icons/dino-double-tick-symbolic.svg
icons/dino-emoticon-symbolic.svg
icons/dino-qr-code-symbolic.svg icons/dino-qr-code-symbolic.svg
icons/dino-security-high-symbolic.svg
icons/dino-microphone-off-symbolic.svg
icons/dino-microphone-symbolic.svg
icons/dino-party-popper-symbolic.svg icons/dino-party-popper-symbolic.svg
icons/dino-phone-hangup-symbolic.svg
icons/dino-phone-in-talk-symbolic.svg
icons/dino-phone-missed-symbolic.svg
icons/dino-phone-ring-symbolic.svg
icons/dino-phone-symbolic.svg
icons/dino-status-away.svg icons/dino-status-away.svg
icons/dino-status-chat.svg icons/dino-status-chat.svg
icons/dino-status-dnd.svg icons/dino-status-dnd.svg
icons/dino-status-online.svg icons/dino-status-online.svg
icons/im.dino.Dino.svg icons/im.dino.Dino.svg
icons/im.dino.Dino-symbolic.svg
icons/dino-tick-symbolic.svg
icons/dino-video-off-symbolic.svg
icons/dino-video-symbolic.svg
icons/dino-device-desktop-symbolic.svg icons/scalable/apps/im.dino.Dino-symbolic.svg
icons/dino-device-phone-symbolic.svg
icons/dino-file-document-symbolic.svg icons/scalable/devices/dino-device-desktop-symbolic.svg
icons/dino-file-download-symbolic.svg icons/scalable/devices/dino-device-phone-symbolic.svg
icons/dino-file-image-symbolic.svg icons/scalable/devices/dino-phone-hangup-symbolic.svg
icons/dino-file-music-symbolic.svg icons/scalable/devices/dino-phone-in-talk-symbolic.svg
icons/dino-file-symbolic.svg icons/scalable/devices/dino-phone-missed-symbolic.svg
icons/dino-file-table-symbolic.svg icons/scalable/devices/dino-phone-ring-symbolic.svg
icons/dino-file-video-symbolic.svg icons/scalable/devices/dino-phone-symbolic.svg
icons/scalable/emotes/dino-emoticon-symbolic.svg
icons/scalable/mimetypes/dino-file-document-symbolic.svg
icons/scalable/mimetypes/dino-file-download-symbolic.svg
icons/scalable/mimetypes/dino-file-image-symbolic.svg
icons/scalable/mimetypes/dino-file-music-symbolic.svg
icons/scalable/mimetypes/dino-file-symbolic.svg
icons/scalable/mimetypes/dino-file-table-symbolic.svg
icons/scalable/mimetypes/dino-file-video-symbolic.svg
icons/scalable/status/dino-changes-allowed-symbolic.svg
icons/scalable/status/dino-changes-prevent-symbolic.svg
icons/scalable/status/dino-double-tick-symbolic.svg
icons/scalable/status/dino-microphone-off-symbolic.svg
icons/scalable/status/dino-microphone-symbolic.svg
icons/scalable/status/dino-security-high-symbolic.svg
icons/scalable/status/dino-tick-symbolic.svg
icons/scalable/status/dino-video-off-symbolic.svg
icons/scalable/status/dino-video-symbolic.svg
add_conversation/add_contact_dialog.ui add_conversation/add_contact_dialog.ui
add_conversation/add_groupchat_dialog.ui add_conversation/add_groupchat_dialog.ui
@ -60,13 +62,13 @@ set(RESOURCE_LIST
call_widget.ui call_widget.ui
chat_input.ui chat_input.ui
contact_details_dialog.ui contact_details_dialog.ui
conversation_item_widget.ui
conversation_list_titlebar.ui conversation_list_titlebar.ui
conversation_list_titlebar_csd.ui conversation_list_titlebar_csd.ui
conversation_row.ui conversation_row.ui
conversation_view.ui conversation_view.ui
file_default_widget.ui file_default_widget.ui
file_send_overlay.ui file_send_overlay.ui
emojichooser.ui
global_search.ui global_search.ui
conversation_content_view/item_metadata_header.ui conversation_content_view/item_metadata_header.ui
conversation_content_view/view.ui conversation_content_view/view.ui
@ -101,11 +103,11 @@ compile_gresources(
unset(MAIN_EXTRA_OPTIONS) unset(MAIN_EXTRA_OPTIONS)
unset(MAIN_EXTRA_PACKAGES) unset(MAIN_EXTRA_PACKAGES)
find_package(GDK3) # find_package(GDK3)
if(GDK3_WITH_X11) # if(GDK3_WITH_X11)
set(MAIN_EXTRA_OPTIONS ${MAIN_EXTRA_OPTIONS} -D GDK3_WITH_X11) # set(MAIN_EXTRA_OPTIONS ${MAIN_EXTRA_OPTIONS} -D GDK3_WITH_X11)
set(MAIN_EXTRA_PACKAGES ${MAIN_EXTRA_PACKAGES} gdk-x11-3.0) # set(MAIN_EXTRA_PACKAGES ${MAIN_EXTRA_PACKAGES} gdk-x11-3.0)
endif(GDK3_WITH_X11) # endif(GDK3_WITH_X11)
set(MAIN_EXTRA_OPTIONS ${MAIN_EXTRA_OPTIONS} --vapidir=${CMAKE_CURRENT_SOURCE_DIR}/vapi) set(MAIN_EXTRA_OPTIONS ${MAIN_EXTRA_OPTIONS} --vapidir=${CMAKE_CURRENT_SOURCE_DIR}/vapi)
vala_precompile(MAIN_VALA_C vala_precompile(MAIN_VALA_C
@ -145,24 +147,6 @@ SOURCES
src/ui/call_window/participant_widget.vala src/ui/call_window/participant_widget.vala
src/ui/call_window/video_settings_popover.vala src/ui/call_window/video_settings_popover.vala
src/ui/chat_input/chat_input_controller.vala
src/ui/chat_input/chat_text_view.vala
src/ui/chat_input/edit_history.vala
src/ui/chat_input/encryption_button.vala
src/ui/chat_input/occupants_tab_completer.vala
src/ui/chat_input/smiley_converter.vala
src/ui/chat_input/spell_checker.vala
src/ui/chat_input/view.vala
src/ui/contact_details/blocking_provider.vala
src/ui/contact_details/settings_provider.vala
src/ui/contact_details/permissions_provider.vala
src/ui/contact_details/dialog.vala
src/ui/contact_details/muc_config_form_provider.vala
src/ui/conversation_selector/conversation_selector_row.vala
src/ui/conversation_selector/conversation_selector.vala
src/ui/conversation_content_view/call_widget.vala src/ui/conversation_content_view/call_widget.vala
src/ui/conversation_content_view/chat_state_populator.vala src/ui/conversation_content_view/chat_state_populator.vala
src/ui/conversation_content_view/content_populator.vala src/ui/conversation_content_view/content_populator.vala
@ -175,6 +159,22 @@ SOURCES
src/ui/conversation_content_view/message_widget.vala src/ui/conversation_content_view/message_widget.vala
src/ui/conversation_content_view/subscription_notification.vala src/ui/conversation_content_view/subscription_notification.vala
src/ui/chat_input/chat_input_controller.vala
src/ui/chat_input/chat_text_view.vala
src/ui/chat_input/encryption_button.vala
src/ui/chat_input/occupants_tab_completer.vala
src/ui/chat_input/smiley_converter.vala
src/ui/chat_input/view.vala
src/ui/contact_details/blocking_provider.vala
src/ui/contact_details/settings_provider.vala
src/ui/contact_details/permissions_provider.vala
src/ui/contact_details/dialog.vala
src/ui/contact_details/muc_config_form_provider.vala
src/ui/conversation_selector/conversation_selector.vala
src/ui/conversation_selector/conversation_selector_row.vala
src/ui/conversation_titlebar/call_entry.vala src/ui/conversation_titlebar/call_entry.vala
src/ui/conversation_titlebar/menu_entry.vala src/ui/conversation_titlebar/menu_entry.vala
src/ui/conversation_titlebar/occupants_entry.vala src/ui/conversation_titlebar/occupants_entry.vala
@ -197,12 +197,10 @@ SOURCES
src/ui/util/sizing_bin.vala src/ui/util/sizing_bin.vala
src/ui/util/size_request_box.vala src/ui/util/size_request_box.vala
src/ui/util/scaling_image.vala src/ui/util/scaling_image.vala
src/ui/util/preview_file_chooser_native.vala
CUSTOM_VAPIS CUSTOM_VAPIS
${CMAKE_BINARY_DIR}/exports/xmpp-vala.vapi ${CMAKE_BINARY_DIR}/exports/xmpp-vala.vapi
${CMAKE_BINARY_DIR}/exports/qlite.vapi ${CMAKE_BINARY_DIR}/exports/qlite.vapi
${CMAKE_BINARY_DIR}/exports/dino_internal.vapi ${CMAKE_BINARY_DIR}/exports/dino_internal.vapi
vapi/emojichooser.vapi
PACKAGES PACKAGES
${MAIN_PACKAGES} ${MAIN_PACKAGES}
${MAIN_EXTRA_PACKAGES} ${MAIN_EXTRA_PACKAGES}
@ -213,7 +211,7 @@ OPTIONS
) )
add_definitions(${VALA_CFLAGS} -DGETTEXT_PACKAGE=\"${GETTEXT_PACKAGE}\" -DLOCALE_INSTALL_DIR=\"${LOCALE_INSTALL_DIR}\") add_definitions(${VALA_CFLAGS} -DGETTEXT_PACKAGE=\"${GETTEXT_PACKAGE}\" -DLOCALE_INSTALL_DIR=\"${LOCALE_INSTALL_DIR}\")
add_executable(dino ${MAIN_VALA_C} ${MAIN_GRESOURCES_TARGET} src/emojichooser.c) add_executable(dino ${MAIN_VALA_C} ${MAIN_GRESOURCES_TARGET})
add_dependencies(dino ${GETTEXT_PACKAGE}-translations) add_dependencies(dino ${GETTEXT_PACKAGE}-translations)
target_include_directories(dino PRIVATE src) target_include_directories(dino PRIVATE src)
target_link_libraries(dino libdino ${MAIN_PACKAGES}) target_link_libraries(dino libdino ${MAIN_PACKAGES})

View file

@ -1,128 +1,102 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<interface> <interface>
<requires lib="gtk" version="4.0"/>
<template class="DinoUiAddContactDialog"> <template class="DinoUiAddContactDialog">
<property name="title" translatable="1">Add Contact</property>
<property name="valign">center</property> <property name="valign">center</property>
<property name="modal">True</property> <property name="modal">True</property>
<child type="titlebar"> <child type="titlebar">
<object class="GtkHeaderBar"> <object class="GtkHeaderBar">
<property name="title" translatable="yes">Add Contact</property> <property name="show-title-buttons">False</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkButton" id="cancel_button"> <object class="GtkButton" id="cancel_button">
<property name="label" translatable="yes">Cancel</property> <property name="label" translatable="1">Cancel</property>
<property name="sensitive">True</property>
<property name="visible">True</property>
</object> </object>
<packing>
<property name="pack_type">start</property>
</packing>
</child> </child>
<child> <child type="end">
<object class="GtkButton" id="ok_button"> <object class="GtkButton" id="ok_button">
<property name="has_default">True</property> <property name="label" translatable="1">Add</property>
<property name="can_default">True</property> <property name="sensitive">0</property>
<property name="label" translatable="yes">Add</property>
<property name="sensitive">False</property>
<property name="visible">True</property>
<style> <style>
<class name="suggested-action"/> <class name="suggested-action"/>
</style> </style>
</object> </object>
<packing>
<property name="pack_type">end</property>
</packing>
</child> </child>
</object> </object>
</child> </child>
<child internal-child="vbox"> <child internal-child="content_area">
<object class="GtkBox"> <object class="GtkBox">
<property name="visible">True</property>
<child> <child>
<object class="GtkGrid" id="info_grid"> <object class="GtkGrid" id="info_grid">
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<property name="margin">20</property> <property name="margin-start">20</property>
<property name="margin-end">20</property>
<property name="margin-top">20</property>
<property name="margin-bottom">20</property>
<property name="row-spacing">7</property> <property name="row-spacing">7</property>
<property name="column-spacing">10</property> <property name="column-spacing">10</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkLabel"> <object class="GtkLabel">
<property name="label" translatable="yes">Account</property> <property name="label" translatable="1">Account</property>
<property name="xalign">1</property> <property name="xalign">1</property>
<property name="visible">True</property> <layout>
<property name="column">0</property>
<property name="row">0</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child> </child>
<child> <child>
<object class="DinoUiAccountComboBox" id="account_combobox"> <object class="DinoUiAccountComboBox" id="account_combobox">
<property name="hexpand">True</property> <property name="hexpand">True</property>
<property name="width_request">200</property> <property name="width_request">200</property>
<property name="visible">True</property> <property name="visible">True</property>
<layout>
<property name="column">1</property>
<property name="row">0</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkLabel"> <object class="GtkLabel">
<property name="label">JID</property> <property name="label">JID</property>
<property name="xalign">1</property> <property name="xalign">1</property>
<property name="visible">True</property> <layout>
<property name="column">0</property>
<property name="row">1</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkEntry" id="jid_entry"> <object class="GtkEntry" id="jid_entry">
<property name="activates_default">True</property> <property name="activates_default">1</property>
<property name="hexpand">True</property> <property name="hexpand">1</property>
<property name="width_request">200</property> <property name="width_request">200</property>
<property name="visible">True</property> <layout>
<property name="column">1</property>
<property name="row">1</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkLabel"> <object class="GtkLabel">
<property name="label" translatable="yes">Alias</property> <property name="label" translatable="1">Alias</property>
<property name="xalign">1</property> <property name="xalign">1</property>
<property name="visible">True</property> <layout>
<property name="column">0</property>
<property name="row">2</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkEntry" id="alias_entry"> <object class="GtkEntry" id="alias_entry">
<property name="activates_default">True</property> <property name="activates_default">1</property>
<property name="hexpand">True</property> <property name="hexpand">1</property>
<property name="width_request">200</property> <property name="width_request">200</property>
<property name="visible">True</property> <layout>
<property name="column">1</property>
<property name="row">2</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">2</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child> </child>
</object> </object>
</child> </child>

View file

@ -1,201 +1,165 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<interface> <interface>
<requires lib="gtk" version="4.0"/>
<template class="DinoUiAddGroupchatDialog"> <template class="DinoUiAddGroupchatDialog">
<property name="valign">center</property> <property name="valign">center</property>
<property name="modal">True</property> <property name="modal">True</property>
<child type="titlebar"> <child type="titlebar">
<object class="GtkHeaderBar"> <object class="GtkHeaderBar">
<property name="visible">True</property> <property name="show-title-buttons">False</property>
<child> <child>
<object class="GtkButton" id="cancel_button"> <object class="GtkButton" id="cancel_button">
<property name="label" translatable="yes">Cancel</property> <property name="label" translatable="1">Cancel</property>
<property name="sensitive">True</property>
<property name="visible">True</property>
</object> </object>
<packing>
<property name="pack_type">start</property>
</packing>
</child> </child>
<child> <child type="end">
<object class="GtkButton" id="ok_button"> <object class="GtkButton" id="ok_button">
<property name="can_default">True</property> <property name="sensitive">0</property>
<property name="has_default">True</property>
<property name="sensitive">False</property>
<property name="visible">True</property>
<style> <style>
<class name="suggested-action"/> <class name="suggested-action"/>
</style> </style>
</object> </object>
<packing>
<property name="pack_type">end</property>
</packing>
</child> </child>
</object> </object>
</child> </child>
<child internal-child="vbox"> <child internal-child="content_area">
<object class="GtkBox" id="main"> <object class="GtkBox" id="main">
<property name="visible">True</property>
<child> <child>
<object class="GtkGrid"> <object class="GtkGrid">
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<property name="margin">20</property> <property name="margin-start">20</property>
<property name="margin-end">20</property>
<property name="margin-top">20</property>
<property name="margin-bottom">20</property>
<property name="margin-start">40</property> <property name="margin-start">40</property>
<property name="margin-end">40</property> <property name="margin-end">40</property>
<property name="row-spacing">7</property> <property name="row-spacing">7</property>
<property name="column-spacing">10</property> <property name="column-spacing">10</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkLabel"> <object class="GtkLabel">
<property name="label" translatable="yes">Account</property> <property name="label" translatable="1">Account</property>
<property name="xalign">1</property> <property name="xalign">1</property>
<property name="visible">True</property> <layout>
<property name="column">0</property>
<property name="row">0</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkStack" id="accounts_stack"> <object class="GtkStack" id="accounts_stack">
<property name="visible">True</property>
<child> <child>
<object class="DinoUiAccountComboBox" id="account_combobox"> <object class="GtkStackPage">
<property name="hexpand">True</property>
<property name="width_request">200</property>
<property name="visible">True</property>
</object>
<packing>
<property name="name">combobox</property> <property name="name">combobox</property>
</packing> <property name="child">
<object class="DinoUiAccountComboBox" id="account_combobox">
<property name="hexpand">True</property>
<property name="width_request">200</property>
<property name="visible">True</property>
</object>
</property>
</object>
</child> </child>
<child> <child>
<object class="GtkLabel" id="account_label"> <object class="GtkStackPage">
<property name="xalign">0</property>
<property name="can_focus">True</property>
<property name="visible">True</property>
</object>
<packing>
<property name="name">label</property> <property name="name">label</property>
</packing> <property name="child">
<object class="GtkLabel" id="account_label">
<property name="xalign">0</property>
<property name="focusable">1</property>
</object>
</property>
</object>
</child> </child>
<layout>
<property name="column">1</property>
<property name="row">0</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkLabel"> <object class="GtkLabel">
<property name="label">JID</property> <property name="label">JID</property>
<property name="xalign">1</property> <property name="xalign">1</property>
<property name="visible">True</property> <layout>
<property name="column">0</property>
<property name="row">1</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkEntry" id="jid_entry"> <object class="GtkEntry" id="jid_entry">
<property name="activates_default">True</property> <property name="activates_default">1</property>
<property name="hexpand">True</property> <property name="hexpand">1</property>
<property name="width_request">200</property> <property name="width_request">200</property>
<property name="visible">True</property> <layout>
<property name="column">1</property>
<property name="row">1</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkLabel"> <object class="GtkLabel">
<property name="label" translatable="yes">Nick</property> <property name="label" translatable="1">Nick</property>
<property name="xalign">1</property> <property name="xalign">1</property>
<property name="visible">True</property> <layout>
<property name="column">0</property>
<property name="row">2</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkEntry" id="nick_entry"> <object class="GtkEntry" id="nick_entry">
<property name="activates_default">True</property> <property name="activates_default">1</property>
<property name="hexpand">True</property> <property name="hexpand">1</property>
<property name="width_request">200</property> <property name="width_request">200</property>
<property name="visible">True</property> <layout>
<property name="column">1</property>
<property name="row">2</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">2</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkLabel"> <object class="GtkLabel">
<property name="label" translatable="yes">Password</property> <property name="label" translatable="1">Password</property>
<property name="xalign">1</property> <property name="xalign">1</property>
<property name="visible">True</property> <layout>
<property name="column">0</property>
<property name="row">3</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">3</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkEntry" id="password_entry"> <object class="GtkEntry" id="password_entry">
<property name="activates_default">True</property> <property name="activates_default">1</property>
<property name="visibility">False</property> <property name="visibility">0</property>
<property name="hexpand">True</property> <property name="hexpand">1</property>
<property name="width_request">200</property> <property name="width_request">200</property>
<property name="visible">True</property> <layout>
<property name="column">1</property>
<property name="row">3</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">3</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkLabel" id="alias_label"> <object class="GtkLabel" id="alias_label">
<property name="label" translatable="yes">Alias</property> <property name="label" translatable="1">Alias</property>
<property name="xalign">1</property> <property name="xalign">1</property>
<property name="visible">True</property> <layout>
<property name="column">0</property>
<property name="row">5</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">5</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkEntry" id="alias_entry"> <object class="GtkEntry" id="alias_entry">
<property name="activates_default">True</property> <property name="activates_default">1</property>
<property name="hexpand">True</property> <property name="hexpand">1</property>
<property name="width_request">200</property> <property name="width_request">200</property>
<property name="visible">True</property> <layout>
<property name="column">1</property>
<property name="row">5</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">5</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child> </child>
</object> </object>
</child> </child>

View file

@ -1,272 +1,238 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<interface> <interface>
<requires lib="gtk" version="4.0"/>
<template class="DinoUiConferenceDetailsFragment"> <template class="DinoUiConferenceDetailsFragment">
<property name="visible">True</property> <property name="visible">True</property>
<child> <child>
<object class="GtkOverlay"> <object class="GtkOverlay">
<property name="visible">True</property> <property name="child">
<child>
<object class="GtkGrid"> <object class="GtkGrid">
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<property name="margin">20</property> <property name="margin-start">20</property>
<property name="margin-end">20</property>
<property name="margin-top">20</property>
<property name="margin-bottom">20</property>
<property name="margin_end">40</property> <property name="margin_end">40</property>
<property name="margin_start">40</property> <property name="margin_start">40</property>
<property name="row-spacing">7</property> <property name="row-spacing">7</property>
<property name="column-spacing">10</property> <property name="column-spacing">10</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkLabel"> <object class="GtkLabel">
<property name="label" translatable="yes">Account</property> <property name="label" translatable="1">Account</property>
<property name="xalign">1</property> <property name="xalign">1</property>
<property name="visible">True</property>
<style> <style>
<class name="dim-label"/> <class name="dim-label"/>
</style> </style>
<layout>
<property name="column">0</property>
<property name="row">0</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkStack" id="accounts_stack"> <object class="GtkStack" id="accounts_stack">
<property name="visible">True</property>
<child> <child>
<object class="GtkButton" id="accounts_button"> <object class="GtkStackPage">
<property name="relief">none</property> <property name="name">label</property>
<property name="visible">True</property> <property name="child">
<child> <object class="GtkButton" id="accounts_button">
<object class="GtkLabel" id="accounts_label"> <property name="has-frame">False</property>
<property name="xalign">0</property> <child>
<object class="GtkLabel" id="accounts_label">
<property name="xalign">0</property>
</object>
</child>
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">entry</property>
<property name="child">
<object class="DinoUiAccountComboBox" id="account_combobox">
<property name="hexpand">True</property>
<property name="width_request">200</property>
<property name="visible">True</property> <property name="visible">True</property>
</object> </object>
</child> </property>
</object> </object>
<packing>
<property name="name">label</property>
</packing>
</child>
<child>
<object class="DinoUiAccountComboBox" id="account_combobox">
<property name="hexpand">True</property>
<property name="width_request">200</property>
<property name="visible">True</property>
</object>
<packing>
<property name="name">entry</property>
</packing>
</child> </child>
<layout>
<property name="column">1</property>
<property name="row">0</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkLabel"> <object class="GtkLabel">
<property name="label">JID</property> <property name="label">JID</property>
<property name="xalign">1</property> <property name="xalign">1</property>
<property name="visible">True</property>
<style> <style>
<class name="dim-label"/> <class name="dim-label"/>
</style> </style>
<layout>
<property name="column">0</property>
<property name="row">1</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkStack" id="jid_stack"> <object class="GtkStack" id="jid_stack">
<property name="visible">True</property>
<child> <child>
<object class="GtkButton" id="jid_button"> <object class="GtkStackPage">
<property name="relief">none</property>
<property name="visible">True</property>
<child>
<object class="GtkLabel" id="jid_label">
<property name="xalign">0</property>
<property name="visible">True</property>
</object>
</child>
</object>
<packing>
<property name="name">label</property> <property name="name">label</property>
</packing> <property name="child">
<object class="GtkButton" id="jid_button">
<property name="has-frame">False</property>
<child>
<object class="GtkLabel" id="jid_label">
<property name="xalign">0</property>
</object>
</child>
</object>
</property>
</object>
</child> </child>
<child> <child>
<object class="GtkEntry" id="jid_entry"> <object class="GtkStackPage">
<property name="hexpand">True</property>
<property name="width_request">200</property>
<property name="visible">True</property>
</object>
<packing>
<property name="name">entry</property> <property name="name">entry</property>
</packing> <property name="child">
<object class="GtkEntry" id="jid_entry">
<property name="hexpand">1</property>
<property name="width_request">200</property>
</object>
</property>
</object>
</child> </child>
<layout>
<property name="column">1</property>
<property name="row">1</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkLabel"> <object class="GtkLabel">
<property name="label" translatable="yes">Nick</property> <property name="label" translatable="1">Nick</property>
<property name="xalign">1</property> <property name="xalign">1</property>
<property name="visible">True</property>
<style> <style>
<class name="dim-label"/> <class name="dim-label"/>
</style> </style>
<layout>
<property name="column">0</property>
<property name="row">2</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkStack" id="nick_stack"> <object class="GtkStack" id="nick_stack">
<property name="visible">True</property>
<child> <child>
<object class="GtkButton" id="nick_button"> <object class="GtkStackPage">
<property name="relief">none</property>
<property name="visible">True</property>
<child>
<object class="GtkLabel" id="nick_label">
<property name="xalign">0</property>
<property name="visible">True</property>
</object>
</child>
</object>
<packing>
<property name="name">label</property> <property name="name">label</property>
</packing> <property name="child">
<object class="GtkButton" id="nick_button">
<property name="has-frame">False</property>
<child>
<object class="GtkLabel" id="nick_label">
<property name="xalign">0</property>
</object>
</child>
</object>
</property>
</object>
</child> </child>
<child> <child>
<object class="GtkEntry" id="nick_entry"> <object class="GtkStackPage">
<property name="hexpand">True</property>
<property name="width_request">200</property>
<property name="visible">True</property>
</object>
<packing>
<property name="name">entry</property> <property name="name">entry</property>
</packing> <property name="child">
<object class="GtkEntry" id="nick_entry">
<property name="hexpand">1</property>
<property name="width_request">200</property>
</object>
</property>
</object>
</child> </child>
<layout>
<property name="column">1</property>
<property name="row">2</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">2</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkLabel" id="password_text_label"> <object class="GtkLabel" id="password_text_label">
<property name="label" translatable="yes">Password</property> <property name="visible">0</property>
<property name="label" translatable="1">Password</property>
<property name="xalign">1</property> <property name="xalign">1</property>
<style> <style>
<class name="dim-label"/> <class name="dim-label"/>
</style> </style>
<layout>
<property name="column">0</property>
<property name="row">3</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">3</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkStack" id="password_stack"> <object class="GtkStack" id="password_stack">
<property name="visible">0</property>
<child> <child>
<object class="GtkButton" id="password_button"> <object class="GtkStackPage">
<property name="relief">none</property>
<property name="visible">True</property>
<child>
<object class="GtkLabel" id="password_label">
<property name="xalign">0</property>
<property name="visible">True</property>
</object>
</child>
</object>
<packing>
<property name="name">label</property> <property name="name">label</property>
</packing> <property name="child">
<object class="GtkButton" id="password_button">
<property name="has-frame">False</property>
<child>
<object class="GtkLabel" id="password_label">
<property name="xalign">0</property>
</object>
</child>
</object>
</property>
</object>
</child> </child>
<child> <child>
<object class="GtkEntry" id="password_entry"> <object class="GtkStackPage">
<property name="hexpand">True</property>
<property name="width_request">200</property>
<property name="visibility">False</property>
<property name="visible">True</property>
</object>
<packing>
<property name="name">entry</property> <property name="name">entry</property>
</packing> <property name="child">
<object class="GtkEntry" id="password_entry">
<property name="hexpand">1</property>
<property name="width_request">200</property>
<property name="visibility">0</property>
</object>
</property>
</object>
</child> </child>
<layout>
<property name="column">1</property>
<property name="row">3</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">3</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child> </child>
</object> </object>
<packing> </property>
<property name="index">-1</property>
</packing>
</child>
<child type="overlay"> <child type="overlay">
<object class="GtkRevealer" id="notification_revealer"> <object class="GtkRevealer" id="notification_revealer">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property> <property name="halign">center</property>
<property name="valign">start</property> <property name="valign">start</property>
<child> <property name="child">
<object class="GtkFrame" id="frame2"> <object class="GtkFrame" id="frame2">
<property name="visible">True</property> <!-- <property name="shadow_type">none</property>-->
<property name="can_focus">False</property>
<property name="label_xalign">0</property>
<property name="shadow_type">none</property>
<style> <style>
<class name="app-notification"/> <class name="app-notification"/>
</style> </style>
<child> <property name="child">
<object class="GtkBox" id="box2"> <object class="GtkBox" id="box2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">20</property> <property name="spacing">20</property>
<child> <child>
<object class="GtkLabel" id="notification_label"> <object class="GtkLabel" id="notification_label"/>
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkButton" id="notification_button"> <object class="GtkButton" id="notification_button">
<property name="visible">True</property> <property name="focusable">1</property>
<property name="can_focus">True</property> <property name="receives_default">1</property>
<property name="receives_default">True</property> <property name="has-frame">False</property>
<property name="relief">none</property>
<child> <child>
<object class="GtkImage" id="image2"> <object class="GtkImage" id="image2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">window-close-symbolic</property> <property name="icon_name">window-close-symbolic</property>
</object> </object>
</child> </child>
@ -274,19 +240,14 @@
<class name="image-button"/> <class name="image-button"/>
</style> </style>
</object> </object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child> </child>
</object> </object>
</child> </property>
<child type="label_item"> <child type="label_item">
<placeholder/> <placeholder/>
</child> </child>
</object> </object>
</child> </property>
</object> </object>
</child> </child>
</object> </object>

View file

@ -1,62 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<interface> <interface>
<template class="DinoUiListRow" parent="GtkListBoxRow"> <requires lib="gtk" version="4.0"/>
<property name="visible">True</property> <object class="GtkGrid" id="outer_grid">
<property name="margin-start">3</property>
<property name="margin-end">3</property>
<property name="margin-top">3</property>
<property name="margin-bottom">3</property>
<property name="column-spacing">10</property>
<child> <child>
<object class="GtkGrid" id="outer_grid"> <object class="DinoUiAvatarImage" id="image">
<property name="orientation">horizontal</property> <property name="allow_gray">False</property>
<property name="margin">3</property> <property name="height">30</property>
<property name="column-spacing">10</property> <property name="width">30</property>
<property name="visible">True</property> <property name="valign">center</property>
</object>
</child>
<child>
<object class="GtkGrid">
<property name="orientation">vertical</property>
<child> <child>
<object class="DinoUiAvatarImage" id="image"> <object class="GtkLabel" id="name_label">
<property name="allow_gray">False</property> <property name="max_width_chars">1</property>
<property name="height">30</property> <property name="ellipsize">end</property>
<property name="width">30</property> <property name="hexpand">1</property>
<property name="valign">center</property> <property name="xalign">0</property>
<property name="visible">True</property> <layout>
<property name="column">0</property>
<property name="row">0</property>
</layout>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkGrid"> <object class="GtkLabel" id="via_label">
<property name="orientation">vertical</property> <property name="max_width_chars">1</property>
<property name="visible">True</property> <property name="ellipsize">end</property>
<child> <property name="hexpand">1</property>
<object class="GtkLabel" id="name_label"> <property name="xalign">0</property>
<property name="max_width_chars">1</property> <attributes>
<property name="ellipsize">end</property> <attribute name="scale" value="0.8"></attribute>
<property name="expand">True</property> </attributes>
<property name="xalign">0</property> <layout>
<property name="visible">True</property> <property name="column">0</property>
</object> <property name="row">1</property>
<packing> </layout>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="via_label">
<property name="max_width_chars">1</property>
<property name="ellipsize">end</property>
<property name="expand">True</property>
<property name="xalign">0</property>
<property name="visible">True</property>
<attributes>
<attribute name="scale" value="0.8"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child>
</object> </object>
</child> </child>
</object> </object>
</child> </child>
</template> </object>
</interface> </interface>

View file

@ -1,86 +1,70 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<interface> <interface>
<requires lib="gtk" version="4.0"/>
<template class="DinoUiSelectJidFragment"> <template class="DinoUiSelectJidFragment">
<property name="height_request">500</property> <property name="height_request">500</property>
<property name="width_request">460</property> <property name="width_request">460</property>
<property name="visible">True</property> <property name="visible">True</property>
<child> <child>
<object class="GtkGrid"> <object class="GtkGrid">
<property name="expand">True</property> <property name="hexpand">1</property>
<property name="margin-top">20</property> <property name="margin-top">20</property>
<property name="margin-right">80</property> <property name="margin-end">80</property>
<property name="margin-bottom">20</property> <property name="margin-bottom">20</property>
<property name="margin-left">80</property> <property name="margin-start">80</property>
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<property name="row-spacing">10</property> <property name="row-spacing">10</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkEntry" id="entry"> <object class="GtkEntry" id="entry">
<property name="activates_default">True</property> <property name="activates_default">1</property>
<property name="hexpand">True</property> <property name="hexpand">1</property>
<property name="visible">True</property>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkBox"> <object class="GtkBox">
<property name="expand">True</property> <property name="hexpand">1</property>
<property name="vexpand">1</property>
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkFrame"> <object class="GtkFrame">
<property name="visible">True</property> <property name="child">
<child>
<object class="GtkScrolledWindow" id="scrolled_window"> <object class="GtkScrolledWindow" id="scrolled_window">
<property name="hscrollbar_policy">never</property> <property name="hscrollbar_policy">never</property>
<property name="expand">True</property> <property name="hexpand">1</property>
<property name="visible">True</property> <property name="vexpand">1</property>
<child> <property name="child">
<object class="GtkBox" id="box"> <object class="GtkBox" id="box">
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<property name="visible">True</property> </object>
</property>
</object>
</property>
</object>
</child>
<child>
<object class="GtkBox">
<property name="css-classes">toolbar</property>
<style>
<class name="toolbar"/>
<class name="inline-toolbar"/>
</style>
<child>
<object class="GtkButton" id="add_button">
<child>
<object class="GtkImage">
<property name="icon-name">list-add-symbolic</property>
<property name="icon-size">normal</property>
</object> </object>
</child> </child>
</object> </object>
</child> </child>
</object>
</child>
<child>
<object class="GtkToolbar">
<property name="visible">True</property>
<style>
<class name="inline-toolbar"/>
</style>
<child> <child>
<object class="GtkToolItem"> <object class="GtkButton" id="remove_button">
<property name="visible">True</property> <property name="sensitive">0</property>
<child> <child>
<object class="GtkBox"> <object class="GtkImage">
<property name="visible">True</property> <property name="icon-name">list-remove-symbolic</property>
<child> <property name="icon-size">normal</property>
<object class="GtkButton" id="add_button">
<property name="visible">True</property>
<child>
<object class="GtkImage">
<property name="icon-name">list-add-symbolic</property>
<property name="icon-size">1</property>
<property name="visible">True</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkButton" id="remove_button">
<property name="sensitive">False</property>
<property name="visible">True</property>
<child>
<object class="GtkImage">
<property name="icon-name">list-remove-symbolic</property>
<property name="icon-size">1</property>
<property name="visible">True</property>
</object>
</child>
</object>
</child>
</object> </object>
</child> </child>
</object> </object>

View file

@ -27,7 +27,6 @@
</style> </style>
<child> <child>
<object class="GtkImage" id="image"> <object class="GtkImage" id="image">
<property name="icon-size">5</property>
<property name="opacity">0.7</property> <property name="opacity">0.7</property>
<property name="visible">True</property> <property name="visible">True</property>
</object> </object>
@ -73,7 +72,10 @@
<property name="halign">end</property> <property name="halign">end</property>
<property name="orientation">horizontal</property> <property name="orientation">horizontal</property>
<property name="spacing">5</property> <property name="spacing">5</property>
<property name="margin">10</property> <property name="margin_top">10</property>
<property name="margin_bottom">10</property>
<property name="margin_start">10</property>
<property name="margin_end">10</property>
<property name="hexpand">True</property> <property name="hexpand">True</property>
<child> <child>
<object class="GtkButton" id="reject_call_button"> <object class="GtkButton" id="reject_call_button">
@ -97,7 +99,10 @@
</child> </child>
<child> <child>
<object class="GtkBox" id="multiparty_peer_box"> <object class="GtkBox" id="multiparty_peer_box">
<property name="margin">10</property> <property name="margin_top">10</property>
<property name="margin_bottom">10</property>
<property name="margin_start">10</property>
<property name="margin_end">10</property>
<property name="spacing">7</property> <property name="spacing">7</property>
<property name="hexpand">True</property> <property name="hexpand">True</property>
</object> </object>

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<interface> <interface>
<requires lib="gtk+" version="3.22"/> <requires lib="gtk" version="4.0"/>
<template class="DinoUiChatInputView"> <template class="DinoUiChatInputView">
<property name="hexpand">True</property> <property name="hexpand">True</property>
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
@ -12,46 +12,60 @@
<object class="GtkFrame" id="frame"> <object class="GtkFrame" id="frame">
<property name="margin_start">14</property> <property name="margin_start">14</property>
<property name="margin_end">14</property> <property name="margin_end">14</property>
<property name="visible">True</property> <property name="child">
<child>
<object class="GtkBox" id="outer_box"> <object class="GtkBox" id="outer_box">
<property name="orientation">horizontal</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkButton" id="file_button"> <object class="GtkButton" id="file_button">
<property name="tooltip-text" translatable="yes">Send a file</property> <property name="icon-name">mail-attachment-symbolic</property>
<property name="can-focus">False</property> <property name="tooltip-text" translatable="1">Send a file</property>
<property name="margin-top">3</property> <property name="margin-top">2</property>
<property name="relief">none</property>
<property name="valign">start</property> <property name="valign">start</property>
<property name="visible">True</property>
<style> <style>
<class name="flat"/> <class name="flat"/>
<class name="dino-chatinput-button"/> <class name="dino-chatinput-button"/>
<class name="dino-attach-button"/>
<class name="image-button"/> <class name="image-button"/>
</style> </style>
<child>
<object class="GtkImage">
<property name="icon-name">mail-attachment-symbolic</property>
<property name="icon-size">1</property>
<property name="visible">True</property>
</object>
</child>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkSeparator" id="file_separator"> <object class="GtkSeparator" id="file_separator">
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<property name="visible">True</property>
</object> </object>
</child> </child>
<child> <child>
<object class="DinoUiChatTextView" id="chat_text_view"> <object class="DinoUiChatTextView" id="chat_text_view">
<property name="visible">True</property> <property name="margin_start">7</property>
</object>
</child>
<child>
<object class="GtkMenuButton" id="emoji_button">
<property name="icon-name">dino-emoticon-symbolic</property>
<property name="has-frame">False</property>
<property name="margin-top">2</property>
<property name="valign">start</property>
<style>
<class name="flat"/>
<class name="dino-chatinput-button"/>
<class name="image-button"/>
</style>
</object>
</child>
<child>
<object class="GtkMenuButton" id="encryption_button">
<property name="icon-name">changes-allow-symbolic</property>
<property name="has-frame">False</property>
<property name="margin-top">2</property>
<property name="valign">start</property>
<style>
<class name="flat"/>
<class name="dino-chatinput-button"/>
<class name="image-button"/>
</style>
</object> </object>
</child> </child>
</object> </object>
</child> </property>
</object> </object>
</child> </child>
<child> <child>
@ -60,9 +74,8 @@
<property name="margin_bottom">3</property> <property name="margin_bottom">3</property>
<property name="margin_start">14</property> <property name="margin_start">14</property>
<property name="margin_end">14</property> <property name="margin_end">14</property>
<property name="visible">True</property>
<attributes> <attributes>
<attribute name="scale" value="0.8"/> <attribute name="scale" value="0.8"></attribute>
</attributes> </attributes>
<style> <style>
<class name="chat-input-status"/> <class name="chat-input-status"/>

View file

@ -1,122 +1,109 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<interface> <interface>
<requires lib="gtk" version="4.0"/>
<template class="DinoUiContactDetailsDialog"> <template class="DinoUiContactDetailsDialog">
<property name="title">Conversation Details</property>
<property name="modal">True</property> <property name="modal">True</property>
<child type="titlebar"> <child type="titlebar">
<object class="GtkHeaderBar"> <object class="GtkHeaderBar">
<property name="title">Conversation Details</property>
<property name="show_close_button">True</property>
<property name="visible">True</property>
</object> </object>
</child> </child>
<child internal-child="vbox"> <child internal-child="content_area">
<object class="GtkBox"> <object class="GtkBox">
<property name="expand">True</property> <property name="hexpand">1</property>
<property name="visible">True</property> <property name="vexpand">1</property>
<child> <child>
<object class="GtkScrolledWindow"> <object class="GtkScrolledWindow">
<property name="propagate_natural_height">True</property> <property name="propagate_natural_height">1</property>
<property name="max_content_height">500</property> <property name="max_content_height">500</property>
<property name="hscrollbar_policy">never</property> <property name="hscrollbar_policy">never</property>
<property name="expand">True</property> <property name="hexpand">1</property>
<property name="visible">True</property> <property name="vexpand">1</property>
<child> <property name="child">
<object class="GtkBox"> <object class="GtkBox">
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkGrid"> <object class="GtkGrid">
<property name="margin-top">20</property> <property name="margin-top">20</property>
<property name="margin-bottom">12</property> <property name="margin-bottom">12</property>
<property name="margin-right">100</property> <property name="margin-end">100</property>
<property name="margin-left">100</property> <property name="margin-start">100</property>
<property name="column-spacing">10</property> <property name="column-spacing">10</property>
<property name="visible">True</property>
<child> <child>
<object class="DinoUiAvatarImage" id="avatar"> <object class="DinoUiAvatarImage" id="avatar">
<property name="height">50</property> <property name="height">50</property>
<property name="width">50</property> <property name="width">50</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="allow_gray">False</property> <property name="allow_gray">False</property>
<layout>
<property name="column">0</property>
<property name="row">0</property>
<property name="row-span">2</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="width">1</property>
<property name="height">2</property>
</packing>
</child> </child>
<child> <child>
<object class="DinoUiUtilEntryLabelHybrid" id="name_hybrid"> <object class="DinoUiUtilEntryLabelHybrid" id="name_hybrid">
<property name="xalign">0</property> <property name="xalign">0</property>
<property name="expand">True</property> <property name="hexpand">True</property>
<property name="visible">True</property> <property name="visible">True</property>
<layout>
<property name="column">1</property>
<property name="row">0</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkLabel" id="name_label"> <object class="GtkLabel" id="name_label">
<property name="xalign">0</property> <property name="xalign">0</property>
<property name="expand">True</property> <property name="hexpand">1</property>
<property name="visible">True</property>
<attributes> <attributes>
<attribute name="weight" value="PANGO_WEIGHT_BOLD"/> <attribute name="weight" value="PANGO_WEIGHT_BOLD"></attribute>
</attributes> </attributes>
<layout>
<property name="column">1</property>
<property name="row">0</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkLabel" id="jid_label"> <object class="GtkLabel" id="jid_label">
<property name="xalign">0</property> <property name="xalign">0</property>
<property name="yalign">0</property> <property name="yalign">0</property>
<property name="selectable">True</property> <property name="selectable">1</property>
<property name="expand">True</property> <property name="hexpand">1</property>
<property name="visible">True</property> <layout>
<property name="column">1</property>
<property name="row">1</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkLabel" id="account_label"> <object class="GtkLabel" id="account_label">
<property name="xalign">1</property> <property name="xalign">1</property>
<property name="yalign">1</property> <property name="yalign">1</property>
<property name="margin">5</property> <property name="margin-start">5</property>
<property name="expand">True</property> <property name="margin-end">5</property>
<property name="visible">True</property> <property name="margin-top">5</property>
<property name="margin-bottom">5</property>
<property name="hexpand">1</property>
<layout>
<property name="column">2</property>
<property name="row">1</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">2</property>
<property name="top_attach">1</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child> </child>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkBox" id="main_box"> <object class="GtkBox" id="main_box">
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<property name="margin-right">100</property> <property name="margin-end">100</property>
<property name="margin-left">100</property> <property name="margin-start">100</property>
<property name="visible">True</property>
</object> </object>
</child> </child>
</object> </object>
</child> </property>
</object> </object>
</child> </child>
</object> </object>

View file

@ -1,115 +1,98 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<interface> <interface>
<requires lib="gtk" version="4.0"/>
<template class="DinoUiConversationSummaryConversationView"> <template class="DinoUiConversationSummaryConversationView">
<property name="expand">True</property>
<property name="homogeneous">False</property>
<property name="spacing">0</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkStack" id="stack"> <object class="GtkStack" id="stack">
<property name="transition_type">crossfade</property> <property name="transition_type">crossfade</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkOverlay"> <object class="GtkStackPage">
<property name="visible">True</property>
<child>
<object class="GtkScrolledWindow" id="scrolled">
<property name="hscrollbar_policy">never</property>
<property name="expand">True</property>
<property name="visible">True</property>
<child>
<object class="GtkEventBox" id="main_wrap_event_box">
<property name="valign">end</property>
<property name="visible">True</property>
<child>
<object class="GtkOverlay">
<property name="visible">True</property>
<child>
<object class="GtkEventBox" id="main_event_box">
<property name="visible">True</property>
<child>
<object class="DinoUiSizeRequestBox" id="main">
<property name="margin-bottom">15</property>
<property name="expand">False</property>
<property name="orientation">vertical</property>
<property name="visible">True</property>
<property name="size-request-mode">height-for-width</property>
</object>
</child>
</object>
</child>
<child type="overlay">
<object class="GtkBox" id="message_menu_box">
<property name="margin-right">10</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="valign">start</property>
<child>
<object class="GtkButton" id="button1">
<property name="vexpand">False</property>
<property name="halign">end</property>
<property name="valign">end</property>
<child>
<object class="GtkImage" id="button1_icon">
<property name="icon-size">1</property>
<property name="visible">True</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="index">-1</property>
</packing>
</child>
<child type="overlay">
<object class="GtkRevealer" id="notification_revealer">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">start</property>
<child>
<object class="GtkFrame" id="frame2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label_xalign">0</property>
<property name="shadow_type">none</property>
<style>
<class name="app-notification"/>
</style>
<child>
<object class="GtkBox" id="notifications">
<property name="expand">False</property>
<property name="orientation">vertical</property>
<property name="visible">True</property>
</object>
</child>
<child type="label_item">
<placeholder/>
</child>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="name">main</property> <property name="name">main</property>
</packing> <property name="child">
<object class="GtkOverlay">
<property name="child">
<object class="GtkScrolledWindow" id="scrolled">
<property name="hscrollbar_policy">never</property>
<property name="hexpand">1</property>
<property name="vexpand">1</property>
<property name="child">
<object class="GtkBox" id="main_wrap_box">
<property name="valign">end</property>
<child>
<object class="GtkOverlay">
<child>
<object class="GtkBox" id="main_event_box">
<property name="visible">True</property>
<child>
<object class="DinoUiSizeRequestBox" id="main">
<property name="margin-bottom">15</property>
<property name="orientation">vertical</property>
<property name="visible">True</property>
<property name="size-request-mode">height-for-width</property>
</object>
</child>
</object>
</child>
<child type="overlay">
<object class="GtkBox" id="message_menu_box">
<property name="margin-end">10</property>
<property name="halign">end</property>
<property name="valign">start</property>
<child>
<object class="GtkButton" id="button1">
<property name="visible">0</property>
<property name="vexpand">0</property>
<property name="halign">end</property>
<property name="valign">end</property>
<child>
<object class="GtkImage" id="button1_icon">
<property name="icon-size">normal</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</property>
</object>
</property>
<child type="overlay">
<object class="GtkRevealer" id="notification_revealer">
<property name="halign">center</property>
<property name="valign">start</property>
<property name="child">
<object class="GtkFrame" id="frame2">
<style>
<class name="app-notification"/>
</style>
<property name="child">
<object class="GtkBox" id="notifications">
<property name="hexpand">0</property>
<property name="vexpand">0</property>
<property name="orientation">vertical</property>
</object>
</property>
<child type="label_item">
<placeholder/>
</child>
</object>
</property>
</object>
</child>
</object>
</property>
</object>
</child> </child>
<child> <child>
<object class="GtkBox"> <object class="GtkStackPage">
<property name="visible">True</property>
</object>
<packing>
<property name="name">void</property> <property name="name">void</property>
</packing> <property name="child">
<object class="GtkBox"/>
</property>
</object>
</child> </child>
</object> </object>
</child> </child>

View file

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk" version="4.0"/>
<object class="GtkGrid" id="main_grid">
<property name="column-spacing">7</property>
<property name="row-spacing">2</property>
<child>
<object class="DinoUiAvatarImage" id="avatar_image">
<property name="height">35</property>
<property name="width">35</property>
<property name="valign">start</property>
<property name="margin-top">2</property>
<layout>
<property name="column">0</property>
<property name="row">0</property>
<property name="row-span">2</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel" id="name_label">
<property name="ellipsize">end</property>
<property name="xalign">0</property>
<property name="valign">baseline</property>
<attributes>
<attribute name="weight" value="PANGO_WEIGHT_BOLD"/>
</attributes>
<layout>
<property name="column">1</property>
<property name="row">0</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel" id="time_label">
<property name="xalign">0</property>
<property name="valign">baseline</property>
<attributes>
<attribute name="scale" value="0.8"/>
</attributes>
<style>
<class name="dim-label"/>
</style>
<layout>
<property name="column">2</property>
<property name="row">0</property>
</layout>
</object>
</child>
<child>
<object class="GtkImage" id="encrypted_image">
<property name="opacity">0.4</property>
<property name="pixel-size">14</property>
<layout>
<property name="column">3</property>
<property name="row">0</property>
</layout>
</object>
</child>
<child>
<object class="GtkImage" id="marked_image">
<property name="opacity">0.4</property>
<property name="pixel-size">14</property>
<property name="halign">start</property>
<property name="hexpand">True</property>
<layout>
<property name="column">4</property>
<property name="row">0</property>
</layout>
</object>
</child>
</object>
</interface>

View file

@ -1,42 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<interface> <interface>
<template class="DinoUiConversationListTitlebarCsd" parent="GtkHeaderBar"> <requires lib="gtk" version="4.0"/>
<object class="GtkHeaderBar" id="header_bar">
<property name="hexpand">False</property> <property name="hexpand">False</property>
<property name="show_close_button">True</property>
<property name="visible">True</property>
<style> <style>
<class name="dino-left"/> <class name="dino-left"/>
</style> </style>
<child> <child>
<object class="GtkMenuButton" id="add_button"> <object class="GtkMenuButton" id="add_button">
<property name="tooltip_text" translatable="yes">Start Conversation</property> <property name="tooltip_text" translatable="True">Start Conversation</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkImage"> <object class="GtkImage">
<property name="visible">True</property>
<property name="icon-name">list-add-symbolic</property> <property name="icon-name">list-add-symbolic</property>
<property name="icon-size">1</property> <property name="icon-size">normal</property>
</object> </object>
</child> </child>
</object> </object>
<packing>
<property name="pack_type">start</property>
</packing>
</child> </child>
<child> <child type="end">
<object class="GtkMenuButton" id="menu_button"> <object class="GtkMenuButton" id="menu_button">
<property name="visible">True</property>
<child> <child>
<object class="GtkImage"> <object class="GtkImage">
<property name="visible">True</property>
<property name="icon-name">open-menu-symbolic</property> <property name="icon-name">open-menu-symbolic</property>
<property name="icon-size">1</property> <property name="icon-size">normal</property>
</object> </object>
</child> </child>
</object> </object>
<packing>
<property name="pack_type">end</property>
</packing>
</child> </child>
</template> </object>
</interface> </interface>

View file

@ -6,12 +6,13 @@
<object class="GtkRevealer" id="main_revealer"> <object class="GtkRevealer" id="main_revealer">
<property name="transition-type">slide-down</property> <property name="transition-type">slide-down</property>
<property name="transition-duration">200</property> <property name="transition-duration">200</property>
<property name="reveal-child">False</property> <property name="reveal-child">True</property>
<property name="visible">True</property> <property name="visible">True</property>
<child> <child>
<object class="GtkBox"> <object class="GtkBox">
<property name="orientation">horizontal</property> <property name="orientation">horizontal</property>
<property name="margin">10</property> <property name="margin-top">10</property>
<property name="margin-bottom">10</property>
<property name="margin-start">7</property> <property name="margin-start">7</property>
<property name="margin-end">14</property> <property name="margin-end">14</property>
<property name="visible">True</property> <property name="visible">True</property>
@ -25,7 +26,7 @@
</child> </child>
<child> <child>
<object class="GtkBox"> <object class="GtkBox">
<property name="margin-left">10</property> <property name="margin-start">10</property>
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<property name="visible">True</property> <property name="visible">True</property>
<child> <child>
@ -37,8 +38,8 @@
<object class="GtkLabel" id="name_label"> <object class="GtkLabel" id="name_label">
<property name="max_width_chars">1</property> <property name="max_width_chars">1</property>
<property name="ellipsize">end</property> <property name="ellipsize">end</property>
<property name="expand">True</property> <property name="hexpand">True</property>
<property name="margin-right">7</property> <property name="margin-end">7</property>
<property name="xalign">0</property> <property name="xalign">0</property>
<property name="visible">True</property> <property name="visible">True</property>
</object> </object>
@ -86,7 +87,8 @@
<object class="GtkLabel" id="message_label"> <object class="GtkLabel" id="message_label">
<property name="max_width_chars">1</property> <property name="max_width_chars">1</property>
<property name="ellipsize">end</property> <property name="ellipsize">end</property>
<property name="expand">True</property> <property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="valign">end</property> <property name="valign">end</property>
<property name="xalign">0</property> <property name="xalign">0</property>
<attributes> <attributes>

View file

@ -1,75 +1,76 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<interface> <interface>
<template class="DinoUiConversationView" parent="GtkOverlay"> <template class="DinoUiConversationView">
<property name="visible">True</property> <property name="visible">True</property>
<child> <child>
<object class="GtkBox"> <object class="GtkOverlay" id="overlay">
<property name="orientation">vertical</property>
<property name="visible">True</property>
<style>
<class name="dino-conversation"/>
</style>
<child> <child>
<object class="GtkOverlay"> <object class="GtkBox">
<property name="visible">True</property> <property name="orientation">vertical</property>
<style>
<class name="dino-conversation"/>
</style>
<child> <child>
<object class="DinoUiConversationSummaryConversationView" id="conversation_frame"> <object class="GtkOverlay" id="conversation_overlay">
<property name="visible">True</property> <property name="vexpand">True</property>
</object>
</child>
<child type="overlay">
<object class="GtkRevealer" id="goto_end_revealer">
<property name="halign">end</property>
<property name="valign">end</property>
<property name="transition-type">crossfade</property>
<property name="visible">True</property>
<property name="margin-end">30</property>
<property name="margin-bottom">30</property>
<child> <child>
<object class="GtkButton" id="goto_end_button"> <object class="DinoUiConversationSummaryConversationView" id="conversation_frame">
<property name="width-request">30</property> </object>
<property name="height-request">30</property> </child>
<property name="vexpand">False</property> <child type="overlay">
<object class="GtkRevealer" id="goto_end_revealer">
<property name="halign">end</property> <property name="halign">end</property>
<property name="valign">end</property> <property name="valign">end</property>
<property name="transition-type">crossfade</property>
<property name="visible">True</property> <property name="visible">True</property>
<style> <property name="margin-end">30</property>
<class name="circular-button"/> <property name="margin-bottom">30</property>
</style>
<child> <child>
<object class="GtkImage"> <object class="GtkButton" id="goto_end_button">
<property name="icon-name">go-down-symbolic</property> <property name="width-request">30</property>
<property name="icon-size">1</property> <property name="height-request">30</property>
<property name="vexpand">False</property>
<property name="halign">end</property>
<property name="valign">end</property>
<property name="visible">True</property> <property name="visible">True</property>
<style>
<class name="circular-button"/>
</style>
<child>
<object class="GtkImage">
<property name="icon-name">go-down-symbolic</property>
<property name="icon-size">1</property>
<property name="visible">True</property>
</object>
</child>
</object> </object>
</child> </child>
</object> </object>
</child> </child>
</object> </object>
</child>
<child>
<object class="DinoUiChatInputView" id="chat_input">
<property name="visible">True</property>
</object>
</child> </child>
</object> </object>
</child> </child>
<child> <child type="overlay">
<object class="DinoUiChatInputView" id="chat_input"> <object class="GtkRevealer" id="white_revealer">
<property name="visible">True</property> <property name="transition-type">crossfade</property>
</object> <property name="transition-duration">200</property>
</child> <property name="reveal-child">False</property>
</object> <property name="visible">False</property>
</child> <child>
<child type="overlay"> <object class="GtkBox">
<object class="GtkRevealer" id="white_revealer"> <property name="opacity">0.7</property>
<property name="transition-type">crossfade</property> <property name="visible">true</property>
<property name="transition-duration">200</property> <style>
<property name="reveal-child">False</property> <class name="dino-white-overlay"/>
<property name="visible">False</property> </style>
<child> </object>
<object class="GtkBox"> </child>
<property name="opacity">0.7</property>
<property name="visible">true</property>
<style>
<class name="dino-white-overlay"/>
</style>
</object> </object>
</child> </child>
</object> </object>

View file

@ -1,410 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface domain="gtk30">
<template class="DinoEmojiChooser" parent="GtkPopover">
<property name="modal">1</property>
<style>
<class name="emoji-picker"/>
</style>
<child>
<object class="GtkBox" id="box">
<property name="orientation">vertical</property>
<property name="visible">1</property>
<child>
<object class="GtkSearchEntry" id="search_entry">
<property name="visible">1</property>
<property name="input-hints">no-emoji</property>
<signal name="search-changed" handler="search_changed"/>
</object>
</child>
<child>
<object class="GtkStack" id="stack">
<property name="visible">1</property>
<child>
<object class="GtkBox">
<property name="visible">1</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkScrolledWindow" id="scrolled_window">
<property name="visible">1</property>
<property name="vexpand">1</property>
<property name="hscrollbar-policy">never</property>
<property name="min-content-height">250</property>
<style>
<class name="view"/>
</style>
<child>
<object class="GtkBox" id="emoji_box">
<property name="visible">1</property>
<property name="orientation">vertical</property>
<property name="margin">6</property>
<property name="spacing">6</property>
<!-- Remember to keep headings here in sync with button tooltips below -->
<child>
<object class="GtkFlowBox" id="recent.box">
<property name="visible">1</property>
<property name="homogeneous">1</property>
<property name="selection-mode">none</property>
<signal name="child-activated" handler="emoji_activated"/>
</object>
</child>
<child>
<object class="GtkLabel" id="people.heading">
<property name="visible">1</property>
<property name="label" translatable="yes">Smileys &amp; People</property>
<property name="xalign">0</property>
</object>
</child>
<child>
<object class="GtkFlowBox" id="people.box">
<property name="visible">1</property>
<property name="homogeneous">1</property>
<property name="selection-mode">none</property>
<signal name="child-activated" handler="emoji_activated"/>
</object>
</child>
<child>
<object class="GtkLabel" id="body.heading">
<property name="visible">1</property>
<property name="label" translatable="yes">Body &amp; Clothing</property>
<property name="xalign">0</property>
</object>
</child>
<child>
<object class="GtkFlowBox" id="body.box">
<property name="visible">1</property>
<property name="homogeneous">1</property>
<property name="selection-mode">none</property>
<signal name="child-activated" handler="emoji_activated"/>
</object>
</child>
<child>
<object class="GtkLabel" id="nature.heading">
<property name="visible">1</property>
<property name="label" translatable="yes">Animals &amp; Nature</property>
<property name="xalign">0</property>
</object>
</child>
<child>
<object class="GtkFlowBox" id="nature.box">
<property name="visible">1</property>
<property name="homogeneous">1</property>
<property name="selection-mode">none</property>
<signal name="child-activated" handler="emoji_activated"/>
</object>
</child>
<child>
<object class="GtkLabel" id="food.heading">
<property name="visible">1</property>
<property name="label" translatable="yes">Food &amp; Drink</property>
<property name="xalign">0</property>
</object>
</child>
<child>
<object class="GtkFlowBox" id="food.box">
<property name="visible">1</property>
<property name="homogeneous">1</property>
<property name="selection-mode">none</property>
<signal name="child-activated" handler="emoji_activated"/>
</object>
</child>
<child>
<object class="GtkLabel" id="travel.heading">
<property name="visible">1</property>
<property name="label" translatable="yes">Travel &amp; Places</property>
<property name="xalign">0</property>
</object>
</child>
<child>
<object class="GtkFlowBox" id="travel.box">
<property name="visible">1</property>
<property name="homogeneous">1</property>
<property name="selection-mode">none</property>
<signal name="child-activated" handler="emoji_activated"/>
</object>
</child>
<child>
<object class="GtkLabel" id="activities.heading">
<property name="visible">1</property>
<property name="label" translatable="yes">Activities</property>
<property name="xalign">0</property>
</object>
</child>
<child>
<object class="GtkFlowBox" id="activities.box">
<property name="visible">1</property>
<property name="homogeneous">1</property>
<property name="selection-mode">none</property>
<signal name="child-activated" handler="emoji_activated"/>
</object>
</child>
<child>
<object class="GtkLabel" id="objects.heading">
<property name="visible">1</property>
<property name="label" translatable="yes" context="emoji category">Objects</property>
<property name="xalign">0</property>
</object>
</child>
<child>
<object class="GtkFlowBox" id="objects.box">
<property name="visible">1</property>
<property name="homogeneous">1</property>
<property name="selection-mode">none</property>
<signal name="child-activated" handler="emoji_activated"/>
</object>
</child>
<child>
<object class="GtkLabel" id="symbols.heading">
<property name="visible">1</property>
<property name="label" translatable="yes">Symbols</property>
<property name="xalign">0</property>
</object>
</child>
<child>
<object class="GtkFlowBox" id="symbols.box">
<property name="visible">1</property>
<property name="homogeneous">1</property>
<property name="selection-mode">none</property>
<signal name="child-activated" handler="emoji_activated"/>
</object>
</child>
<child>
<object class="GtkLabel" id="flags.heading">
<property name="visible">1</property>
<property name="label" translatable="yes">Flags</property>
<property name="xalign">0</property>
</object>
</child>
<child>
<object class="GtkFlowBox" id="flags.box">
<property name="visible">1</property>
<property name="homogeneous">1</property>
<property name="selection-mode">none</property>
<signal name="child-activated" handler="emoji_activated"/>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkBox">
<property name="visible">1</property>
<!-- Remember to keep tooltips here in sync with section headings above -->
<child>
<object class="GtkButton" id="recent.button">
<property name="visible">1</property>
<property name="relief">none</property>
<property name="tooltip-text" translatable="yes">Recent</property>
<style>
<class name="emoji-section"/>
</style>
<child>
<object class="GtkImage" id="recent.icon">
<property name="visible">1</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkButton" id="people.button">
<property name="visible">1</property>
<property name="relief">none</property>
<property name="tooltip-text" translatable="yes">Smileys &amp; People</property>
<style>
<class name="emoji-section"/>
</style>
<child>
<object class="GtkImage" id="people.icon">
<property name="visible">1</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkButton" id="body.button">
<property name="visible">1</property>
<property name="relief">none</property>
<property name="tooltip-text" translatable="yes">Body &amp; Clothing</property>
<style>
<class name="emoji-section"/>
</style>
<child>
<object class="GtkImage" id="body.icon">
<property name="visible">1</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkButton" id="nature.button">
<property name="visible">1</property>
<property name="relief">none</property>
<property name="tooltip-text" translatable="yes">Animals &amp; Nature</property>
<style>
<class name="emoji-section"/>
</style>
<child>
<object class="GtkImage" id="nature.icon">
<property name="visible">1</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkButton" id="food.button">
<property name="visible">1</property>
<property name="relief">none</property>
<property name="tooltip-text" translatable="yes">Food &amp; Drink</property>
<style>
<class name="emoji-section"/>
</style>
<child>
<object class="GtkImage" id="food.icon">
<property name="visible">1</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkButton" id="travel.button">
<property name="visible">1</property>
<property name="relief">none</property>
<property name="tooltip-text" translatable="yes">Travel &amp; Places</property>
<style>
<class name="emoji-section"/>
</style>
<child>
<object class="GtkImage" id="travel.icon">
<property name="visible">1</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkButton" id="activities.button">
<property name="visible">1</property>
<property name="relief">none</property>
<property name="tooltip-text" translatable="yes">Activities</property>
<style>
<class name="emoji-section"/>
</style>
<child>
<object class="GtkImage" id="activities.icon">
<property name="visible">1</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkButton" id="objects.button">
<property name="visible">1</property>
<property name="relief">none</property>
<property name="tooltip-text" translatable="yes" context="emoji category">Objects</property>
<style>
<class name="emoji-section"/>
</style>
<child>
<object class="GtkImage" id="objects.icon">
<property name="visible">1</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkButton" id="symbols.button">
<property name="visible">1</property>
<property name="relief">none</property>
<property name="tooltip-text" translatable="yes">Symbols</property>
<style>
<class name="emoji-section"/>
</style>
<child>
<object class="GtkImage" id="symbols.icon">
<property name="visible">1</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkButton" id="flags.button">
<property name="visible">1</property>
<property name="relief">none</property>
<property name="tooltip-text" translatable="yes">Flags</property>
<style>
<class name="emoji-section"/>
</style>
<child>
<object class="GtkImage" id="flags.icon">
<property name="visible">1</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="name">list</property>
</packing>
</child>
<child>
<object class="GtkGrid">
<property name="visible">1</property>
<property name="row-spacing">12</property>
<property name="halign">center</property>
<property name="valign">center</property>
<style>
<class name="dim-label"/>
</style>
<child>
<object class="GtkImage">
<property name="visible">1</property>
<property name="icon-name">edit-find-symbolic</property>
<property name="pixel-size">72</property>
<style>
<class name="dim-label"/>
</style>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">1</property>
<property name="label" translatable="yes">No Results Found</property>
<attributes>
<attribute name="weight" value="bold"/>
<attribute name="scale" value="1.44"/>
</attributes>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">1</property>
<property name="label" translatable="yes">Try a different search</property>
<style>
<class name="dim-label"/>
</style>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">2</property>
</packing>
</child>
</object>
<packing>
<property name="name">empty</property>
</packing>
</child>
</object>
</child>
</object>
</child>
</template>
</interface>

View file

@ -1,110 +1,93 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<interface> <interface>
<template class="DinoUiFileDefaultWidget" parent="GtkEventBox"> <requires lib="gtk" version="4.0"/>
<property name="halign">start</property> <template class="DinoUiFileDefaultWidget">
<style> <property name="halign">start</property>
<class name="file-box-outer"/> <style>
</style> <class name="file-box-outer"/>
</style>
<child>
<object class="DinoUiSizingBin">
<property name="target-width">500</property>
<property name="max-width">500</property>
<property name="hexpand">True</property>
<child> <child>
<object class="DinoUiSizingBin"> <object class="GtkBox">
<property name="target-width">500</property> <property name="hexpand">1</property>
<property name="max-width">500</property> <property name="spacing">10</property>
<property name="hexpand">True</property> <style>
<property name="visible">True</property> <class name="file-box"/>
</style>
<child>
<object class="GtkStack" id="image_stack">
<property name="transition-type">crossfade</property>
<property name="transition-duration">50</property>
<property name="valign">center</property>
<child> <child>
<object class="GtkBox"> <object class="GtkStackPage">
<property name="orientation">horizontal</property> <property name="name">download_image</property>
<property name="halign">fill</property> <property name="child">
<property name="hexpand">true</property> <object class="GtkImage">
<property name="spacing">10</property> <property name="icon-name">dino-file-download-symbolic</property>
<property name="visible">True</property> <property name="pixel-size">30</property>
<style> <property name="opacity">0.7</property>
<class name="file-box"/> </object>
</style> </property>
<child> </object>
<object class="GtkEventBox" id="stack_event_box">
<property name="visible">True</property>
<child>
<object class="GtkStack" id="image_stack">
<property name="transition-type">crossfade</property>
<property name="transition-duration">50</property>
<property name="valign">center</property>
<property name="visible">True</property>
<child>
<object class="GtkImage">
<property name="icon-name">dino-file-download-symbolic</property>
<property name="icon-size">5</property>
<property name="opacity">0.7</property>
<property name="visible">True</property>
</object>
<packing>
<property name="name">download_image</property>
</packing>
</child>
<child>
<object class="GtkImage" id="content_type_image">
<property name="icon-size">5</property>
<property name="opacity">0.7</property>
<property name="visible">True</property>
</object>
<packing>
<property name="name">content_type_image</property>
</packing>
</child>
<child>
<object class="GtkSpinner" id="spinner">
<property name="visible">True</property>
</object>
<packing>
<property name="name">spinner</property>
</packing>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="hexpand">True</property>
<property name="visible">True</property>
<child>
<object class="GtkLabel" id="name_label">
<property name="ellipsize">middle</property>
<property name="xalign">0</property>
<property name="yalign">0</property>
<property name="visible">True</property>
</object>
</child>
<child>
<object class="GtkLabel" id="mime_label">
<property name="xalign">0</property>
<property name="yalign">1</property>
<property name="visible">True</property>
<attributes>
<attribute name="scale" value="0.8"/>
</attributes>
</object>
</child>
</object>
</child>
<child>
<object class="GtkMenuButton" id="file_menu">
<property name="visible">True</property>
<property name="opacity">0</property>
<property name="relief">none</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="icon-name">open-menu-symbolic</property>
<property name="icon-size">1</property>
</object>
</child>
</object>
</child>
</object>
</child> </child>
</object> <child>
<object class="GtkStackPage">
<property name="name">content_type_image</property>
<property name="child">
<object class="GtkImage" id="content_type_image">
<property name="pixel-size">30</property>
<property name="opacity">0.7</property>
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">spinner</property>
<property name="child">
<object class="GtkSpinner" id="spinner"/>
</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="hexpand">1</property>
<child>
<object class="GtkLabel" id="name_label">
<property name="ellipsize">middle</property>
<property name="xalign">0</property>
<property name="yalign">0</property>
</object>
</child>
<child>
<object class="GtkLabel" id="mime_label">
<property name="xalign">0</property>
<property name="yalign">1</property>
<attributes>
<attribute name="scale" value="0.8"></attribute>
</attributes>
</object>
</child>
</object>
</child>
<child>
<object class="GtkMenuButton" id="file_menu">
<property name="icon-name">open-menu-symbolic</property>
<property name="opacity">0</property>
<property name="has_frame">False</property>
</object>
</child>
</object>
</child> </child>
</template> </object>
</child>
</template>
</interface> </interface>

View file

@ -1,67 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<interface> <interface>
<template class="DinoUiFileSendOverlay"> <requires lib="gtk" version="4.0"/>
<property name="hexpand">False</property> <object class="GtkBox" id="main_box">
<property name="vexpand">False</property> <property name="hexpand">0</property>
<property name="vexpand">0</property>
<property name="halign">center</property> <property name="halign">center</property>
<property name="valign">center</property> <property name="valign">center</property>
<property name="visible">True</property>
<style> <style>
<class name="background"/> <class name="background"/>
<class name="dino-file-overlay"/> <class name="dino-file-overlay"/>
</style> </style>
<child> <child>
<object class="DinoUiSizeRequestBox"> <object class="GtkBox">
<property name="size-request-mode">height-for-width</property>
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<property name="margin-right">20</property> <property name="margin-end">20</property>
<property name="margin-left">20</property> <property name="margin-start">20</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkBox"> <object class="GtkBox">
<property name="margin-top">10</property> <property name="margin-top">10</property>
<property name="margin-bottom">10</property> <property name="margin-bottom">10</property>
<property name="orientation">horizontal</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkLabel"> <object class="GtkLabel">
<property name="label" translatable="yes">Send a file</property> <property name="label" translatable="1">Send a file</property>
<property name="hexpand">True</property> <property name="hexpand">1</property>
<property name="visible">True</property>
<attributes> <attributes>
<attribute name="weight" value="PANGO_WEIGHT_BOLD"/> <attribute name="weight" value="PANGO_WEIGHT_BOLD"></attribute>
</attributes> </attributes>
</object> </object>
<packing>
<property name="pack_type">start</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkButton" id="close_button"> <object class="GtkButton" id="close_button">
<property name="can_default">True</property> <property name="focusable">1</property>
<property name="can_focus">True</property>
<property name="visible">True</property>
<style> <style>
<class name="titlebutton"/> <class name="titlebutton"/>
<class name="close"/> <class name="close"/>
</style> </style>
<child> <child>
<object class="GtkImage"> <object class="GtkImage">
<property name="visible">True</property>
<property name="icon-name">window-close-symbolic</property> <property name="icon-name">window-close-symbolic</property>
<property name="icon-size">1</property>
</object> </object>
</child> </child>
</object> </object>
<packing>
<property name="pack_type">end</property>
</packing>
</child> </child>
</object> </object>
</child> </child>
<child> <child>
<object class="DinoUiSizingBin" id="file_widget_insert"> <object class="DinoUiSizingBin" id="file_widget_insert">
<property name="visible">True</property>
</object> </object>
</child> </child>
<child> <child>
@ -69,29 +53,21 @@
<property name="margin-top">15</property> <property name="margin-top">15</property>
<property name="margin-bottom">15</property> <property name="margin-bottom">15</property>
<property name="spacing">5</property> <property name="spacing">5</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkLabel" id="info_label"> <object class="GtkLabel" id="info_label"/>
<property name="visible">True</property>
</object>
</child> </child>
<child> <child>
<object class="GtkButton" id="send_button"> <object class="GtkButton" id="send_button">
<property name="label" translatable="yes">Send</property> <property name="label" translatable="1">Send</property>
<property name="can_default">True</property> <property name="focusable">1</property>
<property name="can_focus">True</property>
<property name="visible">True</property>
<style> <style>
<class name="suggested-action"/> <class name="suggested-action"/>
</style> </style>
</object> </object>
<packing>
<property name="pack_type">end</property>
</packing>
</child> </child>
</object> </object>
</child> </child>
</object> </object>
</child> </child>
</template> </object>
</interface> </interface>

View file

@ -1,170 +1,158 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<interface> <interface>
<template class="DinoUiGlobalSearch" parent="GtkOverlay"> <requires lib="gtk" version="4.0"/>
<property name="visible">True</property> <object class="GtkOverlay" id="overlay">
<child> <property name="child">
<object class="GtkBox"> <object class="GtkBox">
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkSearchEntry" id="search_entry"> <object class="GtkSearchEntry" id="search_entry">
<property name="visible">True</property> <property name="margin-start">12</property>
<property name="margin">12</property> <property name="margin-end">12</property>
<property name="margin-top">12</property>
<property name="margin-bottom">12</property>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkStack" id="results_empty_stack"> <object class="GtkStack" id="results_empty_stack">
<property name="visible">True</property>
<child> <child>
<object class="GtkBox"> <object class="GtkStackPage">
<property name="orientation">vertical</property>
<property name="spacing">10</property>
<property name="valign">center</property>
<property name="visible">True</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="icon-name">system-search-symbolic</property>
<property name="icon-size">4</property>
<property name="pixel-size">72</property>
<style>
<class name="dim-label"/>
</style>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label" translatable="yes">No active search</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="visible">True</property>
<attributes>
<attribute name="weight" value="PANGO_WEIGHT_BOLD"/>
<attribute name="scale" value="1.3"/>
</attributes>
<style>
<class name="dim-label"/>
</style>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label" translatable="yes">Type to start a search</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="visible">True</property>
<style>
<class name="dim-label"/>
</style>
</object>
</child>
</object>
<packing>
<property name="name">empty</property> <property name="name">empty</property>
</packing> <property name="child">
</child> <object class="GtkBox">
<child> <property name="orientation">vertical</property>
<object class="GtkBox"> <property name="spacing">10</property>
<property name="orientation">vertical</property> <property name="valign">center</property>
<property name="spacing">10</property>
<property name="valign">center</property>
<property name="visible">True</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="icon-name">face-uncertain-symbolic</property>
<property name="icon-size">4</property>
<property name="pixel-size">72</property>
<style>
<class name="dim-label"/>
</style>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label" translatable="yes">No matching messages</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="visible">True</property>
<attributes>
<attribute name="weight" value="PANGO_WEIGHT_BOLD"/>
<attribute name="scale" value="1.3"/>
</attributes>
<style>
<class name="dim-label"/>
</style>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label" translatable="yes">Check the spelling or try to remove filters</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="visible">True</property>
<style>
<class name="dim-label"/>
</style>
</object>
</child>
</object>
<packing>
<property name="name">no-result</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="visible">True</property>
<child>
<object class="GtkLabel" id="entry_number_label">
<property name="xalign">0</property>
<property name="use-markup">True</property>
<property name="margin-left">17</property>
<property name="visible">True</property>
</object>
</child>
<child>
<object class="GtkScrolledWindow" id="results_scrolled">
<property name="hscrollbar-policy">never</property>
<property name="expand">True</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkBox" id="results_box"> <object class="GtkImage">
<property name="orientation">vertical</property> <property name="icon-name">system-search-symbolic</property>
<property name="spacing">25</property> <property name="icon-size">large</property>
<property name="margin">10</property> <property name="pixel-size">72</property>
<property name="valign">start</property> <style>
<property name="visible">True</property> <class name="dim-label"/>
</style>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label" translatable="1">No active search</property>
<attributes>
<attribute name="weight" value="PANGO_WEIGHT_BOLD"></attribute>
<attribute name="scale" value="1.3"></attribute>
</attributes>
<style>
<class name="dim-label"/>
</style>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label" translatable="1">Type to start a search</property>
<style>
<class name="dim-label"/>
</style>
</object> </object>
</child> </child>
</object> </object>
</child> </property>
</object> </object>
<packing> </child>
<child>
<object class="GtkStackPage">
<property name="name">no-result</property>
<property name="child">
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="spacing">10</property>
<property name="valign">center</property>
<child>
<object class="GtkImage">
<property name="icon-name">face-uncertain-symbolic</property>
<property name="icon-size">large</property>
<property name="pixel-size">72</property>
<style>
<class name="dim-label"/>
</style>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label" translatable="1">No matching messages</property>
<attributes>
<attribute name="weight" value="PANGO_WEIGHT_BOLD"></attribute>
<attribute name="scale" value="1.3"></attribute>
</attributes>
<style>
<class name="dim-label"/>
</style>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label" translatable="1">Check the spelling or try to remove filters</property>
<style>
<class name="dim-label"/>
</style>
</object>
</child>
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">results</property> <property name="name">results</property>
</packing> <property name="child">
<object class="GtkBox">
<property name="orientation">vertical</property>
<child>
<object class="GtkLabel" id="entry_number_label">
<property name="xalign">0</property>
<property name="use-markup">1</property>
<property name="margin-start">17</property>
</object>
</child>
<child>
<object class="GtkScrolledWindow" id="results_scrolled">
<property name="hscrollbar-policy">never</property>
<property name="hexpand">1</property>
<property name="vexpand">1</property>
<property name="child">
<object class="GtkBox" id="results_box">
<property name="orientation">vertical</property>
<property name="spacing">25</property>
<property name="margin-start">10</property>
<property name="margin-end">10</property>
<property name="margin-top">10</property>
<property name="margin-bottom">10</property>
<property name="valign">start</property>
</object>
</property>
</object>
</child>
</object>
</property>
</object>
</child> </child>
</object> </object>
</child> </child>
</object> </object>
</child> </property>
<child type="overlay"> <child type="overlay">
<object class="GtkFrame" id="auto_complete_overlay"> <object class="GtkFrame" id="auto_complete_overlay">
<property name="visible">True</property>
<property name="margin-top">42</property> <property name="margin-top">42</property>
<property name="margin-left">12</property> <property name="margin-start">12</property>
<property name="margin-right">12</property> <property name="margin-end">12</property>
<property name="valign">start</property> <property name="valign">start</property>
<style> <style>
<class name="auto-complete"/> <class name="auto-complete"/>
</style> </style>
<child> <property name="child">
<object class="GtkListBox" id="auto_complete_list"> <object class="GtkListBox" id="auto_complete_list">
<property name="visible">True</property>
<property name="selection-mode">browse</property> <property name="selection-mode">browse</property>
</object> </object>
</child> </property>
</object> </object>
</child> </child>
</template> </object>
</interface> </interface>

View file

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View file

Before

Width:  |  Height:  |  Size: 346 B

After

Width:  |  Height:  |  Size: 346 B

View file

Before

Width:  |  Height:  |  Size: 490 B

After

Width:  |  Height:  |  Size: 490 B

View file

Before

Width:  |  Height:  |  Size: 816 B

After

Width:  |  Height:  |  Size: 816 B

View file

Before

Width:  |  Height:  |  Size: 664 B

After

Width:  |  Height:  |  Size: 664 B

View file

Before

Width:  |  Height:  |  Size: 890 B

After

Width:  |  Height:  |  Size: 890 B

View file

Before

Width:  |  Height:  |  Size: 812 B

After

Width:  |  Height:  |  Size: 812 B

View file

Before

Width:  |  Height:  |  Size: 574 B

After

Width:  |  Height:  |  Size: 574 B

View file

Before

Width:  |  Height:  |  Size: 593 B

After

Width:  |  Height:  |  Size: 593 B

View file

Before

Width:  |  Height:  |  Size: 475 B

After

Width:  |  Height:  |  Size: 475 B

View file

Before

Width:  |  Height:  |  Size: 457 B

After

Width:  |  Height:  |  Size: 457 B

View file

Before

Width:  |  Height:  |  Size: 675 B

After

Width:  |  Height:  |  Size: 675 B

View file

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

Before

Width:  |  Height:  |  Size: 417 B

After

Width:  |  Height:  |  Size: 417 B

View file

Before

Width:  |  Height:  |  Size: 599 B

After

Width:  |  Height:  |  Size: 599 B

View file

Before

Width:  |  Height:  |  Size: 669 B

After

Width:  |  Height:  |  Size: 669 B

View file

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

Before

Width:  |  Height:  |  Size: 975 B

After

Width:  |  Height:  |  Size: 975 B

View file

Before

Width:  |  Height:  |  Size: 661 B

After

Width:  |  Height:  |  Size: 661 B

View file

Before

Width:  |  Height:  |  Size: 476 B

After

Width:  |  Height:  |  Size: 476 B

View file

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

Before

Width:  |  Height:  |  Size: 643 B

After

Width:  |  Height:  |  Size: 643 B

View file

Before

Width:  |  Height:  |  Size: 454 B

After

Width:  |  Height:  |  Size: 454 B

View file

Before

Width:  |  Height:  |  Size: 399 B

After

Width:  |  Height:  |  Size: 399 B

View file

@ -1,30 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<interface> <interface>
<requires lib="gtk" version="4.0"/>
<template class="DinoUiManageAccountsAccountRow"> <template class="DinoUiManageAccountsAccountRow">
<property name="visible">True</property> <property name="visible">True</property>
<child> <child>
<object class="GtkGrid"> <object class="GtkGrid">
<property name="orientation">horizontal</property> <property name="margin-start">6</property>
<property name="margin">6</property> <property name="margin-end">6</property>
<property name="margin-top">6</property>
<property name="margin-bottom">6</property>
<property name="column-spacing">6</property> <property name="column-spacing">6</property>
<property name="visible">True</property>
<child> <child>
<object class="DinoUiAvatarImage" id="image"> <object class="DinoUiAvatarImage" id="image">
<property name="height">40</property> <property name="height">40</property>
<property name="width">40</property> <property name="width">40</property>
<property name="visible">True</property>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkLabel" id="jid_label"> <object class="GtkLabel" id="jid_label">
<property name="halign">0.5</property>
<property name="xalign">0</property> <property name="xalign">0</property>
<property name="expand">True</property> <property name="hexpand">1</property>
<property name="visible">True</property>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkImage" id="icon"> <object class="GtkImage" id="icon">
<property name="visible">False</property>
<property name="icon-name">dialog-warning-symbolic</property> <property name="icon-name">dialog-warning-symbolic</property>
</object> </object>
</child> </child>

File diff suppressed because it is too large Load diff

View file

@ -1,78 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<interface> <interface>
<requires lib="gtk" version="4.0"/>
<template class="DinoUiManageAccountsDialog"> <template class="DinoUiManageAccountsDialog">
<property name="title" translatable="1">Accounts</property>
<property name="width-request">700</property> <property name="width-request">700</property>
<property name="height-request">400</property> <property name="height-request">400</property>
<property name="modal">True</property> <property name="modal">True</property>
<child type="titlebar"> <child type="titlebar">
<object class="GtkHeaderBar"> <object class="GtkHeaderBar"/>
<property name="title" translatable="yes">Accounts</property>
<property name="show_close_button">True</property>
<property name="visible">True</property>
</object>
</child> </child>
<child internal-child="vbox"> <child internal-child="content_area">
<object class="GtkBox"> <object class="GtkBox">
<property name="visible">True</property> <property name="visible">True</property>
<child>
<object class="GtkStack" id="main_stack">
<child> <child>
<object class="GtkStack" id="main_stack"> <object class="GtkStackPage">
<property name="visible">True</property> <property name="name">accounts_exist</property>
<child> <property name="child">
<object class="GtkBox"> <object class="GtkBox">
<property name="can_focus">True</property> <property name="focusable">1</property>
<property name="expand">True</property> <property name="hexpand">True</property>
<property name="visible">True</property> <property name="vexpand">True</property>
<child> <child>
<object class="GtkBox"> <object class="GtkBox">
<property name="expand">True</property> <property name="hexpand">True</property>
<property name="orientation">horizontal</property> <property name="vexpand">True</property>
<property name="margin">15</property> <property name="margin-start">15</property>
<property name="margin-end">15</property>
<property name="margin-top">15</property>
<property name="margin-bottom">15</property>
<property name="spacing">20</property> <property name="spacing">20</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkBox"> <object class="GtkBox">
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<property name="width-request">250</property> <property name="width-request">250</property>
<property name="vexpand">True</property> <property name="vexpand">1</property>
<property name="hexpand">False</property> <property name="hexpand">0</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkScrolledWindow"> <object class="GtkScrolledWindow">
<property name="shadow-type">in</property> <!-- <property name="shadow-type">in</property>-->
<property name="expand">True</property> <property name="hexpand">1</property>
<property name="visible">True</property> <property name="vexpand">1</property>
<child> <property name="child">
<object class="GtkBox"> <object class="GtkBox">
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkListBox" id="account_list"> <object class="GtkListBox" id="account_list">
<property name="expand">True</property> <property name="hexpand">1</property>
<property name="visible">True</property> <property name="vexpand">1</property>
</object> </object>
</child> </child>
</object> </object>
</child> </property>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkToolbar"> <object class="GtkBox">
<property name="css-classes">toolbar</property>
<style> <style>
<class name="inline-toolbar"/> <class name="inline-toolbar"/>
</style> </style>
<property name="icon-size">menu</property>
<property name="toolbar-style">icons</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkToolButton" id="add_account_button"> <object class="GtkButton" id="add_account_button">
<property name="icon-name">list-add-symbolic</property> <property name="icon-name">list-add-symbolic</property>
<property name="visible">True</property>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkToolButton" id="remove_account_button"> <object class="GtkButton" id="remove_account_button">
<property name="icon-name">list-remove-symbolic</property> <property name="icon-name">list-remove-symbolic</property>
<property name="visible">True</property>
</object> </object>
</child> </child>
</object> </object>
@ -81,23 +77,19 @@
</child> </child>
<child> <child>
<object class="GtkGrid" id="settings_list"> <object class="GtkGrid" id="settings_list">
<property name="expand">True</property> <property name="hexpand">1</property>
<property name="vexpand">1</property>
<property name="column-spacing">10</property> <property name="column-spacing">10</property>
<property name="row-spacing">5</property> <property name="row-spacing">5</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkBox"> <object class="GtkBox">
<property name="visible">True</property>
<property name="halign">end</property> <property name="halign">end</property>
<child> <child>
<object class="GtkBox"> <object class="GtkBox"/>
<property name="visible">True</property>
</object>
</child> </child>
<child> <child>
<object class="GtkButton" id="image_button"> <object class="GtkButton" id="image_button">
<property name="relief">none</property> <property name="has-frame">False</property>
<property name="visible">True</property>
<style> <style>
<class name="image-button"/> <class name="image-button"/>
</style> </style>
@ -105,145 +97,124 @@
<object class="DinoUiAvatarImage" id="image"> <object class="DinoUiAvatarImage" id="image">
<property name="height">50</property> <property name="height">50</property>
<property name="width">50</property> <property name="width">50</property>
<property name="xalign">1</property> <!-- <property name="xalign">1</property>-->
<property name="visible">True</property>
<property name="allow_gray">False</property> <property name="allow_gray">False</property>
</object> </object>
</child> </child>
</object> </object>
</child> </child>
<layout>
<property name="column">0</property>
<property name="row">0</property>
<property name="row-span">2</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="width">1</property>
<property name="height">2</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkLabel" id="jid_label"> <object class="GtkLabel" id="jid_label">
<property name="xalign">0</property> <property name="xalign">0</property>
<property name="yalign">1</property> <property name="yalign">1</property>
<property name="visible">True</property>
<attributes> <attributes>
<attribute name="weight" value="PANGO_WEIGHT_BOLD"/> <attribute name="weight" value="PANGO_WEIGHT_BOLD"></attribute>
</attributes> </attributes>
<layout>
<property name="column">1</property>
<property name="row">0</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkLabel" id="state_label"> <object class="GtkLabel" id="state_label">
<property name="xalign">0</property> <property name="xalign">0</property>
<property name="yalign">0</property> <property name="yalign">0</property>
<property name="visible">True</property>
<style> <style>
<class name="dim-label"/> <class name="dim-label"/>
</style> </style>
<layout>
<property name="column">1</property>
<property name="row">1</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkSwitch" id="active_switch"> <object class="GtkSwitch" id="active_switch">
<property name="visible">True</property>
<property name="halign">end</property> <property name="halign">end</property>
<property name="valign">center</property> <property name="valign">center</property>
<layout>
<property name="column">2</property>
<property name="row">0</property>
<property name="row-span">2</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">2</property>
<property name="top_attach">0</property>
<property name="width">1</property>
<property name="height">2</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkLabel"> <object class="GtkLabel">
<property name="label" translatable="yes">Password</property> <property name="label" translatable="1">Password</property>
<property name="xalign">1</property> <property name="xalign">1</property>
<property name="visible">True</property>
<style> <style>
<class name="dim-label"/> <class name="dim-label"/>
</style> </style>
<layout>
<property name="column">0</property>
<property name="row">2</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child> </child>
<child> <child>
<object class="DinoUiUtilEntryLabelHybrid" id="password_hybrid"> <object class="DinoUiUtilEntryLabelHybrid" id="password_hybrid">
<property name="xalign">0</property> <property name="xalign">0</property>
<property name="width_request">200</property> <property name="width_request">200</property>
<property name="visibility">False</property> <property name="visibility">False</property>
<property name="visible">True</property> <layout>
<property name="column">1</property>
<property name="row">2</property>
<property name="column-span">2</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">2</property>
<property name="width">2</property>
<property name="height">1</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkLabel"> <object class="GtkLabel">
<property name="label" translatable="yes">Local alias</property> <property name="label" translatable="1">Local alias</property>
<property name="xalign">1</property> <property name="xalign">1</property>
<property name="visible">True</property>
<style> <style>
<class name="dim-label"/> <class name="dim-label"/>
</style> </style>
<layout>
<property name="column">0</property>
<property name="row">3</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">3</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child> </child>
<child> <child>
<object class="DinoUiUtilEntryLabelHybrid" id="alias_hybrid"> <object class="DinoUiUtilEntryLabelHybrid" id="alias_hybrid">
<property name="xalign">0</property> <property name="xalign">0</property>
<property name="width_request">200</property> <property name="width_request">200</property>
<property name="visible">True</property> ` <layout>
<property name="column">1</property>
<property name="row">3</property>
<property name="column-span">2</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">3</property>
<property name="width">2</property>
<property name="height">1</property>
</packing>
</child> </child>
</object> </object>
</child> </child>
</object> </object>
</child> </child>
</object> </object>
<packing> </property>
<property name="name">accounts_exist</property> </object>
</packing> </child>
</child> <child>
<child> <object class="GtkStackPage">
<property name="name">no_accounts</property>
<property name="child">
<object class="GtkBox"> <object class="GtkBox">
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<property name="spacing">10</property> <property name="spacing">10</property>
<property name="valign">center</property> <property name="valign">center</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkImage"> <object class="GtkImage">
<property name="visible">True</property>
<property name="icon-name">system-users-symbolic</property> <property name="icon-name">system-users-symbolic</property>
<property name="icon-size">4</property>
<property name="pixel-size">72</property> <property name="pixel-size">72</property>
<style> <style>
<class name="dim-label"/> <class name="dim-label"/>
@ -252,10 +223,7 @@
</child> </child>
<child> <child>
<object class="GtkLabel"> <object class="GtkLabel">
<property name="label" translatable="yes">No accounts configured</property> <property name="label" translatable="1">No accounts configured</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="visible">True</property>
<style> <style>
<class name="dim-label"/> <class name="dim-label"/>
</style> </style>
@ -263,9 +231,8 @@
</child> </child>
<child> <child>
<object class="GtkButton" id="no_accounts_add"> <object class="GtkButton" id="no_accounts_add">
<property name="label" translatable="yes">Add an account</property> <property name="label" translatable="1">Add an account</property>
<property name="halign">center</property> <property name="halign">center</property>
<property name="visible">True</property>
<style> <style>
<class name="text-button"/> <class name="text-button"/>
<class name="suggested-action"/> <class name="suggested-action"/>
@ -273,13 +240,12 @@
</object> </object>
</child> </child>
</object> </object>
<packing> </property>
<property name="name">no_accounts</property>
</packing>
</child>
</object> </object>
</child> </child>
</object> </object>
</child>
</object>
</child> </child>
</template> </template>
</interface> </interface>

View file

@ -2,32 +2,21 @@
<interface> <interface>
<requires lib="gtk+" version="3.16"/> <requires lib="gtk+" version="3.16"/>
<object class="GtkPopoverMenu" id="menu_encryption"> <object class="GtkPopoverMenu" id="menu_encryption">
<property name="can_focus">False</property> <property name="child">
<child>
<object class="GtkBox" id="encryption_box"> <object class="GtkBox" id="encryption_box">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<property name="margin">10</property> <property name="margin-start">10</property>
<property name="margin-end">10</property>
<property name="margin-top">10</property>
<property name="margin-bottom">10</property>
<child> <child>
<object class="GtkRadioButton" id="button_unencrypted"> <object class="GtkCheckButton" id="button_unencrypted">
<property name="label" translatable="yes">Unencrypted</property> <property name="label" translatable="1">Unencrypted</property>
<property name="visible">True</property> <property name="focusable">1</property>
<property name="can_focus">True</property> <property name="active">1</property>
<property name="receives_default">False</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
</object> </object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child> </child>
</object> </object>
<packing> </property>
<property name="submenu">main</property>
</packing>
</child>
</object> </object>
</interface> </interface>

View file

@ -6,27 +6,21 @@
<property name="spacing">5</property> <property name="spacing">5</property>
<property name="margin_top">5</property> <property name="margin_top">5</property>
<property name="margin_bottom">5</property> <property name="margin_bottom">5</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkFrame" id="frame"> <object class="GtkFrame" id="frame">
<property name="visible">True</property>
<style> <style>
<class name="dino-chatinput"/> <class name="dino-chatinput"/>
</style> </style>
<child> <child>
<object class="GtkBox"> <object class="GtkBox">
<property name="visible">True</property>
<child> <child>
<object class="DinoUiChatTextView" id="chat_text_view"> <object class="DinoUiChatTextView" id="chat_text_view" />
<property name="visible">True</property>
</object>
</child> </child>
<child> <child>
<object class="GtkMenuButton" id="emoji_button"> <object class="GtkMenuButton" id="emoji_button">
<property name="relief">none</property> <property name="has-frame">False</property>
<property name="margin-top">3</property> <property name="margin-top">3</property>
<property name="valign">start</property> <property name="valign">start</property>
<property name="visible">True</property>
<style> <style>
<class name="dino-chatinput-button"/> <class name="dino-chatinput-button"/>
<class name="flat"/> <class name="flat"/>
@ -35,7 +29,6 @@
<object class="GtkImage"> <object class="GtkImage">
<property name="icon-name">dino-emoticon-symbolic</property> <property name="icon-name">dino-emoticon-symbolic</property>
<property name="icon-size">1</property> <property name="icon-size">1</property>
<property name="visible">True</property>
</object> </object>
</child> </child>
</object> </object>
@ -48,17 +41,14 @@
<object class="GtkBox"> <object class="GtkBox">
<property name="spacing">5</property> <property name="spacing">5</property>
<property name="halign">end</property> <property name="halign">end</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkButton" id="cancel_button"> <object class="GtkButton" id="cancel_button">
<property name="label" translatable="yes">Cancel</property> <property name="label" translatable="yes">Cancel</property>
<property name="visible">True</property>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkButton" id="send_button"> <object class="GtkButton" id="send_button">
<property name="label" translatable="yes">Update message</property> <property name="label" translatable="yes">Update message</property>
<property name="visible">True</property>
<style> <style>
<class name="suggested-action"/> <class name="suggested-action"/>
</style> </style>

View file

@ -2,30 +2,23 @@
<interface> <interface>
<requires lib="gtk+" version="3.22"/> <requires lib="gtk+" version="3.22"/>
<template class="DinoUiOccupantMenuList"> <template class="DinoUiOccupantMenuList">
<property name="visible">True</property>
<child> <child>
<object class="GtkBox"> <object class="GtkBox">
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkToolItem"> <object class="GtkSearchEntry" id="search_entry">
<property name="visible">True</property> <property name="margin_top">8</property>
<child> <property name="margin_bottom">8</property>
<object class="GtkSearchEntry" id="search_entry"> <property name="margin_start">8</property>
<property name="margin">8</property> <property name="margin_end">8</property>
<property name="visible">True</property>
</object>
</child>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkScrolledWindow"> <object class="GtkScrolledWindow">
<property name="max_content_height">500</property> <property name="max_content_height">500</property>
<property name="propagate_natural_height">True</property> <property name="propagate_natural_height">True</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkListBox" id="list_box"> <object class="GtkListBox" id="list_box">
<property name="visible">True</property>
</object> </object>
</child> </child>
</object> </object>

View file

@ -1,45 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<interface> <interface>
<template class="DinoUiOccupantMenuListRow" parent="GtkListBoxRow"> <requires lib="gtk" version="4.0"/>
<property name="visible">True</property> <object class="GtkGrid" id="main_grid">
<property name="margin-top">3</property>
<property name="margin-start">7</property>
<property name="margin-bottom">3</property>
<property name="margin-end">7</property>
<property name="column-spacing">10</property>
<child> <child>
<object class="GtkGrid"> <object class="DinoUiAvatarImage" id="image">
<property name="orientation">horizontal</property> <property name="height">30</property>
<property name="margin-top">3</property> <property name="width">30</property>
<property name="margin-left">7</property>
<property name="margin-bottom">3</property>
<property name="margin-right">7</property>
<property name="column-spacing">10</property>
<property name="visible">True</property>
<child>
<object class="DinoUiAvatarImage" id="image">
<property name="height">30</property>
<property name="width">30</property>
<property name="visible">True</property>
</object>
</child>
<child>
<object class="GtkGrid">
<property name="orientation">vertical</property>
<property name="visible">True</property>
<child>
<object class="GtkLabel" id="name_label">
<property name="max_width_chars">1</property>
<property name="ellipsize">end</property>
<property name="expand">True</property>
<property name="xalign">0</property>
<property name="visible">True</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child>
</object>
</child>
</object> </object>
</child> </child>
</template> <child>
<object class="GtkLabel" id="name_label">
<property name="max_width_chars">1</property>
<property name="ellipsize">end</property>
<property name="hexpand">1</property>
<property name="xalign">0</property>
<layout>
<property name="column">1</property>
<property name="row">0</property>
</layout>
</object>
</child>
</object>
</interface> </interface>

View file

@ -5,18 +5,17 @@
<property name="visible">True</property> <property name="visible">True</property>
<child> <child>
<object class="DinoUiAvatarImage" id="image"> <object class="DinoUiAvatarImage" id="image">
<property name="margin">4</property> <property name="margin-top">4</property>
<property name="margin-bottom">4</property>
<property name="margin-start">6</property> <property name="margin-start">6</property>
<property name="margin-end">6</property> <property name="margin-end">6</property>
<property name="height">24</property> <property name="height">24</property>
<property name="width">24</property> <property name="width">24</property>
<property name="visible">True</property>
<property name="allow_gray">False</property> <property name="allow_gray">False</property>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkLabel" id="label"> <object class="GtkLabel" id="label">
<property name="visible">True</property>
<property name="ellipsize">end</property> <property name="ellipsize">end</property>
</object> </object>
</child> </child>

View file

@ -1,81 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<interface> <interface>
<template class="DinoUiSettingsDialog"> <template class="DinoUiSettingsDialog">
<property name="title" translatable="yes">Settings</property>
<property name="modal">True</property> <property name="modal">True</property>
<child type="titlebar"> <child type="titlebar">
<object class="GtkHeaderBar"> <object class="GtkHeaderBar">
<property name="title" translatable="yes">Settings</property>
<property name="show_close_button">True</property>
<property name="visible">True</property>
</object> </object>
</child> </child>
<child internal-child="vbox"> <child internal-child="content_area">
<object class="GtkBox"> <object class="GtkBox">
<property name="visible">True</property>
<child> <child>
<object class="GtkGrid"> <object class="GtkGrid">
<property name="margin">10</property> <property name="margin_top">10</property>
<property name="margin_bottom">10</property>
<property name="margin_start">10</property>
<property name="margin_end">10</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="row-spacing">10</property> <property name="row-spacing">10</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkCheckButton" id="typing_checkbutton"> <object class="GtkCheckButton" id="typing_checkbutton">
<property name="label" translatable="yes">Send typing notifications</property> <property name="label" translatable="yes">Send typing notifications</property>
<property name="visible">True</property> <layout>
<property name="column">0</property>
<property name="row">0</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkCheckButton" id="marker_checkbutton"> <object class="GtkCheckButton" id="marker_checkbutton">
<property name="label" translatable="yes">Send read receipts</property> <property name="label" translatable="yes">Send read receipts</property>
<property name="visible">True</property> <layout>
<property name="column">0</property>
<property name="row">1</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkCheckButton" id="notification_checkbutton"> <object class="GtkCheckButton" id="notification_checkbutton">
<property name="label" translatable="yes">Notify when a new message arrives</property> <property name="label" translatable="yes">Notify when a new message arrives</property>
<property name="visible">True</property> <layout>
<property name="column">0</property>
<property name="row">2</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkCheckButton" id="emoji_checkbutton"> <object class="GtkCheckButton" id="emoji_checkbutton">
<property name="label" translatable="yes">Convert smileys to emojis</property> <property name="label" translatable="yes">Convert smileys to emojis</property>
<property name="visible">True</property> <layout>
<property name="column">0</property>
<property name="row">3</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">3</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkCheckButton" id="check_spelling_checkbutton"> <object class="GtkCheckButton" id="check_spelling_checkbutton">
<property name="label" translatable="yes">Check spelling</property> <property name="label" translatable="yes">Check spelling</property>
<property name="visible">True</property> <layout>
<property name="column">0</property>
<property name="row">4</property>
</layout>
</object> </object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">4</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child> </child>
</object> </object>
</child> </child>

View file

@ -27,8 +27,8 @@ window.dino-main .dino-conversation viewport /* Some themes set this */ {
} }
@keyframes highlight { @keyframes highlight {
from { background: alpha(@warning_color, 0.5) } from { background: alpha(@warning_color, 0.5); }
to { background: transparent } to { background: transparent; }
} }
window.dino-main .dino-conversation .highlight-once { window.dino-main .dino-conversation .highlight-once {
@ -38,10 +38,23 @@ window.dino-main .dino-conversation .highlight-once {
animation-name: highlight; animation-name: highlight;
} }
window.dino-main .dino-conversation .message-box:hover { window.dino-main .dino-conversation .message-box.highlight {
background: alpha(@theme_fg_color, 0.04); background: alpha(@theme_fg_color, 0.04);
} }
window.dino-main .dino-conversation .message-box {
padding: 4px 15px 4px 15px;
}
window.dino-main .dino-conversation .message-box:not(.has-skeleton) {
padding-left: 58px;
}
window.dino-main .dino-conversation .message-box.has-skeleton.last-group-item {
padding-top: 8px;
padding-bottom: 8px;
}
window.dino-main .unread-count-notify { window.dino-main .unread-count-notify {
background-color: alpha(@theme_fg_color, 0.8); background-color: alpha(@theme_fg_color, 0.8);
color: @theme_base_color; color: @theme_base_color;
@ -142,7 +155,7 @@ window.dino-main .incoming-call-box {
window.dino-main .multiparty-participants { window.dino-main .multiparty-participants {
border-top: 1px solid alpha(@theme_fg_color, 0.05); border-top: 1px solid alpha(@theme_fg_color, 0.05);
background: alpha(@theme_fg_color, 0.04) background: alpha(@theme_fg_color, 0.04);
} }
window.dino-main .dino-sidebar > frame.collapsed { window.dino-main .dino-sidebar > frame.collapsed {
@ -171,34 +184,40 @@ window.dino-main .dino-chatinput frame box {
background: transparent; background: transparent;
} }
window.dino-main button.dino-attach-button { window.dino-main .dino-attach-button {
min-width: 24px; /* Make button the same with as avatars */ min-width: 24px; /* Make button the same width as avatars */
} }
window.dino-main button.dino-chatinput-button { window.dino-main .dino-attach-button,
window.dino-main .dino-chatinput-button button {
border: none; border: none;
background: transparent; background: transparent;
box-shadow: none; box-shadow: none;
min-height: 0; min-height: 0;
padding: 7px 5px; padding: 7px 5px;
color: alpha(@theme_fg_color, 0.6); color: alpha(@theme_fg_color, 0.7);
outline: none; outline: none;
} }
window.dino-main button.dino-chatinput-button:hover { window.dino-main .dino-attach-button:hover,
window.dino-main .dino-chatinput-button button:hover {
color: @theme_selected_bg_color; color: @theme_selected_bg_color;
} }
window.dino-main button.dino-chatinput-button:backdrop { window.dino-main .dino-attach-button:backdrop,
window.dino-main .dino-chatinput-button button:backdrop {
color: alpha(@theme_unfocused_fg_color, 0.6); color: alpha(@theme_unfocused_fg_color, 0.6);
} }
window.dino-main button.dino-chatinput-button:active, window.dino-main .dino-attach-button:active,
window.dino-main button.dino-chatinput-button:checked { window.dino-main .dino-attach-button:checked,
window.dino-main .dino-chatinput-button button:active,
window.dino-main .dino-chatinput-button button:checked {
color: alpha(@theme_selected_bg_color, 0.8); color: alpha(@theme_selected_bg_color, 0.8);
} }
window.dino-main button.dino-chatinput-button:checked:backdrop { window.dino-main .dino-attach-button:backdrop,
window.dino-main .dino-chatinput-button button:checked:backdrop {
color: alpha(@theme_unfocused_selected_bg_color, 0.8); color: alpha(@theme_unfocused_selected_bg_color, 0.8);
} }
@ -357,7 +376,7 @@ box.dino-input-error label.input-status-highlight-once {
.dino-call-window .participant-name { .dino-call-window .participant-name {
color: white; color: white;
text-shadow: black; text-shadow: 1px 1px 2px black;
} }
.dino-call-window .text-no-controls { .dino-call-window .text-no-controls {

View file

@ -1,94 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<interface> <interface>
<requires lib="gtk" version="4.0"/>
<object class="GtkPaned" id="paned"> <object class="GtkPaned" id="paned">
<property name="shrink-start-child">False</property>
<property name="shrink-end-child">False</property>
<property name="resize-start-child">False</property>
<property name="position">300</property> <property name="position">300</property>
<property name="orientation">horizontal</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkStack" id="left_stack"> <object class="GtkStack" id="left_stack">
<property name="visible">True</property>
<child> <child>
<object class="GtkScrolledWindow" id="scrolled"> <object class="GtkStackPage">
<property name="expand">True</property>
<property name="hscrollbar_policy">never</property>
<property name="visible">True</property>
<child>
<object class="DinoUiConversationSelector" id="conversation_list">
<property name="visible">True</property>
</object>
</child>
</object>
<packing>
<property name="name">content</property> <property name="name">content</property>
</packing> <property name="child">
</child> <object class="GtkScrolledWindow">
<child> <property name="hscrollbar_policy">never</property>
<object class="GtkBox">
<property name="margin">20</property>
<property name="spacing">10</property>
<property name="valign">start</property>
<property name="halign">start</property>
<property name="visible">True</property>
<child>
<object class="GtkImage" id="conversation_list_placeholder_image">
<property name="visible">True</property>
<property name="valign">start</property>
<style>
<class name="dim-label"/>
</style>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="wrap">True</property>
<property name="margin-top">70</property>
<property name="margin-right">50</property>
<property name="visible">True</property>
<property name="valign">end</property>
<property name="label" translatable="yes">Click here to start a conversation or join a channel.</property>
<style>
<class name="dim-label"/>
</style>
</object>
</child>
</object>
<packing>
<property name="name">placeholder</property>
</packing>
</child>
</object>
<packing>
<property name="resize">False</property>
<property name="shrink">False</property>
</packing>
</child>
<child>
<object class="GtkOverlay">
<property name="visible">True</property>
<child>
<object class="GtkStack" id="right_stack">
<property name="visible">True</property>
<child>
<object class="DinoUiConversationView" id="conversation_view">
<property name="visible">True</property>
</object>
<packing>
<property name="name">content</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="expand">True</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkImage"> <object class="DinoUiConversationSelector" id="conversation_list">
<property name="icon-name">im.dino.Dino-symbolic</property> </object>
<property name="pixel-size">144</property> </child>
<property name="margin-bottom">30</property> </object>
<property name="visible">True</property> </property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">placeholder</property>
<property name="child">
<object class="GtkBox">
<property name="margin-start">20</property>
<property name="margin-end">20</property>
<property name="margin-top">20</property>
<property name="margin-bottom">20</property>
<property name="spacing">10</property>
<property name="valign">start</property>
<property name="halign">start</property>
<child>
<object class="GtkImage" id="conversation_list_placeholder_image">
<property name="valign">start</property>
<style> <style>
<class name="dim-label"/> <class name="dim-label"/>
</style> </style>
@ -96,52 +44,87 @@
</child> </child>
<child> <child>
<object class="GtkLabel"> <object class="GtkLabel">
<property name="visible">True</property> <property name="wrap">1</property>
<property name="label" translatable="yes">You have no open chats</property> <property name="margin-top">70</property>
<property name="xalign">0.5</property> <property name="margin-end">50</property>
<property name="yalign">0.5</property> <property name="valign">end</property>
<property name="label" translatable="1">Click here to start a conversation or join a channel.</property>
<style> <style>
<class name="dim-label"/> <class name="dim-label"/>
</style> </style>
<attributes>
<attribute name="scale" value="1.2"/>
</attributes>
</object> </object>
</child> </child>
</object> </object>
<packing> </property>
<property name="name">placeholder</property>
</packing>
</child>
</object> </object>
</child> </child>
</object>
</child>
<child>
<object class="GtkOverlay">
<property name="child">
<object class="GtkStack" id="right_stack">
<child>
<object class="GtkStackPage">
<property name="name">content</property>
<property name="child">
<object class="DinoUiConversationView" id="conversation_view">
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">placeholder</property>
<property name="child">
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="hexpand">1</property>
<property name="vexpand">1</property>
<property name="halign">center</property>
<property name="valign">center</property>
<child>
<object class="GtkImage">
<property name="icon-name">im.dino.Dino-symbolic</property>
<property name="pixel-size">144</property>
<property name="margin-bottom">30</property>
<style>
<class name="dim-label"/>
</style>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label" translatable="1">You have no open chats</property>
<style>
<class name="dim-label"/>
</style>
<attributes>
<attribute name="scale" value="1.2"></attribute>
</attributes>
</object>
</child>
</object>
</property>
</object>
</child>
</object>
</property>
<child type="overlay"> <child type="overlay">
<object class="GtkRevealer" id="search_revealer"> <object class="GtkRevealer" id="search_revealer">
<property name="visible">True</property>
<property name="halign">end</property> <property name="halign">end</property>
<property name="transition-type">slide-left</property> <property name="transition-type">slide-left</property>
<style> <style>
<class name="dino-sidebar"/> <class name="dino-sidebar"/>
</style> </style>
<child> <property name="child">
<object class="GtkFrame"> <object class="GtkFrame" id="search_frame">
<property name="visible">True</property>
<property name="width-request">400</property> <property name="width-request">400</property>
<property name="shadow-type">none</property>
<child>
<object class="DinoUiGlobalSearch" id="search_box">
<property name="visible">True</property>
</object>
</child>
</object> </object>
</child> </property>
</object> </object>
</child> </child>
</object> </object>
<packing>
<property name="resize">True</property>
<property name="shrink">False</property>
</packing>
</child> </child>
</object> </object>
</interface> </interface>

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<interface> <interface>
<requires lib="gtk" version="4.0"/>
<template class="DinoUiMainWindowPlaceholder"> <template class="DinoUiMainWindowPlaceholder">
<property name="valign">center</property> <property name="valign">center</property>
<property name="visible">True</property> <property name="visible">True</property>
@ -8,14 +9,15 @@
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<property name="valign">center</property> <property name="valign">center</property>
<property name="halign">center</property> <property name="halign">center</property>
<property name="hexpand">True</property> <property name="hexpand">1</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkImage"> <object class="GtkImage">
<property name="icon-name">im.dino.Dino-symbolic</property> <property name="icon-name">im.dino.Dino-symbolic</property>
<property name="pixel-size">144</property> <property name="pixel-size">144</property>
<property name="margin">30</property> <property name="margin-start">30</property>
<property name="visible">True</property> <property name="margin-end">30</property>
<property name="margin-top">30</property>
<property name="margin-bottom">30</property>
<style> <style>
<class name="dim-label"/> <class name="dim-label"/>
</style> </style>
@ -23,22 +25,18 @@
</child> </child>
<child> <child>
<object class="GtkLabel" id="title_label"> <object class="GtkLabel" id="title_label">
<property name="xalign">0.5</property> <property name="visible">0</property>
<property name="yalign">0.5</property>
<style> <style>
<class name="dim-label"/> <class name="dim-label"/>
</style> </style>
<attributes> <attributes>
<attribute name="scale" value="1.3"/> <attribute name="scale" value="1.3"></attribute>
</attributes> </attributes>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkLabel" id="label"> <object class="GtkLabel" id="label">
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="margin-top">5</property> <property name="margin-top">5</property>
<property name="visible">True</property>
<style> <style>
<class name="dim-label"/> <class name="dim-label"/>
</style> </style>
@ -48,7 +46,6 @@
<object class="GtkButton" id="primary_button"> <object class="GtkButton" id="primary_button">
<property name="margin-top">15</property> <property name="margin-top">15</property>
<property name="halign">center</property> <property name="halign">center</property>
<property name="visible">True</property>
<style> <style>
<class name="text-button"/> <class name="text-button"/>
<class name="suggested-action"/> <class name="suggested-action"/>
@ -57,8 +54,9 @@
</child> </child>
<child> <child>
<object class="GtkButton" id="secondary_button"> <object class="GtkButton" id="secondary_button">
<property name="visible">0</property>
<property name="halign">center</property> <property name="halign">center</property>
<property name="no_show_all">True</property> <!-- <property name="no_show_all">True</property>-->
<style> <style>
<class name="text-button"/> <class name="text-button"/>
</style> </style>

View file

@ -1,823 +0,0 @@
/* gtkemojichooser.c: An Emoji chooser widget
* Copyright 2017, Red Hat, Inc.
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "emojichooser.h"
#define BOX_SPACE 6
typedef struct {
GtkWidget *box;
GtkWidget *heading;
GtkWidget *button;
const char *first;
gunichar label;
gboolean empty;
} EmojiSection;
struct _DinoEmojiChooser
{
GtkPopover parent_instance;
GtkWidget *search_entry;
GtkWidget *stack;
GtkWidget *scrolled_window;
int emoji_max_width;
EmojiSection recent;
EmojiSection people;
EmojiSection body;
EmojiSection nature;
EmojiSection food;
EmojiSection travel;
EmojiSection activities;
EmojiSection objects;
EmojiSection symbols;
EmojiSection flags;
GtkGesture *recent_long_press;
GtkGesture *recent_multi_press;
GtkGesture *people_long_press;
GtkGesture *people_multi_press;
GtkGesture *body_long_press;
GtkGesture *body_multi_press;
GVariant *data;
GtkWidget *box;
GVariantIter *iter;
guint populate_idle;
GSettings *settings;
};
struct _DinoEmojiChooserClass {
GtkPopoverClass parent_class;
gboolean (* popover_button_release_event) (GtkWidget *widget,
GdkEventButton *event);
};
enum {
EMOJI_PICKED,
LAST_SIGNAL
};
static int signals[LAST_SIGNAL];
G_DEFINE_TYPE (DinoEmojiChooser, dino_emoji_chooser, GTK_TYPE_POPOVER)
static void
dino_emoji_chooser_finalize (GObject *object)
{
DinoEmojiChooser *chooser = DINO_EMOJI_CHOOSER (object);
if (chooser->populate_idle)
g_source_remove (chooser->populate_idle);
g_variant_unref (chooser->data);
g_object_unref (chooser->settings);
g_clear_object (&chooser->recent_long_press);
g_clear_object (&chooser->recent_multi_press);
g_clear_object (&chooser->people_long_press);
g_clear_object (&chooser->people_multi_press);
g_clear_object (&chooser->body_long_press);
g_clear_object (&chooser->body_multi_press);
G_OBJECT_CLASS (dino_emoji_chooser_parent_class)->finalize (object);
}
static void
scroll_to_section (GtkButton *button,
gpointer data)
{
EmojiSection *section = data;
DinoEmojiChooser *chooser;
GtkAdjustment *adj;
GtkAllocation alloc = { 0, 0, 0, 0 };
chooser = DINO_EMOJI_CHOOSER (gtk_widget_get_ancestor (GTK_WIDGET (button), GTK_TYPE_EMOJI_CHOOSER));
adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (chooser->scrolled_window));
if (section->heading)
gtk_widget_get_allocation (section->heading, &alloc);
gtk_adjustment_set_value (adj, alloc.y - BOX_SPACE);
}
static void
add_emoji (GtkWidget *box,
gboolean prepend,
GVariant *item,
gunichar modifier,
DinoEmojiChooser *chooser);
#define MAX_RECENT (7*3)
static void
populate_recent_section (DinoEmojiChooser *chooser)
{
GVariant *variant;
GVariant *item;
GVariantIter iter;
gboolean empty = FALSE;
variant = g_settings_get_value (chooser->settings, "recent-emoji");
g_variant_iter_init (&iter, variant);
while ((item = g_variant_iter_next_value (&iter)))
{
GVariant *emoji_data;
gunichar modifier;
emoji_data = g_variant_get_child_value (item, 0);
g_variant_get_child (item, 1, "u", &modifier);
add_emoji (chooser->recent.box, FALSE, emoji_data, modifier, chooser);
g_variant_unref (emoji_data);
g_variant_unref (item);
empty = FALSE;
}
if (!empty)
{
gtk_widget_show (chooser->recent.box);
gtk_widget_set_sensitive (chooser->recent.button, TRUE);
}
g_variant_unref (variant);
}
static void
add_recent_item (DinoEmojiChooser *chooser,
GVariant *item,
gunichar modifier)
{
GList *children, *l;
int i;
GVariantBuilder builder;
g_variant_ref (item);
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a((auss)u)"));
g_variant_builder_add (&builder, "(@(auss)u)", item, modifier);
children = gtk_container_get_children (GTK_CONTAINER (chooser->recent.box));
for (l = children, i = 1; l; l = l->next, i++)
{
GVariant *item2 = g_object_get_data (G_OBJECT (l->data), "emoji-data");
gunichar modifier2 = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (l->data), "modifier"));
if (modifier == modifier2 && g_variant_equal (item, item2))
{
gtk_widget_destroy (GTK_WIDGET (l->data));
i--;
continue;
}
if (i >= MAX_RECENT)
{
gtk_widget_destroy (GTK_WIDGET (l->data));
continue;
}
g_variant_builder_add (&builder, "(@(auss)u)", item2, modifier2);
}
g_list_free (children);
add_emoji (chooser->recent.box, TRUE, item, modifier, chooser);
/* Enable recent */
gtk_widget_show (chooser->recent.box);
gtk_widget_set_sensitive (chooser->recent.button, TRUE);
g_settings_set_value (chooser->settings, "recent-emoji", g_variant_builder_end (&builder));
g_variant_unref (item);
}
static void
emoji_activated (GtkFlowBox *box,
GtkFlowBoxChild *child,
gpointer data)
{
DinoEmojiChooser *chooser = data;
char *text;
GtkWidget *ebox;
GtkWidget *label;
GVariant *item;
gunichar modifier;
gtk_popover_popdown (GTK_POPOVER (chooser));
ebox = gtk_bin_get_child (GTK_BIN (child));
label = gtk_bin_get_child (GTK_BIN (ebox));
text = g_strdup (gtk_label_get_label (GTK_LABEL (label)));
item = (GVariant*) g_object_get_data (G_OBJECT (child), "emoji-data");
modifier = (gunichar) GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (child), "modifier"));
add_recent_item (chooser, item, modifier);
g_signal_emit (data, signals[EMOJI_PICKED], 0, text);
g_free (text);
}
static gboolean
has_variations (GVariant *emoji_data)
{
GVariant *codes;
gsize i;
gboolean has_variations;
has_variations = FALSE;
codes = g_variant_get_child_value (emoji_data, 0);
for (i = 0; i < g_variant_n_children (codes); i++)
{
gunichar code;
g_variant_get_child (codes, i, "u", &code);
if (code == 0)
{
has_variations = TRUE;
break;
}
}
g_variant_unref (codes);
return has_variations;
}
static void
show_variations (DinoEmojiChooser *chooser,
GtkWidget *child)
{
GtkWidget *popover;
GtkWidget *view;
GtkWidget *box;
GVariant *emoji_data;
GtkWidget *parent_popover;
gunichar modifier;
if (!child)
return;
emoji_data = (GVariant*) g_object_get_data (G_OBJECT (child), "emoji-data");
if (!emoji_data)
return;
if (!has_variations (emoji_data))
return;
parent_popover = gtk_widget_get_ancestor (child, GTK_TYPE_POPOVER);
popover = gtk_popover_new (child);
view = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_style_context_add_class (gtk_widget_get_style_context (view), "view");
box = gtk_flow_box_new ();
gtk_flow_box_set_homogeneous (GTK_FLOW_BOX (box), TRUE);
gtk_flow_box_set_min_children_per_line (GTK_FLOW_BOX (box), 6);
gtk_flow_box_set_max_children_per_line (GTK_FLOW_BOX (box), 6);
gtk_flow_box_set_activate_on_single_click (GTK_FLOW_BOX (box), TRUE);
gtk_flow_box_set_selection_mode (GTK_FLOW_BOX (box), GTK_SELECTION_NONE);
gtk_container_add (GTK_CONTAINER (popover), view);
gtk_container_add (GTK_CONTAINER (view), box);
g_signal_connect (box, "child-activated", G_CALLBACK (emoji_activated), parent_popover);
add_emoji (box, FALSE, emoji_data, 0, chooser);
for (modifier = 0x1f3fb; modifier <= 0x1f3ff; modifier++)
add_emoji (box, FALSE, emoji_data, modifier, chooser);
gtk_widget_show_all (view);
gtk_popover_popup (GTK_POPOVER (popover));
}
static void
update_hover (GtkWidget *widget,
GdkEvent *event,
gpointer data)
{
if (event->type == GDK_ENTER_NOTIFY)
gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_PRELIGHT, FALSE);
else
gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_PRELIGHT);
}
static void
long_pressed_cb (GtkGesture *gesture,
double x,
double y,
gpointer data)
{
DinoEmojiChooser *chooser = data;
GtkWidget *box;
GtkWidget *child;
box = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
child = GTK_WIDGET (gtk_flow_box_get_child_at_pos (GTK_FLOW_BOX (box), x, y));
show_variations (chooser, child);
}
static void
pressed_cb (GtkGesture *gesture,
int n_press,
double x,
double y,
gpointer data)
{
DinoEmojiChooser *chooser = data;
GtkWidget *box;
GtkWidget *child;
box = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
child = GTK_WIDGET (gtk_flow_box_get_child_at_pos (GTK_FLOW_BOX (box), x, y));
show_variations (chooser, child);
}
static gboolean
popup_menu (GtkWidget *widget,
gpointer data)
{
DinoEmojiChooser *chooser = data;
show_variations (chooser, widget);
return TRUE;
}
static void
add_emoji (GtkWidget *box,
gboolean prepend,
GVariant *item,
gunichar modifier,
DinoEmojiChooser *chooser)
{
GtkWidget *child;
GtkWidget *ebox;
GtkWidget *label;
PangoAttrList *attrs;
GVariant *codes;
char text[64];
char *p = text;
gsize i;
PangoLayout *layout;
PangoRectangle rect;
codes = g_variant_get_child_value (item, 0);
for (i = 0; i < g_variant_n_children (codes); i++)
{
gunichar code;
g_variant_get_child (codes, i, "u", &code);
if (code == 0)
code = modifier;
if (code != 0)
p += g_unichar_to_utf8 (code, p);
}
g_variant_unref (codes);
p += g_unichar_to_utf8 (0xFE0F, p); /* U+FE0F is the Emoji variation selector */
p[0] = 0;
label = gtk_label_new (text);
attrs = pango_attr_list_new ();
pango_attr_list_insert (attrs, pango_attr_scale_new (PANGO_SCALE_X_LARGE));
gtk_label_set_attributes (GTK_LABEL (label), attrs);
pango_attr_list_unref (attrs);
layout = gtk_label_get_layout (GTK_LABEL (label));
pango_layout_get_extents (layout, &rect, NULL);
/* Check for fallback rendering that generates too wide items */
if (pango_layout_get_unknown_glyphs_count (layout) > 0 ||
rect.width >= 1.5 * chooser->emoji_max_width)
{
gtk_widget_destroy (label);
return;
}
child = gtk_flow_box_child_new ();
gtk_style_context_add_class (gtk_widget_get_style_context (child), "emoji");
g_object_set_data_full (G_OBJECT (child), "emoji-data",
g_variant_ref (item),
(GDestroyNotify)g_variant_unref);
if (modifier != 0)
g_object_set_data (G_OBJECT (child), "modifier", GUINT_TO_POINTER (modifier));
ebox = gtk_event_box_new ();
gtk_widget_add_events (ebox, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
g_signal_connect (ebox, "enter-notify-event", G_CALLBACK (update_hover), FALSE);
g_signal_connect (ebox, "leave-notify-event", G_CALLBACK (update_hover), FALSE);
gtk_container_add (GTK_CONTAINER (child), ebox);
gtk_container_add (GTK_CONTAINER (ebox), label);
gtk_widget_show_all (child);
if (chooser)
g_signal_connect (child, "popup-menu", G_CALLBACK (popup_menu), chooser);
gtk_flow_box_insert (GTK_FLOW_BOX (box), child, prepend ? 0 : -1);
}
static gboolean
populate_emoji_chooser (gpointer data)
{
DinoEmojiChooser *chooser = data;
GBytes *bytes = NULL;
GVariant *item;
guint64 start, now;
start = g_get_monotonic_time ();
if (!chooser->data)
{
bytes = g_resources_lookup_data ("/org/gtk/libgtk/emoji/emoji.data", 0, NULL);
if (bytes == NULL) {
bytes = g_resources_lookup_data ("/org/gtk/libgtk/emoji/en.data", 0, NULL);
}
chooser->data = g_variant_ref_sink (g_variant_new_from_bytes (G_VARIANT_TYPE ("a(auss)"), bytes, TRUE));
}
if (!chooser->iter)
{
chooser->iter = g_variant_iter_new (chooser->data);
chooser->box = chooser->people.box;
}
while ((item = g_variant_iter_next_value (chooser->iter)))
{
const char *name;
g_variant_get_child (item, 1, "&s", &name);
if (g_strcmp0 (name, chooser->body.first) == 0)
chooser->box = chooser->body.box;
else if (g_strcmp0 (name, chooser->nature.first) == 0)
chooser->box = chooser->nature.box;
else if (g_strcmp0 (name, chooser->food.first) == 0)
chooser->box = chooser->food.box;
else if (g_strcmp0 (name, chooser->travel.first) == 0)
chooser->box = chooser->travel.box;
else if (g_strcmp0 (name, chooser->activities.first) == 0)
chooser->box = chooser->activities.box;
else if (g_strcmp0 (name, chooser->objects.first) == 0)
chooser->box = chooser->objects.box;
else if (g_strcmp0 (name, chooser->symbols.first) == 0)
chooser->box = chooser->symbols.box;
else if (g_strcmp0 (name, chooser->flags.first) == 0)
chooser->box = chooser->flags.box;
add_emoji (chooser->box, FALSE, item, 0, chooser);
g_variant_unref (item);
now = g_get_monotonic_time ();
if (now > start + 8000)
return G_SOURCE_CONTINUE;
}
/* We scroll to the top on show, so check the right button for the 1st time */
gtk_widget_set_state_flags (chooser->recent.button, GTK_STATE_FLAG_CHECKED, FALSE);
g_variant_iter_free (chooser->iter);
chooser->iter = NULL;
chooser->box = NULL;
chooser->populate_idle = 0;
return G_SOURCE_REMOVE;
}
static void
adj_value_changed (GtkAdjustment *adj,
gpointer data)
{
DinoEmojiChooser *chooser = data;
double value = gtk_adjustment_get_value (adj);
EmojiSection const *sections[] = {
&chooser->recent,
&chooser->people,
&chooser->body,
&chooser->nature,
&chooser->food,
&chooser->travel,
&chooser->activities,
&chooser->objects,
&chooser->symbols,
&chooser->flags,
};
EmojiSection const *select_section = sections[0];
gsize i;
/* Figure out which section the current scroll position is within */
for (i = 0; i < G_N_ELEMENTS (sections); ++i)
{
EmojiSection const *section = sections[i];
GtkAllocation alloc;
if (section->heading)
gtk_widget_get_allocation (section->heading, &alloc);
else
gtk_widget_get_allocation (section->box, &alloc);
if (value < alloc.y - BOX_SPACE)
break;
select_section = section;
}
/* Un/Check the section buttons accordingly */
for (i = 0; i < G_N_ELEMENTS (sections); ++i)
{
EmojiSection const *section = sections[i];
if (section == select_section)
gtk_widget_set_state_flags (section->button, GTK_STATE_FLAG_CHECKED, FALSE);
else
gtk_widget_unset_state_flags (section->button, GTK_STATE_FLAG_CHECKED);
}
}
static gboolean
filter_func (GtkFlowBoxChild *child,
gpointer data)
{
EmojiSection *section = data;
DinoEmojiChooser *chooser;
GVariant *emoji_data;
const char *text;
const char *name;
gboolean res;
res = TRUE;
chooser = DINO_EMOJI_CHOOSER (gtk_widget_get_ancestor (GTK_WIDGET (child), GTK_TYPE_EMOJI_CHOOSER));
text = gtk_entry_get_text (GTK_ENTRY (chooser->search_entry));
emoji_data = (GVariant *) g_object_get_data (G_OBJECT (child), "emoji-data");
if (text[0] == 0)
goto out;
if (!emoji_data)
goto out;
g_variant_get_child (emoji_data, 1, "&s", &name);
res = g_str_match_string (text, name, TRUE);
out:
if (res)
section->empty = FALSE;
return res;
}
static void
invalidate_section (EmojiSection *section)
{
section->empty = TRUE;
gtk_flow_box_invalidate_filter (GTK_FLOW_BOX (section->box));
}
static void
update_headings (DinoEmojiChooser *chooser)
{
gtk_widget_set_visible (chooser->people.heading, !chooser->people.empty);
gtk_widget_set_visible (chooser->people.box, !chooser->people.empty);
gtk_widget_set_visible (chooser->body.heading, !chooser->body.empty);
gtk_widget_set_visible (chooser->body.box, !chooser->body.empty);
gtk_widget_set_visible (chooser->nature.heading, !chooser->nature.empty);
gtk_widget_set_visible (chooser->nature.box, !chooser->nature.empty);
gtk_widget_set_visible (chooser->food.heading, !chooser->food.empty);
gtk_widget_set_visible (chooser->food.box, !chooser->food.empty);
gtk_widget_set_visible (chooser->travel.heading, !chooser->travel.empty);
gtk_widget_set_visible (chooser->travel.box, !chooser->travel.empty);
gtk_widget_set_visible (chooser->activities.heading, !chooser->activities.empty);
gtk_widget_set_visible (chooser->activities.box, !chooser->activities.empty);
gtk_widget_set_visible (chooser->objects.heading, !chooser->objects.empty);
gtk_widget_set_visible (chooser->objects.box, !chooser->objects.empty);
gtk_widget_set_visible (chooser->symbols.heading, !chooser->symbols.empty);
gtk_widget_set_visible (chooser->symbols.box, !chooser->symbols.empty);
gtk_widget_set_visible (chooser->flags.heading, !chooser->flags.empty);
gtk_widget_set_visible (chooser->flags.box, !chooser->flags.empty);
if (chooser->recent.empty && chooser->people.empty &&
chooser->body.empty && chooser->nature.empty &&
chooser->food.empty && chooser->travel.empty &&
chooser->activities.empty && chooser->objects.empty &&
chooser->symbols.empty && chooser->flags.empty)
gtk_stack_set_visible_child_name (GTK_STACK (chooser->stack), "empty");
else
gtk_stack_set_visible_child_name (GTK_STACK (chooser->stack), "list");
}
static void
search_changed (GtkEntry *entry,
gpointer data)
{
DinoEmojiChooser *chooser = data;
invalidate_section (&chooser->recent);
invalidate_section (&chooser->people);
invalidate_section (&chooser->body);
invalidate_section (&chooser->nature);
invalidate_section (&chooser->food);
invalidate_section (&chooser->travel);
invalidate_section (&chooser->activities);
invalidate_section (&chooser->objects);
invalidate_section (&chooser->symbols);
invalidate_section (&chooser->flags);
update_headings (chooser);
}
static void
setup_section (DinoEmojiChooser *chooser,
EmojiSection *section,
const char *first,
const char *icon)
{
GtkAdjustment *adj;
GtkWidget *image;
section->first = first;
image = gtk_bin_get_child (GTK_BIN (section->button));
gtk_image_set_from_icon_name (GTK_IMAGE (image), icon, GTK_ICON_SIZE_BUTTON);
adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (chooser->scrolled_window));
gtk_container_set_focus_vadjustment (GTK_CONTAINER (section->box), adj);
gtk_flow_box_set_filter_func (GTK_FLOW_BOX (section->box), filter_func, section, NULL);
g_signal_connect (section->button, "clicked", G_CALLBACK (scroll_to_section), section);
}
static void
dino_emoji_chooser_init (DinoEmojiChooser *chooser)
{
GtkAdjustment *adj;
chooser->settings = g_settings_new ("org.gtk.Settings.EmojiChooser");
gtk_widget_init_template (GTK_WIDGET (chooser));
/* Get a reasonable maximum width for an emoji. We do this to
* skip overly wide fallback rendering for certain emojis the
* font does not contain and therefore end up being rendered
* as multiply glyphs.
*/
{
PangoLayout *layout = gtk_widget_create_pango_layout (GTK_WIDGET (chooser), "🙂");
PangoAttrList *attrs;
PangoRectangle rect;
attrs = pango_attr_list_new ();
pango_attr_list_insert (attrs, pango_attr_scale_new (PANGO_SCALE_X_LARGE));
pango_layout_set_attributes (layout, attrs);
pango_attr_list_unref (attrs);
pango_layout_get_extents (layout, &rect, NULL);
chooser->emoji_max_width = rect.width;
g_object_unref (layout);
}
chooser->recent_long_press = gtk_gesture_long_press_new (chooser->recent.box);
g_signal_connect (chooser->recent_long_press, "pressed", G_CALLBACK (long_pressed_cb), chooser);
chooser->recent_multi_press = gtk_gesture_multi_press_new (chooser->recent.box);
gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (chooser->recent_multi_press), GDK_BUTTON_SECONDARY);
g_signal_connect (chooser->recent_multi_press, "pressed", G_CALLBACK (pressed_cb), chooser);
chooser->people_long_press = gtk_gesture_long_press_new (chooser->people.box);
g_signal_connect (chooser->people_long_press, "pressed", G_CALLBACK (long_pressed_cb), chooser);
chooser->people_multi_press = gtk_gesture_multi_press_new (chooser->people.box);
gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (chooser->people_multi_press), GDK_BUTTON_SECONDARY);
g_signal_connect (chooser->people_multi_press, "pressed", G_CALLBACK (pressed_cb), chooser);
chooser->body_long_press = gtk_gesture_long_press_new (chooser->body.box);
g_signal_connect (chooser->body_long_press, "pressed", G_CALLBACK (long_pressed_cb), chooser);
chooser->body_multi_press = gtk_gesture_multi_press_new (chooser->body.box);
gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (chooser->body_multi_press), GDK_BUTTON_SECONDARY);
g_signal_connect (chooser->body_multi_press, "pressed", G_CALLBACK (pressed_cb), chooser);
adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (chooser->scrolled_window));
g_signal_connect (adj, "value-changed", G_CALLBACK (adj_value_changed), chooser);
setup_section (chooser, &chooser->recent, NULL, "emoji-recent-symbolic");
setup_section (chooser, &chooser->people, "grinning face", "emoji-people-symbolic");
setup_section (chooser, &chooser->body, "selfie", "emoji-body-symbolic");
setup_section (chooser, &chooser->nature, "monkey face", "emoji-nature-symbolic");
setup_section (chooser, &chooser->food, "grapes", "emoji-food-symbolic");
setup_section (chooser, &chooser->travel, "globe showing Europe-Africa", "emoji-travel-symbolic");
setup_section (chooser, &chooser->activities, "jack-o-lantern", "emoji-activities-symbolic");
setup_section (chooser, &chooser->objects, "muted speaker", "emoji-objects-symbolic");
setup_section (chooser, &chooser->symbols, "ATM sign", "emoji-symbols-symbolic");
setup_section (chooser, &chooser->flags, "chequered flag", "emoji-flags-symbolic");
populate_recent_section (chooser);
chooser->populate_idle = g_idle_add (populate_emoji_chooser, chooser);
g_source_set_name_by_id (chooser->populate_idle, "[gtk] populate_emoji_chooser");
}
static void
dino_emoji_chooser_show (GtkWidget *widget)
{
DinoEmojiChooser *chooser = DINO_EMOJI_CHOOSER (widget);
GtkAdjustment *adj;
GTK_WIDGET_CLASS (dino_emoji_chooser_parent_class)->show (widget);
adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (chooser->scrolled_window));
gtk_adjustment_set_value (adj, 0);
gtk_entry_set_text (GTK_ENTRY (chooser->search_entry), "");
}
static gboolean
dino_emoji_chooser_button_release (GtkWidget *widget,
GdkEventButton *event)
{
DinoEmojiChooserClass *klass = DINO_EMOJI_CHOOSER_GET_CLASS(widget);
GtkWidget *event_widget = gtk_get_event_widget ((GdkEvent *) event);
if (!event_widget && event->window != gtk_widget_get_window (widget))
{
return GDK_EVENT_PROPAGATE;
}
return klass->popover_button_release_event (widget, event);
}
static void
dino_emoji_chooser_class_init (DinoEmojiChooserClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->finalize = dino_emoji_chooser_finalize;
widget_class->show = dino_emoji_chooser_show;
klass->popover_button_release_event = widget_class->button_release_event;
widget_class->button_release_event = dino_emoji_chooser_button_release;
signals[EMOJI_PICKED] = g_signal_new ("emoji-picked",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
NULL,
G_TYPE_NONE, 1, G_TYPE_STRING|G_SIGNAL_TYPE_STATIC_SCOPE);
gtk_widget_class_set_template_from_resource (widget_class, "/im/dino/Dino/emojichooser.ui");
gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, search_entry);
gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, stack);
gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, scrolled_window);
gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, recent.box);
gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, recent.button);
gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, people.box);
gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, people.heading);
gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, people.button);
gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, body.box);
gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, body.heading);
gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, body.button);
gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, nature.box);
gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, nature.heading);
gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, nature.button);
gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, food.box);
gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, food.heading);
gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, food.button);
gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, travel.box);
gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, travel.heading);
gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, travel.button);
gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, activities.box);
gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, activities.heading);
gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, activities.button);
gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, objects.box);
gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, objects.heading);
gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, objects.button);
gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, symbols.box);
gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, symbols.heading);
gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, symbols.button);
gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, flags.box);
gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, flags.heading);
gtk_widget_class_bind_template_child (widget_class, DinoEmojiChooser, flags.button);
gtk_widget_class_bind_template_callback (widget_class, emoji_activated);
gtk_widget_class_bind_template_callback (widget_class, search_changed);
}
DinoEmojiChooser *
dino_emoji_chooser_new (void)
{
return DINO_EMOJI_CHOOSER (g_object_new (GTK_TYPE_EMOJI_CHOOSER, NULL));
}

View file

@ -1,36 +0,0 @@
/* gtkemojichooser.h: An Emoji chooser widget
* Copyright 2017, Red Hat, Inc.
*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define GTK_TYPE_EMOJI_CHOOSER (dino_emoji_chooser_get_type ())
#define DINO_EMOJI_CHOOSER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_EMOJI_CHOOSER, DinoEmojiChooser))
#define DINO_EMOJI_CHOOSER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_EMOJI_CHOOSER, DinoEmojiChooserClass))
#define GTK_IS_EMOJI_CHOOSER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_EMOJI_CHOOSER))
#define GTK_IS_EMOJI_CHOOSER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_EMOJI_CHOOSER))
#define DINO_EMOJI_CHOOSER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_EMOJI_CHOOSER, DinoEmojiChooserClass))
typedef struct _DinoEmojiChooser DinoEmojiChooser;
typedef struct _DinoEmojiChooserClass DinoEmojiChooserClass;
GType dino_emoji_chooser_get_type (void) G_GNUC_CONST;
DinoEmojiChooser *dino_emoji_chooser_new (void);
G_END_DECLS

View file

@ -14,7 +14,7 @@ void main(string[] args) {
Intl.textdomain(GETTEXT_PACKAGE); Intl.textdomain(GETTEXT_PACKAGE);
internationalize(GETTEXT_PACKAGE, search_path_generator.get_locale_path(GETTEXT_PACKAGE, LOCALE_INSTALL_DIR)); internationalize(GETTEXT_PACKAGE, search_path_generator.get_locale_path(GETTEXT_PACKAGE, LOCALE_INSTALL_DIR));
Gtk.init(ref args); Gtk.init();
Dino.Ui.Application app = new Dino.Ui.Application() { search_path_generator=search_path_generator }; Dino.Ui.Application app = new Dino.Ui.Application() { search_path_generator=search_path_generator };
Plugins.Loader loader = new Plugins.Loader(app); Plugins.Loader loader = new Plugins.Loader(app);
loader.load_all(); loader.load_all();

View file

@ -10,14 +10,13 @@ namespace Dino.Ui {
public class AddConferenceDialog : Gtk.Dialog { public class AddConferenceDialog : Gtk.Dialog {
private Stack stack = new Stack(); private Stack stack = new Stack();
private Button cancel_button; private Button cancel_button = new Button() { visible=true };
private Button ok_button; private Button ok_button;
private Label cancel_label = new Label(_("Cancel")) {visible=true};
private Image cancel_image = new Image.from_icon_name("go-previous-symbolic", IconSize.MENU) {visible=true};
private SelectJidFragment select_fragment; private SelectJidFragment select_fragment;
private ConferenceDetailsFragment details_fragment; private ConferenceDetailsFragment details_fragment;
private ConferenceList conference_list; private ConferenceList conference_list;
private ListBox conference_list_box;
private StreamInteractor stream_interactor; private StreamInteractor stream_interactor;
@ -29,7 +28,7 @@ public class AddConferenceDialog : Gtk.Dialog {
stack.visible = true; stack.visible = true;
stack.vhomogeneous = false; stack.vhomogeneous = false;
get_content_area().add(stack); get_content_area().append(stack);
setup_headerbar(); setup_headerbar();
setup_jid_add_view(); setup_jid_add_view();
@ -40,8 +39,7 @@ public class AddConferenceDialog : Gtk.Dialog {
private void show_jid_add_view() { private void show_jid_add_view() {
// Rewire headerbar (if CSD) // Rewire headerbar (if CSD)
if (Util.use_csd()) { if (Util.use_csd()) {
if (cancel_image.get_parent() != null) cancel_button.remove(cancel_image); cancel_button.set_label(_("Cancel"));
cancel_button.add(cancel_label);
cancel_button.clicked.disconnect(show_jid_add_view); cancel_button.clicked.disconnect(show_jid_add_view);
cancel_button.clicked.connect(on_cancel); cancel_button.clicked.connect(on_cancel);
ok_button.label = _("Next"); ok_button.label = _("Next");
@ -59,8 +57,7 @@ public class AddConferenceDialog : Gtk.Dialog {
private void show_conference_details_view() { private void show_conference_details_view() {
// Rewire headerbar (if CSD) // Rewire headerbar (if CSD)
if (Util.use_csd()) { if (Util.use_csd()) {
if (cancel_label.get_parent() != null) cancel_button.remove(cancel_label); cancel_button.set_icon_name("go-previous-symbolic");
cancel_button.add(cancel_image);
cancel_button.clicked.disconnect(on_cancel); cancel_button.clicked.disconnect(on_cancel);
cancel_button.clicked.connect(show_jid_add_view); cancel_button.clicked.connect(show_jid_add_view);
ok_button.label = _("Join"); ok_button.label = _("Join");
@ -73,31 +70,30 @@ public class AddConferenceDialog : Gtk.Dialog {
stack.transition_type = StackTransitionType.SLIDE_LEFT; stack.transition_type = StackTransitionType.SLIDE_LEFT;
stack.set_visible_child_name("details"); stack.set_visible_child_name("details");
animate_window_resize(); // animate_window_resize();
} }
private void setup_headerbar() { private void setup_headerbar() {
cancel_button = new Button() { visible=true }; ok_button = new Button() { can_focus=true, visible=true };
ok_button = new Button() { can_focus=true, can_default=true, visible=true };
ok_button.get_style_context().add_class("suggested-action"); ok_button.get_style_context().add_class("suggested-action");
if (Util.use_csd()) { if (Util.use_csd()) {
HeaderBar header_bar = get_header_bar() as HeaderBar; HeaderBar header_bar = get_header_bar() as HeaderBar;
header_bar.show_close_button = false; header_bar.show_title_buttons = false;
header_bar.pack_start(cancel_button); header_bar.pack_start(cancel_button);
header_bar.pack_end(ok_button); header_bar.pack_end(ok_button);
ok_button.has_default = true; // ok_button.has_default = true;
} }
} }
private void setup_jid_add_view() { private void setup_jid_add_view() {
conference_list = new ConferenceList(stream_interactor); conference_list = new ConferenceList(stream_interactor);
conference_list.row_activated.connect(() => { ok_button.clicked(); }); conference_list_box = conference_list.get_list_box();
conference_list_box.row_activated.connect(() => { ok_button.clicked(); });
select_fragment = new SelectJidFragment(stream_interactor, conference_list, stream_interactor.get_accounts()); select_fragment = new SelectJidFragment(stream_interactor, conference_list_box, stream_interactor.get_accounts());
select_fragment.add_jid.connect((row) => { select_fragment.add_jid.connect((row) => {
AddGroupchatDialog dialog = new AddGroupchatDialog(stream_interactor); AddGroupchatDialog dialog = new AddGroupchatDialog(stream_interactor);
dialog.set_transient_for(this); dialog.set_transient_for(this);
@ -109,23 +105,23 @@ public class AddConferenceDialog : Gtk.Dialog {
}); });
Box wrap_box = new Box(Orientation.VERTICAL, 0) { visible=true }; Box wrap_box = new Box(Orientation.VERTICAL, 0) { visible=true };
wrap_box.add(select_fragment); wrap_box.append(select_fragment);
stack.add_named(wrap_box, "select"); stack.add_named(wrap_box, "select");
if (!Util.use_csd()) { if (!Util.use_csd()) {
Box box = new Box(Orientation.HORIZONTAL, 5) { halign=Align.END, margin_bottom=15, margin_start=80, margin_end=80, visible=true }; Box box = new Box(Orientation.HORIZONTAL, 5) { halign=Align.END, margin_bottom=15, margin_start=80, margin_end=80, visible=true };
Button ok_button = new Button() { label=_("Next"), sensitive=false, halign = Align.END, can_focus=true, can_default=true, visible=true }; Button ok_button = new Button.with_label(_("Next")) { sensitive=false, halign = Align.END, can_focus=true, visible=true };
ok_button.get_style_context().add_class("suggested-action"); ok_button.get_style_context().add_class("suggested-action");
ok_button.clicked.connect(on_next_button_clicked); ok_button.clicked.connect(on_next_button_clicked);
select_fragment.notify["done"].connect(() => { ok_button.sensitive = select_fragment.done; }); select_fragment.notify["done"].connect(() => { ok_button.sensitive = select_fragment.done; });
Button cancel_button = new Button() { label=_("Cancel"), halign=Align.START, visible=true }; Button cancel_button = new Button.with_label(_("Cancel")) { halign=Align.START, visible=true };
cancel_button.clicked.connect(on_cancel); cancel_button.clicked.connect(on_cancel);
box.add(cancel_button); box.append(cancel_button);
box.add(ok_button); box.append(ok_button);
wrap_box.add(box); wrap_box.append(box);
ok_button.has_default = true; // ok_button.has_default = true;
} }
} }
@ -134,22 +130,22 @@ public class AddConferenceDialog : Gtk.Dialog {
details_fragment.joined.connect(() => this.close()); details_fragment.joined.connect(() => this.close());
Box wrap_box = new Box(Orientation.VERTICAL, 0) { visible=true }; Box wrap_box = new Box(Orientation.VERTICAL, 0) { visible=true };
wrap_box.add(details_fragment); wrap_box.append(details_fragment);
if (!Util.use_csd()) { if (!Util.use_csd()) {
Box box = new Box(Orientation.HORIZONTAL, 5) { halign=Align.END, margin_bottom=15, margin_start=80, margin_end=80, visible=true }; Box box = new Box(Orientation.HORIZONTAL, 5) { halign=Align.END, margin_bottom=15, margin_start=80, margin_end=80, visible=true };
Button ok_button = new Button() { label=_("Join"), halign = Align.END, can_focus=true, can_default=true, visible=true }; Button ok_button = new Button.with_label(_("Join")) { halign = Align.END, can_focus=true, visible=true };
ok_button.get_style_context().add_class("suggested-action"); ok_button.get_style_context().add_class("suggested-action");
details_fragment.notify["done"].connect(() => { ok_button.sensitive = select_fragment.done; }); details_fragment.notify["done"].connect(() => { ok_button.sensitive = select_fragment.done; });
details_fragment.ok_button = ok_button; details_fragment.ok_button = ok_button;
Button cancel_button = new Button() { label=_("Back"), halign=Align.START, visible=true }; Button cancel_button = new Button.with_label(_("Back")) { halign=Align.START, visible=true };
cancel_button.clicked.connect(show_jid_add_view); cancel_button.clicked.connect(show_jid_add_view);
box.add(cancel_button); box.append(cancel_button);
box.add(ok_button); box.append(ok_button);
wrap_box.add(box); wrap_box.append(box);
} }
stack.add_named(wrap_box, "details"); stack.add_named(wrap_box, "details");
} }
@ -164,8 +160,9 @@ public class AddConferenceDialog : Gtk.Dialog {
private void on_next_button_clicked() { private void on_next_button_clicked() {
details_fragment.clear(); details_fragment.clear();
ListRow? row = conference_list.get_selected_row() as ListRow;
ConferenceListRow? conference_row = conference_list.get_selected_row() as ConferenceListRow; ListRow? row = conference_list_box.get_selected_row() != null ? conference_list_box.get_selected_row().get_child() as ListRow : null;
ConferenceListRow? conference_row = conference_list_box.get_selected_row() != null ? conference_list_box.get_selected_row() as ConferenceListRow : null;
if (conference_row != null) { if (conference_row != null) {
details_fragment.account = conference_row.account; details_fragment.account = conference_row.account;
details_fragment.jid = conference_row.bookmark.jid.to_string(); details_fragment.jid = conference_row.bookmark.jid.to_string();
@ -184,10 +181,11 @@ public class AddConferenceDialog : Gtk.Dialog {
} }
private void animate_window_resize() { private void animate_window_resize() {
int def_height, curr_width, curr_height; int curr_height = get_size(Orientation.VERTICAL);
get_size(out curr_width, out curr_height); int curr_width = get_size(Orientation.HORIZONTAL);
stack.get_preferred_height(null, out def_height); var natural_size = new Requisition();
int difference = def_height - curr_height; stack.get_preferred_size(null, out natural_size);
int difference = natural_size.height - curr_height;
Timer timer = new Timer(); Timer timer = new Timer();
Timeout.add((int) (stack.transition_duration / 30), Timeout.add((int) (stack.transition_duration / 30),
() => { () => {
@ -195,7 +193,8 @@ public class AddConferenceDialog : Gtk.Dialog {
timer.elapsed(out microsec); timer.elapsed(out microsec);
ulong millisec = microsec / 1000; ulong millisec = microsec / 1000;
double partial = double.min(1, (double) millisec / stack.transition_duration); double partial = double.min(1, (double) millisec / stack.transition_duration);
resize(curr_width, (int) (curr_height + difference * partial)); var a = this.list_toplevels().nth_data(0);
set_size_request(curr_width, (int) (curr_height + difference * partial));
return millisec < stack.transition_duration; return millisec < stack.transition_duration;
}); });
} }

View file

@ -30,11 +30,12 @@ protected class AddGroupchatDialog : Gtk.Dialog {
cancel_button.clicked.connect(() => { close(); }); cancel_button.clicked.connect(() => { close(); });
ok_button.clicked.connect(on_ok_button_clicked); ok_button.clicked.connect(on_ok_button_clicked);
jid_entry.key_release_event.connect(on_jid_key_release);
nick_entry.key_release_event.connect(check_ok); jid_entry.changed.connect(on_jid_key_release);
nick_entry.changed.connect(check_ok);
} }
private bool on_jid_key_release() { private void on_jid_key_release() {
check_ok(); check_ok();
if (!alias_entry_changed) { if (!alias_entry_changed) {
try { try {
@ -44,17 +45,15 @@ protected class AddGroupchatDialog : Gtk.Dialog {
alias_entry.text = jid_entry.text; alias_entry.text = jid_entry.text;
} }
} }
return false;
} }
private bool check_ok() { private void check_ok() {
try { try {
Jid parsed_jid = new Jid(jid_entry.text); Jid parsed_jid = new Jid(jid_entry.text);
ok_button.sensitive = parsed_jid != null && parsed_jid.localpart != null && parsed_jid.resourcepart == null; ok_button.sensitive = parsed_jid != null && parsed_jid.localpart != null && parsed_jid.resourcepart == null;
} catch (InvalidJidError e) { } catch (InvalidJidError e) {
ok_button.sensitive = false; ok_button.sensitive = false;
} }
return false;
} }
private void on_ok_button_clicked() { private void on_ok_button_clicked() {

View file

@ -114,12 +114,12 @@ protected class ConferenceDetailsFragment : Box {
account_combobox.changed.connect(() => { accounts_label.label = account_combobox.selected.bare_jid.to_string(); }); account_combobox.changed.connect(() => { accounts_label.label = account_combobox.selected.bare_jid.to_string(); });
accounts_label.label = account_combobox.selected.bare_jid.to_string(); accounts_label.label = account_combobox.selected.bare_jid.to_string();
jid_entry.key_release_event.connect(on_jid_key_release_event); // jid_entry.key_release_event.connect(on_jid_key_release_event);
nick_entry.key_release_event.connect(on_nick_key_release_event); // nick_entry.key_release_event.connect(on_nick_key_release_event);
password_entry.key_release_event.connect(on_password_key_release_event); // password_entry.key_release_event.connect(on_password_key_release_event);
jid_entry.key_release_event.connect(() => { done = true; return false; }); // just for notifying // jid_entry.key_release_event.connect(() => { done = true; return false; }); // just for notifying
nick_entry.key_release_event.connect(() => { done = true; return false; }); // nick_entry.key_release_event.connect(() => { done = true; return false; });
notification_button.clicked.connect(() => { notification_revealer.set_reveal_child(false); }); notification_button.clicked.connect(() => { notification_revealer.set_reveal_child(false); });
@ -195,25 +195,25 @@ protected class ConferenceDetailsFragment : Box {
notification_revealer.set_reveal_child(true); notification_revealer.set_reveal_child(true);
} }
private bool on_jid_key_release_event(EventKey event) { // private bool on_jid_key_release_event(EventKey event) {
jid_label.label = jid_entry.text; // jid_label.label = jid_entry.text;
if (event.keyval == Key.Return) jid_stack.set_visible_child_name("label"); // if (event.keyval == Key.Return) jid_stack.set_visible_child_name("label");
return false; // return false;
} // }
//
private bool on_nick_key_release_event(EventKey event) { // private bool on_nick_key_release_event(EventKey event) {
nick_label.label = nick_entry.text; // nick_label.label = nick_entry.text;
if (event.keyval == Key.Return) nick_stack.set_visible_child_name("label"); // if (event.keyval == Key.Return) nick_stack.set_visible_child_name("label");
return false; // return false;
} // }
//
private bool on_password_key_release_event(EventKey event) { // private bool on_password_key_release_event(EventKey event) {
string filler = ""; // string filler = "";
for (int i = 0; i < password_entry.text.length; i++) filler += password_entry.get_invisible_char().to_string(); // for (int i = 0; i < password_entry.text.length; i++) filler += password_entry.get_invisible_char().to_string();
password_label.label = filler; // password_label.label = filler;
if (event.keyval == Key.Return) password_stack.set_visible_child_name("label"); // if (event.keyval == Key.Return) password_stack.set_visible_child_name("label");
return false; // return false;
} // }
private void set_active_stack(Stack stack) { private void set_active_stack(Stack stack) {
stack.set_visible_child_name("entry"); stack.set_visible_child_name("entry");

View file

@ -7,20 +7,22 @@ using Dino.Entities;
namespace Dino.Ui { namespace Dino.Ui {
protected class ConferenceList : FilterableList { protected class ConferenceList {
public signal void conversation_selected(Conversation? conversation); public signal void conversation_selected(Conversation? conversation);
private StreamInteractor stream_interactor; private StreamInteractor stream_interactor;
private ListBox list_box = new ListBox();
private HashMap<Account, Set<Conference>> lists = new HashMap<Account, Set<Conference>>(Account.hash_func, Account.equals_func); private HashMap<Account, Set<Conference>> lists = new HashMap<Account, Set<Conference>>(Account.hash_func, Account.equals_func);
private HashMap<Account, HashMap<Jid, Widget>> widgets = new HashMap<Account, HashMap<Jid, Widget>>(Account.hash_func, Account.equals_func); private HashMap<Account, HashMap<Jid, Widget>> widgets = new HashMap<Account, HashMap<Jid, Widget>>(Account.hash_func, Account.equals_func);
public ConferenceList(StreamInteractor stream_interactor) { public ConferenceList(StreamInteractor stream_interactor) {
this.stream_interactor = stream_interactor; this.stream_interactor = stream_interactor;
set_filter_func(filter); // list_box.set_filter_func(filter);
set_header_func(header); list_box.set_header_func(header);
set_sort_func(sort); // list_box.set_sort_func(sort);
stream_interactor.get_module(MucManager.IDENTITY).bookmarks_updated.connect((account, conferences) => { stream_interactor.get_module(MucManager.IDENTITY).bookmarks_updated.connect((account, conferences) => {
lists[account] = conferences; lists[account] = conferences;
@ -44,18 +46,18 @@ protected class ConferenceList : FilterableList {
} }
var widget = new ConferenceListRow(stream_interactor, conference, account); var widget = new ConferenceListRow(stream_interactor, conference, account);
widgets[account][conference.jid] = widget; widgets[account][conference.jid] = widget;
add(widget); list_box.append(widget);
} }
private void remove_conference(Account account, Jid jid) { private void remove_conference(Account account, Jid jid) {
if (widgets.has_key(account) && widgets[account].has_key(jid)) { if (widgets.has_key(account) && widgets[account].has_key(jid)) {
remove(widgets[account][jid]); list_box.remove(widgets[account][jid]);
widgets[account].unset(jid); widgets[account].unset(jid);
} }
} }
public void refresh_conferences() { public void refresh_conferences() {
@foreach((widget) => { remove(widget); }); // @foreach((widget) => { remove(widget); });
foreach (Account account in lists.keys) { foreach (Account account in lists.keys) {
foreach (Conference conference in lists[account]) { foreach (Conference conference in lists[account]) {
add_conference(account, conference); add_conference(account, conference);
@ -78,25 +80,8 @@ protected class ConferenceList : FilterableList {
} }
} }
private bool filter(ListBoxRow r) { public ListBox get_list_box() {
if (r.get_type().is_a(typeof(ListRow))) { return list_box;
ListRow row = r as ListRow;
if (filter_values != null) {
foreach (string filter in filter_values) {
if (!(row.name_label.label.down().contains(filter.down()) ||
row.jid.to_string().down().contains(filter.down()))) {
return false;
}
}
}
}
return true;
}
public override int sort(ListBoxRow row1, ListBoxRow row2) {
ListRow c1 = (row1 as ListRow);
ListRow c2 = (row2 as ListRow);
return c1.name_label.label.collate(c2.name_label.label);
} }
} }

View file

@ -6,16 +6,27 @@ using Xmpp;
namespace Dino.Ui { namespace Dino.Ui {
[GtkTemplate (ui = "/im/dino/Dino/add_conversation/list_row.ui")] public class ListRow : Widget {
public class ListRow : ListBoxRow {
[GtkChild] public unowned AvatarImage image; public Grid outer_grid;
[GtkChild] public unowned Label name_label; public AvatarImage image;
[GtkChild] public unowned Label via_label; public Label name_label;
public Label via_label;
public Jid? jid; public Jid? jid;
public Account? account; public Account? account;
construct {
Builder builder = new Builder.from_resource("/im/dino/Dino/add_conversation/list_row.ui");
outer_grid = (Grid) builder.get_object("outer_grid");
image = (AvatarImage) builder.get_object("image");
name_label = (Label) builder.get_object("name_label");
via_label = (Label) builder.get_object("via_label");
this.layout_manager = new BinLayout();
outer_grid.insert_after(this, null);
}
public ListRow() {} public ListRow() {}
public ListRow.from_jid(StreamInteractor stream_interactor, Jid jid, Account account, bool show_account) { public ListRow.from_jid(StreamInteractor stream_interactor, Jid jid, Account account, bool show_account) {

View file

@ -6,22 +6,23 @@ using Xmpp;
namespace Dino.Ui { namespace Dino.Ui {
protected class RosterList : FilterableList { protected class RosterList {
public signal void conversation_selected(Conversation? conversation); public signal void conversation_selected(Conversation? conversation);
private StreamInteractor stream_interactor; private StreamInteractor stream_interactor;
private Gee.List<Account> accounts; private Gee.List<Account> accounts;
private ulong[] handler_ids = new ulong[0]; private ulong[] handler_ids = new ulong[0];
private ListBox list_box = new ListBox();
private HashMap<Account, HashMap<Jid, ListRow>> rows = new HashMap<Account, HashMap<Jid, ListRow>>(Account.hash_func, Account.equals_func); private HashMap<Account, HashMap<Jid, ListRow>> rows = new HashMap<Account, HashMap<Jid, ListRow>>(Account.hash_func, Account.equals_func);
public RosterList(StreamInteractor stream_interactor, Gee.List<Account> accounts) { public RosterList(StreamInteractor stream_interactor, Gee.List<Account> accounts) {
this.stream_interactor = stream_interactor; this.stream_interactor = stream_interactor;
this.accounts = accounts; this.accounts = accounts;
set_filter_func(filter); // set_filter_func(filter);
set_header_func(header); list_box.set_header_func(header);
set_sort_func(sort); // set_sort_func(sort);
handler_ids += stream_interactor.get_module(RosterManager.IDENTITY).removed_roster_item.connect( (account, jid, roster_item) => { handler_ids += stream_interactor.get_module(RosterManager.IDENTITY).removed_roster_item.connect( (account, jid, roster_item) => {
if (accounts.contains(account)) { if (accounts.contains(account)) {
@ -33,7 +34,7 @@ protected class RosterList : FilterableList {
on_updated_roster_item(account, jid, roster_item); on_updated_roster_item(account, jid, roster_item);
} }
}); });
destroy.connect(() => { list_box.destroy.connect(() => {
foreach (ulong handler_id in handler_ids) stream_interactor.get_module(RosterManager.IDENTITY).disconnect(handler_id); foreach (ulong handler_id in handler_ids) stream_interactor.get_module(RosterManager.IDENTITY).disconnect(handler_id);
}); });
@ -42,7 +43,7 @@ protected class RosterList : FilterableList {
private void on_removed_roster_item(Account account, Jid jid, Roster.Item roster_item) { private void on_removed_roster_item(Account account, Jid jid, Roster.Item roster_item) {
if (rows.has_key(account) && rows[account].has_key(jid)) { if (rows.has_key(account) && rows[account].has_key(jid)) {
remove(rows[account][jid]); list_box.remove(rows[account][jid]);
rows[account].unset(jid); rows[account].unset(jid);
} }
} }
@ -51,9 +52,9 @@ protected class RosterList : FilterableList {
on_removed_roster_item(account, jid, roster_item); on_removed_roster_item(account, jid, roster_item);
ListRow row = new ListRow.from_jid(stream_interactor, roster_item.jid, account, accounts.size > 1); ListRow row = new ListRow.from_jid(stream_interactor, roster_item.jid, account, accounts.size > 1);
rows[account][jid] = row; rows[account][jid] = row;
add(row); list_box.append(row);
invalidate_sort(); list_box.invalidate_sort();
invalidate_filter(); list_box.invalidate_filter();
} }
private void fetch_roster_items(Account account) { private void fetch_roster_items(Account account) {
@ -69,25 +70,8 @@ protected class RosterList : FilterableList {
} }
} }
private bool filter(ListBoxRow r) { public ListBox get_list_box() {
if (r.get_type().is_a(typeof(ListRow))) { return list_box;
ListRow row = r as ListRow;
if (filter_values != null) {
foreach (string filter in filter_values) {
if (!(row.name_label.label.down().contains(filter.down()) ||
row.jid.to_string().down().contains(filter.down()))) {
return false;
}
}
}
}
return true;
}
public override int sort(ListBoxRow row1, ListBoxRow row2) {
ListRow c1 = (row1 as ListRow);
ListRow c2 = (row2 as ListRow);
return c1.name_label.label.collate(c2.name_label.label);
} }
} }

View file

@ -14,6 +14,7 @@ public class SelectContactDialog : Gtk.Dialog {
public Button ok_button; public Button ok_button;
private RosterList roster_list; private RosterList roster_list;
private ListBox roster_list_box;
private SelectJidFragment select_jid_fragment; private SelectJidFragment select_jid_fragment;
private StreamInteractor stream_interactor; private StreamInteractor stream_interactor;
private Gee.List<Account> accounts; private Gee.List<Account> accounts;
@ -45,7 +46,7 @@ public class SelectContactDialog : Gtk.Dialog {
if (Util.use_csd()) { if (Util.use_csd()) {
HeaderBar header_bar = get_header_bar() as HeaderBar; HeaderBar header_bar = get_header_bar() as HeaderBar;
header_bar.show_close_button = false; header_bar.show_title_buttons = false;
header_bar.pack_start(cancel_button); header_bar.pack_start(cancel_button);
header_bar.pack_end(ok_button); header_bar.pack_end(ok_button);
@ -54,15 +55,15 @@ public class SelectContactDialog : Gtk.Dialog {
cancel_button.halign = Align.START; cancel_button.halign = Align.START;
ok_button.halign = Align.END; ok_button.halign = Align.END;
box.add(cancel_button); box.append(cancel_button);
box.add(ok_button); box.append(ok_button);
get_content_area().add(box); get_content_area().append(box);
} }
cancel_button.clicked.connect(() => { close(); }); cancel_button.clicked.connect(() => { close(); });
ok_button.clicked.connect(() => { ok_button.clicked.connect(() => {
ListRow? selected_row = roster_list.get_selected_row() as ListRow; ListRow? selected_row = roster_list_box.get_selected_row() != null ? roster_list_box.get_selected_row().get_child() as ListRow : null;
if (selected_row != null) selected(selected_row.account, selected_row.jid); if (selected_row != null) selected(selected_row.account, selected_row.jid);
close(); close();
}); });
@ -70,21 +71,22 @@ public class SelectContactDialog : Gtk.Dialog {
private void setup_view() { private void setup_view() {
roster_list = new RosterList(stream_interactor, accounts); roster_list = new RosterList(stream_interactor, accounts);
roster_list.row_activated.connect(() => { ok_button.clicked(); }); roster_list_box = roster_list.get_list_box();
select_jid_fragment = new SelectJidFragment(stream_interactor, roster_list, accounts); roster_list_box.row_activated.connect(() => { ok_button.clicked(); });
select_jid_fragment = new SelectJidFragment(stream_interactor, roster_list_box, accounts);
select_jid_fragment.add_jid.connect((row) => { select_jid_fragment.add_jid.connect((row) => {
AddContactDialog add_contact_dialog = new AddContactDialog(stream_interactor); AddContactDialog add_contact_dialog = new AddContactDialog(stream_interactor);
add_contact_dialog.set_transient_for(this); add_contact_dialog.set_transient_for(this);
add_contact_dialog.present(); add_contact_dialog.present();
}); });
select_jid_fragment.remove_jid.connect((row) => { select_jid_fragment.remove_jid.connect((row) => {
ListRow list_row = roster_list.get_selected_row() as ListRow; ListRow list_row = roster_list_box.get_selected_row() as ListRow;
stream_interactor.get_module(RosterManager.IDENTITY).remove_jid(list_row.account, list_row.jid); stream_interactor.get_module(RosterManager.IDENTITY).remove_jid(list_row.account, list_row.jid);
}); });
select_jid_fragment.notify["done"].connect(() => { select_jid_fragment.notify["done"].connect(() => {
ok_button.sensitive = select_jid_fragment.done; ok_button.sensitive = select_jid_fragment.done;
}); });
get_content_area().add(select_jid_fragment); get_content_area().append(select_jid_fragment);
} }
} }

View file

@ -12,7 +12,7 @@ public class SelectJidFragment : Gtk.Box {
public signal void add_jid(); public signal void add_jid();
public signal void remove_jid(ListRow row); public signal void remove_jid(ListRow row);
public bool done { public bool done {
get { return filterable_list.get_selected_row() != null; } get { return list.get_selected_row() != null; }
private set {} private set {}
} }
@ -22,43 +22,45 @@ public class SelectJidFragment : Gtk.Box {
[GtkChild] private unowned Button remove_button; [GtkChild] private unowned Button remove_button;
private StreamInteractor stream_interactor; private StreamInteractor stream_interactor;
private FilterableList filterable_list;
private Gee.List<Account> accounts; private Gee.List<Account> accounts;
private ArrayList<AddListRow> added_rows = new ArrayList<AddListRow>(); private ArrayList<AddListRow> added_rows = new ArrayList<AddListRow>();
public SelectJidFragment(StreamInteractor stream_interactor, FilterableList filterable_list, Gee.List<Account> accounts) { private ListBox list;
private string[]? filter_values;
public SelectJidFragment(StreamInteractor stream_interactor, ListBox list, Gee.List<Account> accounts) {
this.stream_interactor = stream_interactor; this.stream_interactor = stream_interactor;
this.filterable_list = filterable_list; this.list = list;
this.accounts = accounts; this.accounts = accounts;
filterable_list.visible = true; list.activate_on_single_click = false;
filterable_list.activate_on_single_click = false; list.vexpand = true;
filterable_list.vexpand = true; box.append(list);
box.add(filterable_list);
filterable_list.set_sort_func(sort); list.set_sort_func(sort);
filterable_list.row_selected.connect(check_buttons_active); list.set_filter_func(filter);
filterable_list.row_selected.connect(() => { done = true; }); // just for notifying list.row_selected.connect(check_buttons_active);
list.row_selected.connect(() => { done = true; }); // just for notifying
entry.changed.connect(() => { set_filter(entry.text); }); entry.changed.connect(() => { set_filter(entry.text); });
add_button.clicked.connect(() => { add_jid(); }); add_button.clicked.connect(() => { add_jid(); });
remove_button.clicked.connect(() => { remove_jid(filterable_list.get_selected_row() as ListRow); }); remove_button.clicked.connect(() => { remove_jid(list.get_selected_row() as ListRow); });
} }
public void set_filter(string str) { public void set_filter(string str) {
if (entry.text != str) entry.text = str; if (entry.text != str) entry.text = str;
foreach (AddListRow row in added_rows) filterable_list.remove(row); foreach (AddListRow row in added_rows) list.remove(row);
added_rows.clear(); added_rows.clear();
string[] ? values = str == "" ? null : str.split(" "); filter_values = str == "" ? null : str.split(" ");
filterable_list.set_filter_values(values); list.invalidate_filter();
try { try {
Jid parsed_jid = new Jid(str); Jid parsed_jid = new Jid(str);
if (parsed_jid != null && parsed_jid.localpart != null) { if (parsed_jid != null && parsed_jid.localpart != null) {
foreach (Account account in accounts) { foreach (Account account in accounts) {
AddListRow row = new AddListRow(stream_interactor, parsed_jid, account); AddListRow row = new AddListRow(stream_interactor, parsed_jid, account);
filterable_list.add(row); list.append(row);
added_rows.add(row); added_rows.add(row);
} }
} }
@ -68,7 +70,7 @@ public class SelectJidFragment : Gtk.Box {
} }
private void check_buttons_active() { private void check_buttons_active() {
ListBoxRow? row = filterable_list.get_selected_row(); ListBoxRow? row = list.get_selected_row();
bool active = row != null && !row.get_type().is_a(typeof(AddListRow)); bool active = row != null && !row.get_type().is_a(typeof(AddListRow));
remove_button.sensitive = active; remove_button.sensitive = active;
} }
@ -81,7 +83,29 @@ public class SelectJidFragment : Gtk.Box {
} else if (al2 != null && al1 == null) { } else if (al2 != null && al1 == null) {
return 1; return 1;
} }
return filterable_list.sort(row1, row2);
ListRow? c1 = (row1.child as ListRow);
ListRow? c2 = (row2.child as ListRow);
if (c1 != null && c2 != null) {
return c1.name_label.label.collate(c2.name_label.label);
}
return 0;
}
private bool filter(ListBoxRow r) {
ListRow? row = (r.child as ListRow);
if (row == null) return true;
if (filter_values != null) {
foreach (string filter in filter_values) {
if (!(row.name_label.label.down().contains(filter.down()) ||
row.jid.to_string().down().contains(filter.down()))) {
return false;
}
}
}
return true;
} }
private class AddListRow : ListRow { private class AddListRow : ListRow {
@ -101,16 +125,4 @@ public class SelectJidFragment : Gtk.Box {
} }
} }
public abstract class FilterableList : Gtk.ListBox {
public string[]? filter_values;
public void set_filter_values(string[] values) {
if (filter_values == values) return;
filter_values = values;
invalidate_filter();
}
public abstract int sort(ListBoxRow row1, ListBoxRow row2);
}
} }

View file

@ -35,7 +35,7 @@ public class Dino.Ui.Application : Gtk.Application, Dino.Application {
CssProvider provider = new CssProvider(); CssProvider provider = new CssProvider();
provider.load_from_resource("/im/dino/Dino/theme.css"); provider.load_from_resource("/im/dino/Dino/theme.css");
StyleContext.add_provider_for_screen(Gdk.Screen.get_default(), provider, STYLE_PROVIDER_PRIORITY_APPLICATION); StyleContext.add_provider_for_display(Gdk.Display.get_default(), provider, STYLE_PROVIDER_PRIORITY_APPLICATION);
create_actions(); create_actions();
add_main_option_entries(options); add_main_option_entries(options);
@ -68,7 +68,7 @@ public class Dino.Ui.Application : Gtk.Application, Dino.Application {
var desktop_env = Environment.get_variable("XDG_CURRENT_DESKTOP"); var desktop_env = Environment.get_variable("XDG_CURRENT_DESKTOP");
if (desktop_env == null || !desktop_env.down().contains("gnome")) { if (desktop_env == null || !desktop_env.down().contains("gnome")) {
if (this.active_window != null) { if (this.active_window != null) {
this.active_window.urgency_hint = true; // this.active_window.urgency_hint = true;
} }
} }
}); });
@ -80,7 +80,7 @@ public class Dino.Ui.Application : Gtk.Application, Dino.Application {
config = new Config(db); config = new Config(db);
window = new MainWindow(this, stream_interactor, db, config); window = new MainWindow(this, stream_interactor, db, config);
controller.set_window(window); controller.set_window(window);
if ((get_flags() & ApplicationFlags.IS_SERVICE) == ApplicationFlags.IS_SERVICE) window.delete_event.connect(window.hide_on_delete); if ((get_flags() & ApplicationFlags.IS_SERVICE) == ApplicationFlags.IS_SERVICE) window.hide_on_close = true;
} }
window.present(); window.present();
}); });
@ -205,9 +205,9 @@ public class Dino.Ui.Application : Gtk.Application, Dino.Application {
if (!use_csd()) { if (!use_csd()) {
// Hack to prevent CRITICAL in Gtk when trying to destroy non-existant headerbar // Hack to prevent CRITICAL in Gtk when trying to destroy non-existant headerbar
Widget shortcuts_hack = dialog.get_titlebar(); Widget shortcuts_hack = dialog.get_titlebar();
dialog.destroy.connect_after(() => { // dialog.destroy.connect_after(() => {
shortcuts_hack = null; // shortcuts_hack = null;
}); // });
dialog.set_titlebar(null); dialog.set_titlebar(null);
} }
dialog.title = _("Keyboard Shortcuts"); dialog.title = _("Keyboard Shortcuts");
@ -292,11 +292,11 @@ public class Dino.Ui.Application : Gtk.Application, Dino.Application {
dialog.copyright = "Copyright © 2016-2022 - Dino Team"; dialog.copyright = "Copyright © 2016-2022 - Dino Team";
dialog.license_type = License.GPL_3_0; dialog.license_type = License.GPL_3_0;
dialog.response.connect((response_id) => { // dialog.response.connect((response_id) => {
if (response_id == Gtk.ResponseType.CANCEL || response_id == Gtk.ResponseType.DELETE_EVENT) { // if (response_id == Gtk.ResponseType.CANCEL || response_id == Gtk.ResponseType.DELETE_EVENT) {
dialog.destroy(); // dialog.destroy();
} // }
}); // });
if (!use_csd()) { if (!use_csd()) {
dialog.set_titlebar(null); dialog.set_titlebar(null);
@ -315,7 +315,7 @@ public class Dino.Ui.Application : Gtk.Application, Dino.Application {
conference_fragment.account = account; conference_fragment.account = account;
} }
Box content_area = dialog.get_content_area(); Box content_area = dialog.get_content_area();
content_area.add(conference_fragment); content_area.append(conference_fragment);
conference_fragment.joined.connect(() => { conference_fragment.joined.connect(() => {
dialog.destroy(); dialog.destroy();
}); });

View file

@ -5,7 +5,7 @@ using Xmpp.Util;
namespace Dino.Ui { namespace Dino.Ui {
public class AvatarImage : Misc { public class AvatarImage : Widget {
public int height { get; set; default = 35; } public int height { get; set; default = 35; }
public int width { get; set; default = 35; } public int width { get; set; default = 35; }
public bool allow_gray { get; set; default = true; } public bool allow_gray { get; set; default = true; }
@ -34,17 +34,24 @@ public class AvatarImage : Misc {
disconnect_stream_interactor(); disconnect_stream_interactor();
} }
public override void get_preferred_width(out int minimum_width, out int natural_width) { public override void measure(Orientation orientation, int for_size, out int minimum, out int natural, out int minimum_baseline, out int natural_baseline) {
minimum_width = width; if (orientation == Orientation.HORIZONTAL) {
natural_width = width; minimum = width;
natural = width;
} else {
minimum = height;
natural = height;
}
minimum_baseline = natural_baseline = -1;
} }
public override void get_preferred_height(out int minimum_height, out int natural_height) { public override void snapshot(Snapshot snapshot) {
minimum_height = height; Cairo.Context context = snapshot.append_cairo(Graphene.Rect.alloc().init(0, 0, width, height));
natural_height = height; draw(context);
} }
public override bool draw(Cairo.Context ctx_in) { public bool draw(Cairo.Context ctx_in) {
if (conversation == null || jids == null) return false;
Cairo.Context ctx = ctx_in; Cairo.Context ctx = ctx_in;
int width = this.width, height = this.height, base_factor = 1; int width = this.width, height = this.height, base_factor = 1;
if (use_image_surface == -1) { if (use_image_surface == -1) {

View file

@ -14,11 +14,11 @@ public class Dino.Ui.AudioSettingsPopover : Gtk.Popover {
private HashMap<ListBoxRow, Plugins.MediaDevice> row_speaker_device = new HashMap<ListBoxRow, Plugins.MediaDevice>(); private HashMap<ListBoxRow, Plugins.MediaDevice> row_speaker_device = new HashMap<ListBoxRow, Plugins.MediaDevice>();
public AudioSettingsPopover() { public AudioSettingsPopover() {
Box box = new Box(Orientation.VERTICAL, 15) { margin=18, visible=true }; Box box = new Box(Orientation.VERTICAL, 15) { visible=true };
box.add(create_microphone_box()); box.append(create_microphone_box());
box.add(create_speaker_box()); box.append(create_speaker_box());
this.add(box); this.set_child(box);
} }
private Widget create_microphone_box() { private Widget create_microphone_box() {
@ -26,18 +26,18 @@ public class Dino.Ui.AudioSettingsPopover : Gtk.Popover {
Gee.List<Plugins.MediaDevice> devices = call_plugin.get_devices("audio", false); Gee.List<Plugins.MediaDevice> devices = call_plugin.get_devices("audio", false);
Box micro_box = new Box(Orientation.VERTICAL, 10) { visible=true }; Box micro_box = new Box(Orientation.VERTICAL, 10) { visible=true };
micro_box.add(new Label("<b>" + _("Microphones") + "</b>") { use_markup=true, xalign=0, visible=true, can_focus=true /* grab initial focus*/ }); micro_box.append(new Label("<b>" + _("Microphones") + "</b>") { use_markup=true, xalign=0, visible=true, can_focus=true /* grab initial focus*/ });
if (devices.size == 0) { if (devices.size == 0) {
micro_box.add(new Label(_("No microphone found."))); micro_box.append(new Label(_("No microphone found.")));
} else { } else {
ListBox micro_list_box = new ListBox() { activate_on_single_click=true, selection_mode=SelectionMode.SINGLE, visible=true }; ListBox micro_list_box = new ListBox() { activate_on_single_click=true, selection_mode=SelectionMode.SINGLE, visible=true };
micro_list_box.set_header_func(listbox_header_func); micro_list_box.set_header_func(listbox_header_func);
Frame micro_frame = new Frame(null) { visible=true }; Frame micro_frame = new Frame(null) { visible=true };
micro_frame.add(micro_list_box); micro_frame.set_child(micro_list_box);
foreach (Plugins.MediaDevice device in devices) { foreach (Plugins.MediaDevice device in devices) {
Label display_name_label = new Label(device.display_name) { xalign=0, visible=true }; Label display_name_label = new Label(device.display_name) { xalign=0, visible=true };
Image image = new Image.from_icon_name("object-select-symbolic", IconSize.BUTTON) { visible=true }; Image image = new Image.from_icon_name("object-select-symbolic") { visible=true };
if (current_microphone_device == null || current_microphone_device.id != device.id) { if (current_microphone_device == null || current_microphone_device.id != device.id) {
image.opacity = 0; image.opacity = 0;
} }
@ -48,21 +48,21 @@ public class Dino.Ui.AudioSettingsPopover : Gtk.Popover {
image.opacity = 1; image.opacity = 1;
} }
}); });
Box device_box = new Box(Orientation.HORIZONTAL, 0) { spacing=7, margin=7, visible=true }; Box device_box = new Box(Orientation.HORIZONTAL, 0) { spacing=7, visible=true };
device_box.add(image); device_box.append(image);
Box label_box = new Box(Orientation.VERTICAL, 0) { visible = true }; Box label_box = new Box(Orientation.VERTICAL, 0) { visible = true };
label_box.add(display_name_label); label_box.append(display_name_label);
if (device.detail_name != null) { if (device.detail_name != null) {
Label detail_name_label = new Label(device.detail_name) { xalign=0, visible=true }; Label detail_name_label = new Label(device.detail_name) { xalign=0, visible=true };
detail_name_label.get_style_context().add_class("dim-label"); detail_name_label.get_style_context().add_class("dim-label");
detail_name_label.attributes = new Pango.AttrList(); detail_name_label.attributes = new Pango.AttrList();
detail_name_label.attributes.insert(Pango.attr_scale_new(0.8)); detail_name_label.attributes.insert(Pango.attr_scale_new(0.8));
label_box.add(detail_name_label); label_box.append(detail_name_label);
} }
device_box.add(label_box); device_box.append(label_box);
ListBoxRow list_box_row = new ListBoxRow() { visible=true }; ListBoxRow list_box_row = new ListBoxRow() { visible=true };
list_box_row.add(device_box); list_box_row.set_child(device_box);
micro_list_box.add(list_box_row); micro_list_box.append(list_box_row);
row_microphone_device[list_box_row] = device; row_microphone_device[list_box_row] = device;
} }
@ -71,7 +71,7 @@ public class Dino.Ui.AudioSettingsPopover : Gtk.Popover {
microphone_selected(row_microphone_device[row]); microphone_selected(row_microphone_device[row]);
micro_list_box.unselect_row(row); micro_list_box.unselect_row(row);
}); });
micro_box.add(micro_frame); micro_box.append(micro_frame);
} }
return micro_box; return micro_box;
@ -82,10 +82,10 @@ public class Dino.Ui.AudioSettingsPopover : Gtk.Popover {
Gee.List<Plugins.MediaDevice> devices = call_plugin.get_devices("audio", true); Gee.List<Plugins.MediaDevice> devices = call_plugin.get_devices("audio", true);
Box speaker_box = new Box(Orientation.VERTICAL, 10) { visible=true }; Box speaker_box = new Box(Orientation.VERTICAL, 10) { visible=true };
speaker_box.add(new Label("<b>" + _("Speakers") +"</b>") { use_markup=true, xalign=0, visible=true }); speaker_box.append(new Label("<b>" + _("Speakers") +"</b>") { use_markup=true, xalign=0, visible=true });
if (devices.size == 0) { if (devices.size == 0) {
speaker_box.add(new Label(_("No speaker found."))); speaker_box.append(new Label(_("No speaker found.")));
} else { } else {
ListBox speaker_list_box = new ListBox() { activate_on_single_click=true, selection_mode=SelectionMode.SINGLE, visible=true }; ListBox speaker_list_box = new ListBox() { activate_on_single_click=true, selection_mode=SelectionMode.SINGLE, visible=true };
speaker_list_box.set_header_func(listbox_header_func); speaker_list_box.set_header_func(listbox_header_func);
@ -93,10 +93,10 @@ public class Dino.Ui.AudioSettingsPopover : Gtk.Popover {
}); });
Frame speaker_frame = new Frame(null) { visible=true }; Frame speaker_frame = new Frame(null) { visible=true };
speaker_frame.add(speaker_list_box); speaker_frame.set_child(speaker_list_box);
foreach (Plugins.MediaDevice device in devices) { foreach (Plugins.MediaDevice device in devices) {
Label display_name_label = new Label(device.display_name) { xalign=0, visible=true }; Label display_name_label = new Label(device.display_name) { xalign=0, visible=true };
Image image = new Image.from_icon_name("object-select-symbolic", IconSize.BUTTON) { visible=true }; Image image = new Image.from_icon_name("object-select-symbolic") { visible=true };
if (current_speaker_device == null || current_speaker_device.id != device.id) { if (current_speaker_device == null || current_speaker_device.id != device.id) {
image.opacity = 0; image.opacity = 0;
} }
@ -107,21 +107,21 @@ public class Dino.Ui.AudioSettingsPopover : Gtk.Popover {
image.opacity = 1; image.opacity = 1;
} }
}); });
Box device_box = new Box(Orientation.HORIZONTAL, 0) { spacing=7, margin=7, visible=true }; Box device_box = new Box(Orientation.HORIZONTAL, 0) { spacing=7, visible=true };
device_box.add(image); device_box.append(image);
Box label_box = new Box(Orientation.VERTICAL, 0) { visible = true }; Box label_box = new Box(Orientation.VERTICAL, 0) { visible = true };
label_box.add(display_name_label); label_box.append(display_name_label);
if (device.detail_name != null) { if (device.detail_name != null) {
Label detail_name_label = new Label(device.detail_name) { xalign=0, visible=true }; Label detail_name_label = new Label(device.detail_name) { xalign=0, visible=true };
detail_name_label.get_style_context().add_class("dim-label"); detail_name_label.get_style_context().add_class("dim-label");
detail_name_label.attributes = new Pango.AttrList(); detail_name_label.attributes = new Pango.AttrList();
detail_name_label.attributes.insert(Pango.attr_scale_new(0.8)); detail_name_label.attributes.insert(Pango.attr_scale_new(0.8));
label_box.add(detail_name_label); label_box.append(detail_name_label);
} }
device_box.add(label_box); device_box.append(label_box);
ListBoxRow list_box_row = new ListBoxRow() { visible=true }; ListBoxRow list_box_row = new ListBoxRow() { visible=true };
list_box_row.add(device_box); list_box_row.set_child(device_box);
speaker_list_box.add(list_box_row); speaker_list_box.append(list_box_row);
row_speaker_device[list_box_row] = device; row_speaker_device[list_box_row] = device;
} }
@ -130,7 +130,7 @@ public class Dino.Ui.AudioSettingsPopover : Gtk.Popover {
speaker_selected(row_speaker_device[row]); speaker_selected(row_speaker_device[row]);
speaker_list_box.unselect_row(row); speaker_list_box.unselect_row(row);
}); });
speaker_box.add(speaker_frame); speaker_box.append(speaker_frame);
} }
return speaker_box; return speaker_box;

View file

@ -9,63 +9,58 @@ public class Dino.Ui.CallBottomBar : Gtk.Box {
public bool audio_enabled { get; set; } public bool audio_enabled { get; set; }
public bool video_enabled { get; set; } public bool video_enabled { get; set; }
public static IconSize ICON_SIZE_MEDIADEVICE_BUTTON = Gtk.icon_size_register("im.dino.Dino.CALL_MEDIADEVICE_BUTTON", 10, 10);
public string counterpart_display_name { get; set; } public string counterpart_display_name { get; set; }
private Button audio_button = new Button() { height_request=45, width_request=45, halign=Align.START, valign=Align.START, visible=true }; private Button audio_button = new Button() { height_request=45, width_request=45, halign=Align.START, valign=Align.START, visible=true };
private Overlay audio_button_overlay = new Overlay() { visible=true }; private Overlay audio_button_overlay = new Overlay() { visible=true };
private Image audio_image = new Image() { visible=true }; private Image audio_image = new Image() { pixel_size=22 };
private MenuButton audio_settings_button = new MenuButton() { halign=Align.END, valign=Align.END }; private MenuButton audio_settings_button = new MenuButton() { icon_name="go-up-symbolic", halign=Align.END, valign=Align.END };
public AudioSettingsPopover? audio_settings_popover; public AudioSettingsPopover? audio_settings_popover;
private Button video_button = new Button() { height_request=45, width_request=45, halign=Align.START, valign=Align.START, visible=true }; private Button video_button = new Button() { height_request=45, width_request=45, halign=Align.START, valign=Align.START, visible=true };
private Overlay video_button_overlay = new Overlay() { visible=true }; private Overlay video_button_overlay = new Overlay() { visible=true };
private Image video_image = new Image() { visible=true }; private Image video_image = new Image() { pixel_size=22 };
private MenuButton video_settings_button = new MenuButton() { halign=Align.END, valign=Align.END }; private MenuButton video_settings_button = new MenuButton() { icon_name="go-up-symbolic", halign=Align.END, valign=Align.END };
public VideoSettingsPopover? video_settings_popover; public VideoSettingsPopover? video_settings_popover;
private Label label = new Label("") { margin=20, halign=Align.CENTER, valign=Align.CENTER, wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR, hexpand=true, visible=true }; private Label label = new Label("") { halign=Align.CENTER, valign=Align.CENTER, wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR, hexpand=true, visible=true };
private Stack stack = new Stack() { visible=true }; private Stack stack = new Stack() { visible=true };
public CallBottomBar() { public CallBottomBar() {
Object(orientation:Orientation.HORIZONTAL, spacing:0); Object(orientation:Orientation.HORIZONTAL, spacing:0);
Box main_buttons = new Box(Orientation.HORIZONTAL, 20) { margin_start=40, margin_end=40, margin=20, halign=Align.CENTER, hexpand=true, visible=true }; Box main_buttons = new Box(Orientation.HORIZONTAL, 20) { margin_start=40, margin_end=40, margin_bottom=20, margin_top=20, halign=Align.CENTER, hexpand=true, visible=true };
audio_button.add(audio_image); audio_button.set_child(audio_image);
audio_button.get_style_context().add_class("call-button"); audio_button.get_style_context().add_class("call-button");
audio_button.clicked.connect(() => { audio_enabled = !audio_enabled; }); audio_button.clicked.connect(() => { audio_enabled = !audio_enabled; });
audio_button.margin_end = audio_button.margin_bottom = 5; // space for the small settings button audio_button.margin_end = audio_button.margin_bottom = 5; // space for the small settings button
audio_button_overlay.add(audio_button); audio_button_overlay.set_child(audio_button);
audio_button_overlay.add_overlay(audio_settings_button); audio_button_overlay.add_overlay(audio_settings_button);
audio_settings_button.set_image(new Image.from_icon_name("go-up-symbolic", ICON_SIZE_MEDIADEVICE_BUTTON) { visible=true });
audio_settings_button.get_style_context().add_class("call-mediadevice-settings-button"); audio_settings_button.get_style_context().add_class("call-mediadevice-settings-button");
audio_settings_button.use_popover = true; main_buttons.append(audio_button_overlay);
main_buttons.add(audio_button_overlay);
video_button.add(video_image); video_button.set_child(video_image);
video_button.get_style_context().add_class("call-button"); video_button.get_style_context().add_class("call-button");
video_button.clicked.connect(() => { video_enabled = !video_enabled; }); video_button.clicked.connect(() => { video_enabled = !video_enabled; });
video_button.margin_end = video_button.margin_bottom = 5; video_button.margin_end = video_button.margin_bottom = 5;
video_button_overlay.add(video_button); video_button_overlay.set_child(video_button);
video_button_overlay.add_overlay(video_settings_button); video_button_overlay.add_overlay(video_settings_button);
video_settings_button.set_image(new Image.from_icon_name("go-up-symbolic", ICON_SIZE_MEDIADEVICE_BUTTON) { visible=true });
video_settings_button.get_style_context().add_class("call-mediadevice-settings-button"); video_settings_button.get_style_context().add_class("call-mediadevice-settings-button");
video_settings_button.use_popover = true; main_buttons.append(video_button_overlay);
main_buttons.add(video_button_overlay);
Button button_hang = new Button.from_icon_name("dino-phone-hangup-symbolic", IconSize.LARGE_TOOLBAR) { height_request=45, width_request=45, halign=Align.START, valign=Align.START, visible=true }; Button button_hang = new Button() { height_request=45, width_request=45, halign=Align.START, valign=Align.START, visible=true };
button_hang.set_child(new Image() { icon_name="dino-phone-hangup-symbolic", pixel_size=22 });
button_hang.get_style_context().add_class("call-button"); button_hang.get_style_context().add_class("call-button");
button_hang.get_style_context().add_class("destructive-action"); button_hang.get_style_context().add_class("destructive-action");
button_hang.clicked.connect(() => hang_up()); button_hang.clicked.connect(() => hang_up());
main_buttons.add(button_hang); main_buttons.append(button_hang);
label.get_style_context().add_class("text-no-controls"); label.get_style_context().add_class("text-no-controls");
stack.add_named(main_buttons, "control-buttons"); stack.add_named(main_buttons, "control-buttons");
stack.add_named(label, "label"); stack.add_named(label, "label");
this.add(stack); this.append(stack);
this.notify["audio-enabled"].connect(on_audio_enabled_changed); this.notify["audio-enabled"].connect(on_audio_enabled_changed);
this.notify["video-enabled"].connect(on_video_enabled_changed); this.notify["video-enabled"].connect(on_video_enabled_changed);
@ -85,18 +80,15 @@ public class Dino.Ui.CallBottomBar : Gtk.Box {
if (!show) return null; if (!show) return null;
audio_settings_popover = new AudioSettingsPopover(); audio_settings_popover = new AudioSettingsPopover();
audio_settings_button.popover = audio_settings_popover; audio_settings_button.popover = audio_settings_popover;
audio_settings_popover.microphone_selected.connect(() => { audio_settings_button.popdown(); });
audio_settings_popover.set_relative_to(audio_settings_button); audio_settings_popover.speaker_selected.connect(() => { audio_settings_button.popdown(); });
audio_settings_popover.microphone_selected.connect(() => { audio_settings_button.active = false; });
audio_settings_popover.speaker_selected.connect(() => { audio_settings_button.active = false; });
return audio_settings_popover; return audio_settings_popover;
} }
public void show_audio_device_error() { public void show_audio_device_error() {
audio_settings_button.set_image(new Image.from_icon_name("dialog-warning-symbolic", IconSize.BUTTON) { visible=true }); audio_settings_button.set_icon_name("dialog-warning-symbolic");
Util.force_error_color(audio_settings_button); Util.force_error_color(audio_settings_button);
} }
@ -106,28 +98,24 @@ public class Dino.Ui.CallBottomBar : Gtk.Box {
if (!show) return null; if (!show) return null;
video_settings_popover = new VideoSettingsPopover(); video_settings_popover = new VideoSettingsPopover();
video_settings_button.popover = video_settings_popover; video_settings_button.popover = video_settings_popover;
video_settings_popover.camera_selected.connect(() => { video_settings_button.popdown(); });
video_settings_popover.set_relative_to(video_settings_button);
video_settings_popover.camera_selected.connect(() => { video_settings_button.active = false; });
return video_settings_popover; return video_settings_popover;
} }
public void show_video_device_error() { public void show_video_device_error() {
video_settings_button.set_image(new Image.from_icon_name("dialog-warning-symbolic", IconSize.BUTTON) { visible=true }); video_settings_button.set_icon_name("dialog-warning-symbolic");
Util.force_error_color(video_settings_button); Util.force_error_color(video_settings_button);
} }
public void on_audio_enabled_changed() { public void on_audio_enabled_changed() {
if (audio_enabled) { if (audio_enabled) {
audio_image.set_from_icon_name("dino-microphone-symbolic", IconSize.LARGE_TOOLBAR); audio_image.icon_name = "dino-microphone-symbolic";
audio_button.get_style_context().add_class("white-button"); audio_button.get_style_context().add_class("white-button");
audio_button.get_style_context().remove_class("transparent-white-button"); audio_button.get_style_context().remove_class("transparent-white-button");
} else { } else {
audio_image.set_from_icon_name("dino-microphone-off-symbolic", IconSize.LARGE_TOOLBAR); audio_image.icon_name = "dino-microphone-off-symbolic";
audio_button.get_style_context().remove_class("white-button"); audio_button.get_style_context().remove_class("white-button");
audio_button.get_style_context().add_class("transparent-white-button"); audio_button.get_style_context().add_class("transparent-white-button");
} }
@ -135,12 +123,12 @@ public class Dino.Ui.CallBottomBar : Gtk.Box {
public void on_video_enabled_changed() { public void on_video_enabled_changed() {
if (video_enabled) { if (video_enabled) {
video_image.set_from_icon_name("dino-video-symbolic", IconSize.LARGE_TOOLBAR); video_image.icon_name = "dino-video-symbolic";
video_button.get_style_context().add_class("white-button"); video_button.get_style_context().add_class("white-button");
video_button.get_style_context().remove_class("transparent-white-button"); video_button.get_style_context().remove_class("transparent-white-button");
} else { } else {
video_image.set_from_icon_name("dino-video-off-symbolic", IconSize.LARGE_TOOLBAR); video_image.icon_name = "dino-video-off-symbolic";
video_button.get_style_context().remove_class("white-button"); video_button.get_style_context().remove_class("white-button");
video_button.get_style_context().add_class("transparent-white-button"); video_button.get_style_context().add_class("transparent-white-button");
} }
@ -152,6 +140,6 @@ public class Dino.Ui.CallBottomBar : Gtk.Box {
} }
public bool is_menu_active() { public bool is_menu_active() {
return video_settings_button.active || audio_settings_button.active; return video_settings_button.popover.visible || audio_settings_button.popover.visible; // TODO gtk4 does this work? check for null?
} }
} }

View file

@ -4,16 +4,16 @@ namespace Dino.Ui {
public class CallConnectionDetailsWindow : Gtk.Window { public class CallConnectionDetailsWindow : Gtk.Window {
public Box box = new Box(Orientation.VERTICAL, 15) { margin=10, halign=Align.CENTER, valign=Align.CENTER, visible=true }; public Box box = new Box(Orientation.VERTICAL, 15) { halign=Align.CENTER, valign=Align.CENTER, visible=true };
private bool video_added = false; private bool video_added = false;
private CallContentDetails audio_details = new CallContentDetails("Audio") { visible=true }; private CallContentDetails audio_details = new CallContentDetails("Audio") { visible=true };
private CallContentDetails video_details = new CallContentDetails("Video"); private CallContentDetails video_details = new CallContentDetails("Video");
public CallConnectionDetailsWindow() { public CallConnectionDetailsWindow() {
box.add(audio_details); box.append(audio_details);
box.add(video_details); box.append(video_details);
add(box); set_child(box);
} }
public void update_content(PeerInfo peer_info) { public void update_content(PeerInfo peer_info) {

View file

@ -2,61 +2,63 @@ using Dino.Entities;
using Gtk; using Gtk;
using Pango; using Pango;
public class Dino.Ui.CallEncryptionButton : MenuButton { public class Dino.Ui.CallEncryptionButtonController {
private Image encryption_image = new Image.from_icon_name("", IconSize.BUTTON) { visible=true };
private bool has_been_set = false; private bool has_been_set = false;
public bool controls_active { get; set; default=false; } public bool controls_active { get; set; default=false; }
public CallEncryptionButton() { public MenuButton button;
this.opacity = 0;
add(encryption_image);
this.set_popover(popover);
this.notify["controls-active"].connect(update_opacity); public CallEncryptionButtonController(MenuButton button) {
this.button = button;
button.opacity = 0;
// button.set_popover(popover);
button.notify["controls-active"].connect(update_opacity);
} }
public void set_icon(bool encrypted, string? icon_name) { public void set_icon(bool encrypted, string? icon_name) {
if (encrypted) { if (encrypted) {
encryption_image.set_from_icon_name(icon_name ?? "changes-prevent-symbolic", IconSize.BUTTON); button.icon_name = icon_name ?? "changes-prevent-symbolic";
get_style_context().remove_class("unencrypted"); button.get_style_context().remove_class("unencrypted");
} else { } else {
encryption_image.set_from_icon_name(icon_name ?? "changes-allow-symbolic", IconSize.BUTTON); button.icon_name = icon_name ?? "changes-allow-symbolic";
get_style_context().add_class("unencrypted"); button.get_style_context().add_class("unencrypted");
} }
has_been_set = true; has_been_set = true;
update_opacity(); update_opacity();
} }
public void set_info(string? title, bool show_keys, Xmpp.Xep.Jingle.ContentEncryption? audio_encryption, Xmpp.Xep.Jingle.ContentEncryption? video_encryption) { public void set_info(string? title, bool show_keys, Xmpp.Xep.Jingle.ContentEncryption? audio_encryption, Xmpp.Xep.Jingle.ContentEncryption? video_encryption) {
Popover popover = new Popover(this); Popover popover = new Popover();
this.set_popover(popover); button.set_popover(popover);
if (audio_encryption == null) { if (audio_encryption == null) {
popover.add(new Label("This call is unencrypted.") { margin=10, visible=true } ); popover.set_child(new Label("This call is unencrypted.") { visible=true } );
return; return;
} }
if (title != null && !show_keys) { if (title != null && !show_keys) {
popover.add(new Label(title) { use_markup=true, margin=10, visible=true } ); popover.set_child(new Label(title) { use_markup=true, visible=true } );
return; return;
} }
Box box = new Box(Orientation.VERTICAL, 10) { margin=10, visible=true }; Box box = new Box(Orientation.VERTICAL, 10) { visible=true };
box.add(new Label("<b>%s</b>".printf(title ?? "This call is end-to-end encrypted.")) { use_markup=true, xalign=0, visible=true }); box.append(new Label("<b>%s</b>".printf(title ?? "This call is end-to-end encrypted.")) { use_markup=true, xalign=0, visible=true });
if (video_encryption == null) { if (video_encryption == null) {
box.add(create_media_encryption_grid(audio_encryption)); box.append(create_media_encryption_grid(audio_encryption));
} else { } else {
box.add(new Label("<b>Audio</b>") { use_markup=true, xalign=0, visible=true }); box.append(new Label("<b>Audio</b>") { use_markup=true, xalign=0, visible=true });
box.add(create_media_encryption_grid(audio_encryption)); box.append(create_media_encryption_grid(audio_encryption));
box.add(new Label("<b>Video</b>") { use_markup=true, xalign=0, visible=true }); box.append(new Label("<b>Video</b>") { use_markup=true, xalign=0, visible=true });
box.add(create_media_encryption_grid(video_encryption)); box.append(create_media_encryption_grid(video_encryption));
} }
popover.add(box); popover.set_child(box);
} }
public void update_opacity() { public void update_opacity() {
this.opacity = controls_active && has_been_set ? 1 : 0; button.opacity = controls_active && has_been_set ? 1 : 0;
} }
private Grid create_media_encryption_grid(Xmpp.Xep.Jingle.ContentEncryption? encryption) { private Grid create_media_encryption_grid(Xmpp.Xep.Jingle.ContentEncryption? encryption) {

View file

@ -15,13 +15,15 @@ namespace Dino.Ui {
public Grid grid = new Grid() { visible=true }; public Grid grid = new Grid() { visible=true };
public CallBottomBar bottom_bar = new CallBottomBar() { visible=true }; public CallBottomBar bottom_bar = new CallBottomBar() { visible=true };
public Revealer bottom_bar_revealer = new Revealer() { valign=Align.END, transition_type=RevealerTransitionType.CROSSFADE, transition_duration=200, visible=true }; public Revealer bottom_bar_revealer = new Revealer() { valign=Align.END, transition_type=RevealerTransitionType.CROSSFADE, transition_duration=200, visible=true };
public HeaderBar header_bar = new HeaderBar() { valign=Align.START, halign=Align.END, show_close_button=true, visible=true, opacity=0.0 }; public HeaderBar header_bar = new HeaderBar() { valign=Align.START, halign=Align.END, show_title_buttons=true, visible=true, opacity=0.0 };
public Revealer header_bar_revealer = new Revealer() { halign=Align.END, valign=Align.START, transition_type=RevealerTransitionType.SLIDE_LEFT, transition_duration=200, visible=true, reveal_child=false }; public Revealer header_bar_revealer = new Revealer() { halign=Align.END, valign=Align.START, transition_type=RevealerTransitionType.SLIDE_LEFT, transition_duration=200, visible=true, reveal_child=false };
public Box own_video_box = new Box(Orientation.HORIZONTAL, 0) { halign=Align.END, valign=Align.END, visible=true }; public Box own_video_box = new Box(Orientation.HORIZONTAL, 0) { halign=Align.END, valign=Align.END, visible=true };
private Widget? own_video = null; private Widget? own_video = null;
private HashMap<string, ParticipantWidget> participant_widgets = new HashMap<string, ParticipantWidget>(); private HashMap<string, ParticipantWidget> participant_widgets = new HashMap<string, ParticipantWidget>();
private ArrayList<string> participants = new ArrayList<string>(); private ArrayList<string> participants = new ArrayList<string>();
private EventControllerMotion this_motion_events = new EventControllerMotion();
private int own_video_width = 150; private int own_video_width = 150;
private int own_video_height = 100; private int own_video_height = 100;
@ -31,32 +33,36 @@ namespace Dino.Ui {
construct { construct {
header_bar.get_style_context().add_class("call-header-bar"); header_bar.get_style_context().add_class("call-header-bar");
header_bar.custom_title = new Box(Orientation.VERTICAL, 0); header_bar.title_widget = new Box(Orientation.VERTICAL, 0);
header_bar.spacing = 0; // header_bar.spacing = 0;
header_bar_revealer.add(header_bar); header_bar_revealer.set_child(header_bar);
bottom_bar_revealer.add(bottom_bar); bottom_bar_revealer.set_child(bottom_bar);
own_video_box.get_style_context().add_class("own-video"); own_video_box.get_style_context().add_class("own-video");
this.get_style_context().add_class("dino-call-window"); this.get_style_context().add_class("dino-call-window");
overlay.add(grid); overlay.set_child(grid);
overlay.add_overlay(own_video_box); overlay.add_overlay(own_video_box);
overlay.add_overlay(bottom_bar_revealer); overlay.add_overlay(bottom_bar_revealer);
overlay.add_overlay(header_bar_revealer); overlay.add_overlay(header_bar_revealer);
overlay.get_child_position.connect(on_get_child_position); overlay.get_child_position.connect(on_get_child_position);
add(overlay); set_child(overlay);
} }
public CallWindow() { public CallWindow() {
this.bind_property("controls-active", bottom_bar_revealer, "reveal-child", BindingFlags.SYNC_CREATE); this.bind_property("controls-active", bottom_bar_revealer, "reveal-child", BindingFlags.SYNC_CREATE);
this.motion_notify_event.connect(reveal_control_elements); ((Widget) this).add_controller(this_motion_events);
this.enter_notify_event.connect(reveal_control_elements); this_motion_events.motion.connect(reveal_control_elements);
this.leave_notify_event.connect(reveal_control_elements); this_motion_events.enter.connect(reveal_control_elements);
this.configure_event.connect(reveal_control_elements); // upon resizing this_motion_events.leave.connect(reveal_control_elements);
this.configure_event.connect(reposition_participant_widgets); this.notify["default-width"].connect(reveal_control_elements);
this.notify["default-height"].connect(reveal_control_elements);
this.notify["default-width"].connect(reposition_participant_widgets);
this.notify["default-height"].connect(reposition_participant_widgets);
this.set_titlebar(new OutsideHeaderBar(this.header_bar) { visible=true }); this.set_titlebar(new OutsideHeaderBar(this.header_bar) { visible=true });
@ -103,11 +109,10 @@ namespace Dino.Ui {
} }
} }
private bool reposition_participant_widgets() { private void reposition_participant_widgets() {
int width, height; int width = get_size(Orientation.HORIZONTAL);
this.get_size(out width,out height); int height = get_size(Orientation.VERTICAL);
reposition_participant_widgets_rec(participants, width, height, 0, 0, 0, 0); reposition_participant_widgets_rec(participants, width, height, 0, 0, 0, 0);
return false;
} }
private void reposition_participant_widgets_rec(ArrayList<string> participants, int width, int height, int margin_top, int margin_right, int margin_bottom, int margin_left) { private void reposition_participant_widgets_rec(ArrayList<string> participants, int width, int height, int margin_top, int margin_right, int margin_bottom, int margin_left) {
@ -144,14 +149,14 @@ namespace Dino.Ui {
} }
public void set_own_video(Widget? widget_) { public void set_own_video(Widget? widget_) {
own_video_box.foreach((widget) => { own_video_box.remove(widget); }); // own_video_box.foreach((widget) => { own_video_box.remove(widget); });
own_video = widget_; own_video = widget_;
if (own_video == null) { if (own_video == null) {
own_video = new Box(Orientation.HORIZONTAL, 0) { expand=true }; own_video = new Box(Orientation.HORIZONTAL, 0) { hexpand=true, vexpand=true };
} }
own_video.visible = true; own_video.visible = true;
own_video_box.add(own_video); own_video_box.append(own_video);
} }
public void set_own_video_ratio(int width, int height) { public void set_own_video_ratio(int width, int height) {
@ -165,7 +170,7 @@ namespace Dino.Ui {
} }
public void unset_own_video() { public void unset_own_video() {
own_video_box.foreach((widget) => { own_video_box.remove(widget); }); // own_video_box.foreach((widget) => { own_video_box.remove(widget); });
} }
public void set_status(string participant_id, string state) { public void set_status(string participant_id, string state) {
@ -192,13 +197,12 @@ namespace Dino.Ui {
bottom_bar.show_counterpart_ended(text); bottom_bar.show_counterpart_ended(text);
} }
private bool reveal_control_elements() { private void reveal_control_elements() {
if (!bottom_bar_revealer.child_revealed) { if (!bottom_bar_revealer.child_revealed) {
controls_active = true; controls_active = true;
} }
timeout_hide_control_elements(); timeout_hide_control_elements();
return false;
} }
private void timeout_hide_control_elements() { private void timeout_hide_control_elements() {
@ -229,8 +233,8 @@ namespace Dino.Ui {
private bool on_get_child_position(Widget widget, out Gdk.Rectangle allocation) { private bool on_get_child_position(Widget widget, out Gdk.Rectangle allocation) {
if (widget == own_video_box) { if (widget == own_video_box) {
int width, height; int width = get_size(Orientation.HORIZONTAL);
this.get_size(out width,out height); int height = get_size(Orientation.VERTICAL);
allocation = Gdk.Rectangle(); allocation = Gdk.Rectangle();
allocation.width = own_video_width; allocation.width = own_video_width;
@ -252,8 +256,8 @@ namespace Dino.Ui {
public OutsideHeaderBar(HeaderBar header_bar) { public OutsideHeaderBar(HeaderBar header_bar) {
this.header_bar = header_bar; this.header_bar = header_bar;
size_allocate.connect_after(on_header_bar_size_allocate); // size_allocate.connect_after(on_header_bar_size_allocate);
header_bar.size_allocate.connect(on_header_bar_size_allocate); // header_bar.size_allocate.connect(on_header_bar_size_allocate);
} }
public void on_header_bar_size_allocate() { public void on_header_bar_size_allocate() {
@ -263,7 +267,7 @@ namespace Dino.Ui {
Allocation alloc; Allocation alloc;
get_allocation(out alloc); get_allocation(out alloc);
alloc.height = header_bar_alloc.height; alloc.height = header_bar_alloc.height;
set_allocation(alloc); // set_allocation(alloc);
} }
} }
} }

View file

@ -31,7 +31,7 @@ public class Dino.Ui.CallWindowController : Object {
this.stream_interactor = stream_interactor; this.stream_interactor = stream_interactor;
this.calls = stream_interactor.get_module(Calls.IDENTITY); this.calls = stream_interactor.get_module(Calls.IDENTITY);
this.own_video = call_plugin.create_widget(Plugins.WidgetType.GTK); this.own_video = call_plugin.create_widget(Plugins.WidgetType.GTK4);
call_window.set_default_size(704, 528); // 640x480 * 1.1 call_window.set_default_size(704, 528); // 640x480 * 1.1
@ -70,9 +70,10 @@ public class Dino.Ui.CallWindowController : Object {
call_window.destroy(); call_window.destroy();
this.dispose(); this.dispose();
}); });
call_window_handler_ids += call_window.destroy.connect(() => { call_window_handler_ids += call_window.close_request.connect(() => {
call_state.end(); call_state.end();
this.dispose(); this.dispose();
return false;
}); });
bottom_bar_handler_ids += call_window.bottom_bar.notify["audio-enabled"].connect(() => { bottom_bar_handler_ids += call_window.bottom_bar.notify["audio-enabled"].connect(() => {
call_state.mute_own_audio(!call_window.bottom_bar.audio_enabled); call_state.mute_own_audio(!call_window.bottom_bar.audio_enabled);
@ -81,17 +82,23 @@ public class Dino.Ui.CallWindowController : Object {
call_state.mute_own_video(!call_window.bottom_bar.video_enabled); call_state.mute_own_video(!call_window.bottom_bar.video_enabled);
update_own_video(); update_own_video();
}); });
call_window_handler_ids += call_window.configure_event.connect((event) => { call_window_handler_ids += call_window.notify["default-width"].connect((event) => {
if (window_width == -1 || window_height == -1) return false; if (call_window.default_width == -1) return;
int current_height = this.call_window.get_allocated_height();
int current_width = this.call_window.get_allocated_width(); int current_width = this.call_window.get_allocated_width();
if (window_width != current_width || window_height != current_height) { if (window_width != current_width) {
debug("Call window size changed by user. Disabling auto window-to-video size adaptation. %i->%i x %i->%i", window_width, current_width, window_height, current_height); debug("Call window size changed by user. Disabling auto window-to-video size adaptation. Width %i->%i", window_width, current_width);
window_size_changed = true; window_size_changed = true;
} }
return false;
}); });
call_window_handler_ids += call_window.realize.connect(() => { call_window_handler_ids += call_window.notify["default-height"].connect((event) => {
if (call_window.default_height == -1) return;
int current_height = this.call_window.get_allocated_height();
if (window_height != current_height) {
debug("Call window size changed by user. Disabling auto window-to-video size adaptation. Height %i->%i", window_height, current_height);
window_size_changed = true;
}
});
call_window_handler_ids += ((Widget)call_window).realize.connect(() => {
capture_window_size(); capture_window_size();
}); });
@ -138,7 +145,7 @@ public class Dino.Ui.CallWindowController : Object {
Gee.List<Account> acc_list = new ArrayList<Account>(Account.equals_func); Gee.List<Account> acc_list = new ArrayList<Account>(Account.equals_func);
acc_list.add(call.account); acc_list.add(call.account);
SelectContactDialog add_chat_dialog = new SelectContactDialog(stream_interactor, acc_list); SelectContactDialog add_chat_dialog = new SelectContactDialog(stream_interactor, acc_list);
add_chat_dialog.set_transient_for((Window) call_window.get_toplevel()); add_chat_dialog.set_transient_for((Window) call_window.get_root());
add_chat_dialog.title = _("Invite to Call"); add_chat_dialog.title = _("Invite to Call");
add_chat_dialog.ok_button.label = _("Invite"); add_chat_dialog.ok_button.label = _("Invite");
add_chat_dialog.selected.connect((account, jid) => { add_chat_dialog.selected.connect((account, jid) => {
@ -192,11 +199,11 @@ public class Dino.Ui.CallWindowController : Object {
} }
}); });
peer_state.encryption_updated.connect((audio_encryption, video_encryption, same) => { peer_state.encryption_updated.connect((audio_encryption, video_encryption, same) => {
update_encryption_indicator(participant_widgets[peer_id].encryption_button, audio_encryption, video_encryption, same); update_encryption_indicator(participant_widgets[peer_id].encryption_button_controller, audio_encryption, video_encryption, same);
}); });
} }
private void update_encryption_indicator(CallEncryptionButton encryption_button, Xep.Jingle.ContentEncryption? audio_encryption, Xep.Jingle.ContentEncryption? video_encryption, bool same) { private void update_encryption_indicator(CallEncryptionButtonController encryption_button, Xep.Jingle.ContentEncryption? audio_encryption, Xep.Jingle.ContentEncryption? video_encryption, bool same) {
string? title = null; string? title = null;
string? icon_name = null; string? icon_name = null;
bool show_keys = true; bool show_keys = true;
@ -235,24 +242,26 @@ public class Dino.Ui.CallWindowController : Object {
return true; return true;
}); });
conn_details_window.set_transient_for(call_window); conn_details_window.set_transient_for(call_window);
conn_details_window.destroy.connect(() => Source.remove(timeout_handle_id)); conn_details_window.close_request.connect(() => { Source.remove(timeout_handle_id); return false; });
conn_details_window.present(); conn_details_window.present();
this.call_window.destroy.connect(() => conn_details_window.close() ); this.call_window.close_request.connect(() => { conn_details_window.close(); return false; });
}); });
invite_handler_ids[participant_id] += participant_widget.invite_button_clicked.connect(() => invite_button_clicked()); invite_handler_ids[participant_id] += participant_widget.invite_button_clicked.connect(() => invite_button_clicked());
participant_widgets[participant_id] = participant_widget; participant_widgets[participant_id] = participant_widget;
call_window.add_participant(participant_id, participant_widget); call_window.add_participant(participant_id, participant_widget);
participant_videos[participant_id] = call_plugin.create_widget(Plugins.WidgetType.GTK); participant_videos[participant_id] = call_plugin.create_widget(Plugins.WidgetType.GTK4);
participant_videos[participant_id].resolution_changed.connect((width, height) => { participant_videos[participant_id].resolution_changed.connect((width, height) => {
if (window_size_changed || participant_widgets.size > 1) return; if (window_size_changed || participant_widgets.size > 1) return;
if (width == 0 || height == 0) return; if (width == 0 || height == 0) return;
if (width > height) { if (width > height) {
call_window.resize(704, (int) (height * 704 / width)); call_window.default_width = 704;
call_window.default_height = (int) (height * 704 / width);
} else { } else {
call_window.resize((int) (width * 704 / height), 704); call_window.default_width = (int) (width * 704 / height);
call_window.default_height = 704;
} }
capture_window_size(); capture_window_size();
}); });

View file

@ -6,15 +6,19 @@ using Gtk;
namespace Dino.Ui { namespace Dino.Ui {
public class ParticipantWidget : Gtk.Overlay { public class ParticipantWidget : Box {
public Overlay overlay = new Overlay();
public Widget main_widget; public Widget main_widget;
public HeaderBar header_bar = new HeaderBar() { valign=Align.START, visible=true }; public HeaderBar header_bar = new HeaderBar() { valign=Align.START, visible=true };
public Label title_label = new Label("");
public Label subtitle_label = new Label("");
public Box inner_box = new Box(Orientation.HORIZONTAL, 0) { margin_start=5, margin_top=5, hexpand=true, visible=true }; public Box inner_box = new Box(Orientation.HORIZONTAL, 0) { margin_start=5, margin_top=5, hexpand=true, visible=true };
public Box title_box = new Box(Orientation.VERTICAL, 0) { valign=Align.CENTER, hexpand=true, visible=true }; public Box title_box = new Box(Orientation.VERTICAL, 0) { valign=Align.CENTER, hexpand=true, visible=true };
public CallEncryptionButton encryption_button = new CallEncryptionButton() { opacity=0, relief=ReliefStyle.NONE, height_request=30, width_request=30, margin_end=5, visible=true }; public MenuButton encryption_button = new MenuButton() { opacity=0, has_frame=false, height_request=30, width_request=30, margin_end=5, visible=true };
public MenuButton menu_button = new MenuButton() { relief=ReliefStyle.NONE, visible=true }; public CallEncryptionButtonController encryption_button_controller;
public Button invite_button = new Button.from_icon_name("dino-account-plus") { relief=ReliefStyle.NONE, visible=true }; public MenuButton menu_button = new MenuButton() { icon_name="open-menu-symbolic", has_frame=false, visible=true };
public Button invite_button = new Button.from_icon_name("dino-account-plus") { has_frame=false, visible=true };
public bool shows_video = false; public bool shows_video = false;
public string? participant_name; public string? participant_name;
@ -26,19 +30,38 @@ namespace Dino.Ui {
public signal void debug_information_clicked(); public signal void debug_information_clicked();
public signal void invite_button_clicked(); public signal void invite_button_clicked();
class construct {
install_action("menu.debuginfo", null, (widget, action_name) => { ((ParticipantWidget) widget).debug_information_clicked(); });
}
public ParticipantWidget(string participant_name) { public ParticipantWidget(string participant_name) {
encryption_button_controller = new CallEncryptionButtonController(encryption_button);
this.participant_name = participant_name; this.participant_name = participant_name;
header_bar.title = participant_name;
Box titles_box = new Box(Orientation.VERTICAL, 0) { valign=Align.CENTER };
title_label.attributes = new AttrList();
title_label.attributes.insert(Pango.attr_weight_new(Weight.BOLD));
titles_box.append(title_label);
subtitle_label.attributes = new AttrList();
subtitle_label.attributes.insert(Pango.attr_scale_new(Pango.Scale.SMALL));
subtitle_label.get_style_context().add_class("dim-label");
titles_box.append(subtitle_label);
header_bar.set_title_widget(titles_box);
title_label.label = participant_name;
header_bar.get_style_context().add_class("participant-header-bar"); header_bar.get_style_context().add_class("participant-header-bar");
header_bar.pack_start(invite_button); header_bar.pack_start(invite_button);
header_bar.pack_start(encryption_button); header_bar.pack_start(encryption_button);
header_bar.pack_end(menu_button); header_bar.pack_end(menu_button);
menu_button.image = new Image.from_icon_name("open-menu-symbolic", IconSize.MENU); create_menu();
menu_button.set_popover(create_menu());
invite_button.clicked.connect(() => invite_button_clicked()); invite_button.clicked.connect(() => invite_button_clicked());
this.add_overlay(header_bar); this.append(overlay);
overlay.add_overlay(header_bar);
this.notify["controls-active"].connect(reveal_or_hide_controls); this.notify["controls-active"].connect(reveal_or_hide_controls);
this.notify["may-show-invite-button"].connect(reveal_or_hide_controls); this.notify["may-show-invite-button"].connect(reveal_or_hide_controls);
@ -48,7 +71,7 @@ namespace Dino.Ui {
this.is_highest_row = is_highest; this.is_highest_row = is_highest;
this.is_start_row = is_start; this.is_start_row = is_start;
header_bar.show_close_button = is_highest_row; header_bar.show_title_buttons = is_highest_row;
if (is_highest_row) { if (is_highest_row) {
header_bar.get_style_context().add_class("call-header-background"); header_bar.get_style_context().add_class("call-header-background");
Gtk.Settings? gtk_settings = Gtk.Settings.get_default(); Gtk.Settings? gtk_settings = Gtk.Settings.get_default();
@ -78,37 +101,35 @@ namespace Dino.Ui {
} else { } else {
avatar.set_text("?", false); avatar.set_text("?", false);
} }
box.add(avatar); box.append(avatar);
set_participant_widget(box); set_participant_widget(box);
} }
private void set_participant_widget(Widget widget) { private void set_participant_widget(Widget widget) {
widget.expand = true; widget.hexpand = widget.vexpand = true;
if (main_widget != null) this.remove(main_widget);
main_widget = widget; main_widget = widget;
this.add(main_widget); overlay.set_child(main_widget);
} }
private PopoverMenu create_menu() { private void create_menu() {
PopoverMenu menu = new PopoverMenu(); Menu menu_model = new Menu();
Box box = new Box(Orientation.VERTICAL, 0) { margin=10, visible=true }; menu_model.append(_("Debug information"), "menu.debuginfo");
ModelButton debug_information_button = new ModelButton() { text=_("Debug information"), visible=true }; Gtk.PopoverMenu popover_menu = new Gtk.PopoverMenu.from_model(menu_model);
debug_information_button.clicked.connect(() => debug_information_clicked()); menu_button.popover = popover_menu;
box.add(debug_information_button);
menu.add(box);
return menu;
} }
public void set_status(string state) { public void set_status(string state) {
subtitle_label.visible = true;
if (state == "requested") { if (state == "requested") {
header_bar.subtitle = _("Calling…"); subtitle_label.label = _("Calling…");
} else if (state == "ringing") { } else if (state == "ringing") {
header_bar.subtitle = _("Ringing…"); subtitle_label.label = _("Ringing…");
} else if (state == "establishing") { } else if (state == "establishing") {
header_bar.subtitle = _("Connecting…"); subtitle_label.label = _("Connecting…");
} else { } else {
header_bar.subtitle = ""; subtitle_label.visible = false;
} }
} }

View file

@ -11,10 +11,10 @@ public class Dino.Ui.VideoSettingsPopover : Gtk.Popover {
private HashMap<ListBoxRow, Plugins.MediaDevice> row_device = new HashMap<ListBoxRow, Plugins.MediaDevice>(); private HashMap<ListBoxRow, Plugins.MediaDevice> row_device = new HashMap<ListBoxRow, Plugins.MediaDevice>();
public VideoSettingsPopover() { public VideoSettingsPopover() {
Box box = new Box(Orientation.VERTICAL, 15) { margin=18, visible=true }; Box box = new Box(Orientation.VERTICAL, 15) { visible=true };
box.add(create_camera_box()); box.append(create_camera_box());
this.add(box); this.set_child(box);
} }
private Widget create_camera_box() { private Widget create_camera_box() {
@ -22,18 +22,18 @@ public class Dino.Ui.VideoSettingsPopover : Gtk.Popover {
Gee.List<Plugins.MediaDevice> devices = call_plugin.get_devices("video", false); Gee.List<Plugins.MediaDevice> devices = call_plugin.get_devices("video", false);
Box camera_box = new Box(Orientation.VERTICAL, 10) { visible=true }; Box camera_box = new Box(Orientation.VERTICAL, 10) { visible=true };
camera_box.add(new Label("<b>" + _("Cameras") + "</b>") { use_markup=true, xalign=0, visible=true, can_focus=true /* grab initial focus*/ }); camera_box.append(new Label("<b>" + _("Cameras") + "</b>") { use_markup=true, xalign=0, visible=true, can_focus=true /* grab initial focus*/ });
if (devices.size == 0) { if (devices.size == 0) {
camera_box.add(new Label(_("No camera found.")) { visible=true }); camera_box.append(new Label(_("No camera found.")) { visible=true });
} else { } else {
ListBox list_box = new ListBox() { activate_on_single_click=true, selection_mode=SelectionMode.SINGLE, visible=true }; ListBox list_box = new ListBox() { activate_on_single_click=true, selection_mode=SelectionMode.SINGLE, visible=true };
list_box.set_header_func(listbox_header_func); list_box.set_header_func(listbox_header_func);
Frame frame = new Frame(null) { visible=true }; Frame frame = new Frame(null) { visible=true };
frame.add(list_box); frame.set_child(list_box);
foreach (Plugins.MediaDevice device in devices) { foreach (Plugins.MediaDevice device in devices) {
Label display_name_label = new Label(device.display_name) { xalign=0, visible=true }; Label display_name_label = new Label(device.display_name) { xalign=0, visible=true };
Image image = new Image.from_icon_name("object-select-symbolic", IconSize.BUTTON) { visible=true }; Image image = new Image.from_icon_name("object-select-symbolic") { visible=true };
if (current_device == null || current_device.id != device.id) { if (current_device == null || current_device.id != device.id) {
image.opacity = 0; image.opacity = 0;
} }
@ -44,21 +44,21 @@ public class Dino.Ui.VideoSettingsPopover : Gtk.Popover {
image.opacity = 1; image.opacity = 1;
} }
}); });
Box device_box = new Box(Orientation.HORIZONTAL, 0) { spacing=7, margin=7, visible=true }; Box device_box = new Box(Orientation.HORIZONTAL, 0) { spacing=7, visible=true };
device_box.add(image); device_box.append(image);
Box label_box = new Box(Orientation.VERTICAL, 0) { visible = true }; Box label_box = new Box(Orientation.VERTICAL, 0) { visible = true };
label_box.add(display_name_label); label_box.append(display_name_label);
if (device.detail_name != null) { if (device.detail_name != null) {
Label detail_name_label = new Label(device.detail_name) { xalign=0, visible=true }; Label detail_name_label = new Label(device.detail_name) { xalign=0, visible=true };
detail_name_label.get_style_context().add_class("dim-label"); detail_name_label.get_style_context().add_class("dim-label");
detail_name_label.attributes = new Pango.AttrList(); detail_name_label.attributes = new Pango.AttrList();
detail_name_label.attributes.insert(Pango.attr_scale_new(0.8)); detail_name_label.attributes.insert(Pango.attr_scale_new(0.8));
label_box.add(detail_name_label); label_box.append(detail_name_label);
} }
device_box.add(label_box); device_box.append(label_box);
ListBoxRow list_box_row = new ListBoxRow() { visible=true }; ListBoxRow list_box_row = new ListBoxRow() { visible=true };
list_box_row.add(device_box); list_box_row.set_child(device_box);
list_box.add(list_box_row); list_box.append(list_box_row);
row_device[list_box_row] = device; row_device[list_box_row] = device;
} }
@ -67,7 +67,7 @@ public class Dino.Ui.VideoSettingsPopover : Gtk.Popover {
camera_selected(row_device[row]); camera_selected(row_device[row]);
list_box.unselect_row(row); list_box.unselect_row(row);
}); });
camera_box.add(frame); camera_box.append(frame);
} }
return camera_box; return camera_box;

View file

@ -34,9 +34,12 @@ public class ChatInputController : Object {
reset_input_field_status(); reset_input_field_status();
chat_input.chat_text_view.text_view.buffer.changed.connect(on_text_input_changed); var text_input_key_events = new EventControllerKey();
chat_input.chat_text_view.text_view.key_press_event.connect(on_text_input_key_press); text_input_key_events.key_pressed.connect(on_text_input_key_press);
chat_input.chat_text_view.text_view.add_controller(text_input_key_events);
chat_input.chat_text_view.text_view.paste_clipboard.connect(() => clipboard_pasted()); chat_input.chat_text_view.text_view.paste_clipboard.connect(() => clipboard_pasted());
chat_input.chat_text_view.text_view.buffer.changed.connect(on_text_input_changed);
chat_text_view_controller.send_text.connect(send_text); chat_text_view_controller.send_text.connect(send_text);
@ -50,7 +53,7 @@ public class ChatInputController : Object {
status_description_label.activate_link.connect((uri) => { status_description_label.activate_link.connect((uri) => {
if (uri == OPEN_CONVERSATION_DETAILS_URI){ if (uri == OPEN_CONVERSATION_DETAILS_URI){
ContactDetails.Dialog contact_details_dialog = new ContactDetails.Dialog(stream_interactor, conversation); ContactDetails.Dialog contact_details_dialog = new ContactDetails.Dialog(stream_interactor, conversation);
contact_details_dialog.set_transient_for((Gtk.Window) chat_input.get_toplevel()); contact_details_dialog.set_transient_for((Gtk.Window) chat_input.get_root());
contact_details_dialog.present(); contact_details_dialog.present();
} }
return true; return true;
@ -136,7 +139,7 @@ public class ChatInputController : Object {
case "/ping": case "/ping":
Xmpp.XmppStream? stream = stream_interactor.get_stream(conversation.account); Xmpp.XmppStream? stream = stream_interactor.get_stream(conversation.account);
try { try {
stream.get_module(Xmpp.Xep.Ping.Module.IDENTITY).send_ping.begin(stream, conversation.counterpart.with_resource(token[1]), null); stream.get_module(Xmpp.Xep.Ping.Module.IDENTITY).send_ping.begin(stream, conversation.counterpart.with_resource(token[1]));
} catch (Xmpp.InvalidJidError e) { } catch (Xmpp.InvalidJidError e) {
warning("Could not ping invalid Jid: %s", e.message); warning("Could not ping invalid Jid: %s", e.message);
} }
@ -184,8 +187,8 @@ public class ChatInputController : Object {
} }
} }
private bool on_text_input_key_press(EventKey event) { private bool on_text_input_key_press(uint keyval, uint keycode, Gdk.ModifierType state) {
if (event.keyval == Gdk.Key.Up && chat_input.chat_text_view.text_view.buffer.text == "") { if (keyval == Gdk.Key.Up && chat_input.chat_text_view.text_view.buffer.text == "") {
activate_last_message_correction(); activate_last_message_correction();
return true; return true;
} else { } else {

View file

@ -30,61 +30,64 @@ public class ChatTextViewController : Object {
} }
} }
public class ChatTextView : ScrolledWindow { public class ChatTextView : Box {
public signal void send_text(); public signal void send_text();
public signal void cancel_input(); public signal void cancel_input();
public TextView text_view = new TextView() { can_focus=true, hexpand=true, margin=8, wrap_mode=Gtk.WrapMode.WORD_CHAR, valign=Align.CENTER, visible=true }; public ScrolledWindow scrolled_window = new ScrolledWindow() { propagate_natural_height=true, max_content_height=300 };
public TextView text_view = new TextView() { hexpand=true, wrap_mode=Gtk.WrapMode.WORD_CHAR, valign=Align.CENTER, margin_top=7, margin_bottom=7 };
private int vscrollbar_min_height; private int vscrollbar_min_height;
private SmileyConverter smiley_converter; private SmileyConverter smiley_converter;
public EditHistory edit_history; // private SpellChecker spell_checker;
private SpellChecker spell_checker;
construct { construct {
max_content_height = 300; scrolled_window.set_child(text_view);
propagate_natural_height = true; this.append(scrolled_window);
this.add(text_view);
smiley_converter = new SmileyConverter(text_view); smiley_converter = new SmileyConverter(text_view);
edit_history = new EditHistory(text_view);
spell_checker = new SpellChecker(text_view);
this.get_vscrollbar().get_preferred_height(out vscrollbar_min_height, null); // scrolled_window.get_vscrollbar().get_preferred_size(out vscrollbar_min_size, null);
this.vadjustment.notify["upper"].connect_after(on_upper_notify); scrolled_window.vadjustment.notify["upper"].connect(on_upper_notify);
text_view.key_press_event.connect(on_text_input_key_press);
Gtk.drag_dest_unset(text_view); var text_input_key_events = new EventControllerKey();
text_input_key_events.key_pressed.connect(on_text_input_key_press);
text_view.add_controller(text_input_key_events);
text_view.realize.connect(() => {
var minimum_size = new Requisition();
scrolled_window.get_preferred_size(out minimum_size, null);
vscrollbar_min_height = minimum_size.height;
});
// Gtk.drag_dest_unset(text_view);
} }
public void initialize_for_conversation(Conversation conversation) { public void initialize_for_conversation(Conversation conversation) {
edit_history.initialize_for_conversation(conversation); // spell_checker.initialize_for_conversation(conversation);
spell_checker.initialize_for_conversation(conversation);
} }
public override void get_preferred_height(out int min_height, out int nat_height) { // public override void get_preferred_size(out Gtk.Requisition minimum_size, out Gtk.Requisition natural_size) {
base.get_preferred_height(out min_height, out nat_height); // base.get_preferred_height(out min_height, out nat_height);
min_height = nat_height; // min_height = nat_height;
} // }
private void on_upper_notify() { private void on_upper_notify() {
this.vadjustment.value = this.vadjustment.upper - this.vadjustment.page_size; scrolled_window.vadjustment.value = scrolled_window.vadjustment.upper - scrolled_window.vadjustment.page_size;
// hack for vscrollbar not requiring space and making textview higher //TODO doesn't resize immediately // hack for vscrollbar not requiring space and making textview higher //TODO doesn't resize immediately
this.get_vscrollbar().visible = (this.vadjustment.upper > this.max_content_height - 2 * this.vscrollbar_min_height); scrolled_window.get_vscrollbar().visible = (scrolled_window.vadjustment.upper > scrolled_window.max_content_height - 2 * this.vscrollbar_min_height);
} }
private bool on_text_input_key_press(EventKey event) { private bool on_text_input_key_press(uint keyval, uint keycode, Gdk.ModifierType state) {
if (event.keyval in new uint[]{Key.Return, Key.KP_Enter}) { if (keyval in new uint[]{ Key.Return, Key.KP_Enter }) {
if ((event.state & ModifierType.SHIFT_MASK) > 0) { if ((state & ModifierType.SHIFT_MASK) > 0) {
text_view.buffer.insert_at_cursor("\n", 1); text_view.buffer.insert_at_cursor("\n", 1);
} else if (text_view.buffer.text.strip() != "") { } else if (text_view.buffer.text.strip() != "") {
send_text(); send_text();
edit_history.reset_history();
} }
return true; return true;
} }
if (event.keyval == Key.Escape) { if (keyval == Key.Escape) {
cancel_input(); cancel_input();
} }
return false; return false;

View file

@ -1,77 +0,0 @@
using Gdk;
using Gee;
using Gtk;
using Dino.Entities;
namespace Dino.Ui {
public class EditHistory {
private Conversation? conversation;
private TextView text_input;
private HashMap<Conversation, Gee.List<string>> histories = new HashMap<Conversation, Gee.List<string>>(Conversation.hash_func, Conversation.equals_func);
private HashMap<Conversation, int> indices = new HashMap<Conversation, int>(Conversation.hash_func, Conversation.equals_func);
public EditHistory(TextView text_input) {
this.text_input = text_input;
text_input.key_press_event.connect(on_text_input_key_press);
text_input.cut_clipboard.connect_after(save_state);
text_input.paste_clipboard.connect_after(save_state);
text_input.move_cursor.connect_after(save_state);
text_input.button_release_event.connect_after(() => { save_state(); return false; });
}
public void initialize_for_conversation(Conversation conversation) {
this.conversation = conversation;
if (!histories.has_key(conversation)) {
reset_history();
}
}
public bool on_text_input_key_press(EventKey event) {
bool ctrl_pressed = (event.state & ModifierType.CONTROL_MASK) > 0;
if (ctrl_pressed && event.keyval == Key.z) {
undo();
} else if (ctrl_pressed && (event.keyval in new uint[]{ Key.Z, Key.y } )) {
redo();
} else if (event.keyval in new uint[]{ Key.space, Key.Tab, Key.ISO_Left_Tab }) {
save_state();
}
return false;
}
private void undo() {
save_state();
if (indices[conversation] > 0) {
indices[conversation] = indices[conversation] - 1;
text_input.buffer.text = histories[conversation][indices[conversation]];
}
}
private void redo() {
if (indices[conversation] < histories[conversation].size - 1) {
indices[conversation] = indices[conversation] + 1;
text_input.buffer.text = histories[conversation][indices[conversation]];
}
}
private void save_state() {
if (histories[conversation][indices[conversation]] == text_input.buffer.text) return;
if (indices[conversation] < histories[conversation].size - 1) {
histories[conversation] = histories[conversation].slice(0, indices[conversation] + 1);
}
histories[conversation].add(text_input.buffer.text);
indices[conversation] = indices[conversation] + 1;
}
public void reset_history() {
histories[conversation] = new ArrayList<string>();
histories[conversation].add("");
indices[conversation] = 0;
}
}
}

View file

@ -5,27 +5,25 @@ using Dino.Entities;
namespace Dino.Ui { namespace Dino.Ui {
public class EncryptionButton : MenuButton { public class EncryptionButton {
public signal void encryption_changed(Plugins.EncryptionListEntry? encryption_entry); public signal void encryption_changed(Plugins.EncryptionListEntry? encryption_entry);
private MenuButton menu_button;
private Conversation? conversation; private Conversation? conversation;
private RadioButton? button_unencrypted; private CheckButton? button_unencrypted;
private Map<RadioButton, Plugins.EncryptionListEntry> encryption_radios = new HashMap<RadioButton, Plugins.EncryptionListEntry>(); private Map<CheckButton, Plugins.EncryptionListEntry> encryption_radios = new HashMap<CheckButton, Plugins.EncryptionListEntry>();
private string? current_icon; private string? current_icon;
private StreamInteractor stream_interactor; private StreamInteractor stream_interactor;
public EncryptionButton(StreamInteractor stream_interactor) { public EncryptionButton(StreamInteractor stream_interactor, MenuButton menu_button) {
this.stream_interactor = stream_interactor; this.stream_interactor = stream_interactor;
this.menu_button = menu_button;
use_popover = true;
image = new Image.from_icon_name("changes-allow-symbolic", IconSize.BUTTON);
get_style_context().add_class("flat");
Builder builder = new Builder.from_resource("/im/dino/Dino/menu_encryption.ui"); Builder builder = new Builder.from_resource("/im/dino/Dino/menu_encryption.ui");
popover = builder.get_object("menu_encryption") as PopoverMenu; menu_button.popover = builder.get_object("menu_encryption") as PopoverMenu;
Box encryption_box = builder.get_object("encryption_box") as Box; Box encryption_box = builder.get_object("encryption_box") as Box;
button_unencrypted = builder.get_object("button_unencrypted") as RadioButton; button_unencrypted = builder.get_object("button_unencrypted") as CheckButton;
button_unencrypted.toggled.connect(encryption_button_toggled); button_unencrypted.toggled.connect(encryption_button_toggled);
stream_interactor.get_module(MucManager.IDENTITY).room_info_updated.connect((account, muc_jid) => { stream_interactor.get_module(MucManager.IDENTITY).room_info_updated.connect((account, muc_jid) => {
@ -36,17 +34,18 @@ public class EncryptionButton : MenuButton {
Application app = GLib.Application.get_default() as Application; Application app = GLib.Application.get_default() as Application;
foreach (var e in app.plugin_registry.encryption_list_entries) { foreach (var e in app.plugin_registry.encryption_list_entries) {
RadioButton btn = new RadioButton.with_label(button_unencrypted.get_group(), e.name); CheckButton btn = new CheckButton.with_label(e.name);
btn.set_group(button_unencrypted);
encryption_radios[btn] = e; encryption_radios[btn] = e;
btn.toggled.connect(encryption_button_toggled); btn.toggled.connect(encryption_button_toggled);
btn.visible = true; btn.visible = true;
encryption_box.pack_end(btn, false); encryption_box.prepend(btn);
} }
clicked.connect(update_encryption_menu_state); menu_button.activate.connect(update_encryption_menu_state);
} }
private void encryption_button_toggled() { private void encryption_button_toggled() {
foreach (RadioButton e in encryption_radios.keys) { foreach (CheckButton e in encryption_radios.keys) {
if (e.get_active()) { if (e.get_active()) {
conversation.encryption = encryption_radios[e].encryption; conversation.encryption = encryption_radios[e].encryption;
encryption_changed(encryption_radios[e]); encryption_changed(encryption_radios[e]);
@ -62,7 +61,7 @@ public class EncryptionButton : MenuButton {
} }
private void update_encryption_menu_state() { private void update_encryption_menu_state() {
foreach (RadioButton e in encryption_radios.keys) { foreach (CheckButton e in encryption_radios.keys) {
if (conversation.encryption == encryption_radios[e].encryption) { if (conversation.encryption == encryption_radios[e].encryption) {
e.set_active(true); e.set_active(true);
encryption_changed(encryption_radios[e]); encryption_changed(encryption_radios[e]);
@ -76,7 +75,7 @@ public class EncryptionButton : MenuButton {
private void set_icon(string icon) { private void set_icon(string icon) {
if (icon != current_icon) { if (icon != current_icon) {
image = new Image.from_icon_name(icon, IconSize.BUTTON); menu_button.set_icon_name(icon);
current_icon = icon; current_icon = icon;
} }
} }
@ -87,23 +86,23 @@ public class EncryptionButton : MenuButton {
private void update_visibility() { private void update_visibility() {
if (conversation.encryption != Encryption.NONE) { if (conversation.encryption != Encryption.NONE) {
visible = true; menu_button.visible = true;
return; return;
} }
switch (conversation.type_) { switch (conversation.type_) {
case Conversation.Type.CHAT: case Conversation.Type.CHAT:
visible = true; menu_button.visible = true;
break; break;
case Conversation.Type.GROUPCHAT_PM: case Conversation.Type.GROUPCHAT_PM:
visible = false; menu_button.visible = false;
break; break;
case Conversation.Type.GROUPCHAT: case Conversation.Type.GROUPCHAT:
visible = stream_interactor.get_module(MucManager.IDENTITY).is_private_room(conversation.account, conversation.counterpart); menu_button.visible = stream_interactor.get_module(MucManager.IDENTITY).is_private_room(conversation.account, conversation.counterpart);
break; break;
} }
} }
public new void set_conversation(Conversation conversation) { public void set_conversation(Conversation conversation) {
this.conversation = conversation; this.conversation = conversation;
update_encryption_menu_state(); update_encryption_menu_state();
update_encryption_menu_icon(); update_encryption_menu_icon();

View file

@ -27,16 +27,18 @@ public class OccupantsTabCompletor {
this.stream_interactor = stream_interactor; this.stream_interactor = stream_interactor;
this.text_input = text_input; this.text_input = text_input;
text_input.key_press_event.connect(on_text_input_key_press); var text_input_key_events = new EventControllerKey();
text_input_key_events.key_pressed.connect(on_text_input_key_press);
text_input.add_controller(text_input_key_events);
} }
public void initialize_for_conversation(Conversation conversation) { public void initialize_for_conversation(Conversation conversation) {
this.conversation = conversation; this.conversation = conversation;
} }
public bool on_text_input_key_press(EventKey event) { public bool on_text_input_key_press(uint keyval, uint keycode, Gdk.ModifierType state) {
if (conversation.type_ == Conversation.Type.GROUPCHAT) { if (conversation.type_ == Conversation.Type.GROUPCHAT) {
if (event.keyval == Key.Tab || event.keyval == Key.ISO_Left_Tab) { if (keyval == Key.Tab || keyval == Key.ISO_Left_Tab) {
string text = text_input.buffer.text; string text = text_input.buffer.text;
int start_index = int.max(text.last_index_of(" "), text.last_index_of("\n")) + 1; int start_index = int.max(text.last_index_of(" "), text.last_index_of("\n")) + 1;
string word = text.substring(start_index); string word = text.substring(start_index);
@ -51,11 +53,11 @@ public class OccupantsTabCompletor {
index = -1; index = -1;
} }
} }
if (event.keyval != Key.ISO_Group_Shift && active) { if (keyval != Key.ISO_Group_Shift && active) {
text_input.buffer.text = next_completion(event.keyval == Key.ISO_Left_Tab); text_input.buffer.text = next_completion(keyval == Key.ISO_Left_Tab);
return true; return true;
} }
} else if (event.keyval != Key.Shift_L && active) { } else if (keyval != Key.Shift_L && active) {
active = false; active = false;
} }
} }

View file

@ -33,11 +33,13 @@ class SmileyConverter {
public SmileyConverter(TextView text_input) { public SmileyConverter(TextView text_input) {
this.text_input = text_input; this.text_input = text_input;
text_input.key_press_event.connect(on_text_input_key_press); var text_input_key_events = new EventControllerKey();
text_input_key_events.key_pressed.connect(on_text_input_key_press);
text_input.add_controller(text_input_key_events);
} }
public bool on_text_input_key_press(EventKey event) { public bool on_text_input_key_press(uint keyval, uint keycode, Gdk.ModifierType state) {
if (event.keyval == Key.space || event.keyval == Key.Return) { if (keyval == Key.space || keyval == Key.Return) {
check_convert(); check_convert();
} }
return false; return false;

View file

@ -23,6 +23,8 @@ public class View : Box {
[GtkChild] public unowned ChatTextView chat_text_view; [GtkChild] public unowned ChatTextView chat_text_view;
[GtkChild] public unowned Box outer_box; [GtkChild] public unowned Box outer_box;
[GtkChild] public unowned Button file_button; [GtkChild] public unowned Button file_button;
[GtkChild] public unowned MenuButton emoji_button;
[GtkChild] public unowned MenuButton encryption_button;
[GtkChild] public unowned Separator file_separator; [GtkChild] public unowned Separator file_separator;
[GtkChild] public unowned Label chat_input_status; [GtkChild] public unowned Label chat_input_status;
@ -31,29 +33,13 @@ public class View : Box {
public View init(StreamInteractor stream_interactor) { public View init(StreamInteractor stream_interactor) {
this.stream_interactor = stream_interactor; this.stream_interactor = stream_interactor;
encryption_widget = new EncryptionButton(stream_interactor) { relief=ReliefStyle.NONE, margin_top=3, valign=Align.START, visible=true }; encryption_widget = new EncryptionButton(stream_interactor, encryption_button);
file_button.get_style_context().add_class("dino-attach-button"); EmojiChooser chooser = new EmojiChooser();
chooser.emoji_picked.connect((emoji) => {
encryption_widget.get_style_context().add_class("dino-chatinput-button"); chat_text_view.text_view.buffer.insert_at_cursor(emoji, emoji.data.length);
});
// Emoji button for emoji picker (recents don't work < 3.22.19, category icons don't work <3.23.2) emoji_button.set_popover(chooser);
if (Gtk.get_major_version() >= 3 && Gtk.get_minor_version() >= 24) {
MenuButton emoji_button = new MenuButton() { relief=ReliefStyle.NONE, margin_top=3, valign=Align.START, visible=true };
emoji_button.get_style_context().add_class("flat");
emoji_button.get_style_context().add_class("dino-chatinput-button");
emoji_button.image = new Image.from_icon_name("dino-emoticon-symbolic", IconSize.BUTTON) { visible=true };
EmojiChooser chooser = new EmojiChooser();
chooser.emoji_picked.connect((emoji) => {
chat_text_view.text_view.buffer.insert_at_cursor(emoji, emoji.data.length);
});
emoji_button.set_popover(chooser);
outer_box.add(emoji_button);
}
outer_box.add(encryption_widget);
Util.force_css(frame, "* { border-radius: 3px; }"); Util.force_css(frame, "* { border-radius: 3px; }");

View file

@ -14,7 +14,7 @@ public class BlockingProvider : Plugins.ContactDetailsProvider, Object {
} }
public void populate(Conversation conversation, Plugins.ContactDetails contact_details, Plugins.WidgetType type) { public void populate(Conversation conversation, Plugins.ContactDetails contact_details, Plugins.WidgetType type) {
if (type != Plugins.WidgetType.GTK) return; if (type != Plugins.WidgetType.GTK4) return;
if (conversation.type_ != Conversation.Type.CHAT) return; if (conversation.type_ != Conversation.Type.CHAT) return;
if (stream_interactor.get_module(BlockingManager.IDENTITY).is_supported(conversation.account)) { if (stream_interactor.get_module(BlockingManager.IDENTITY).is_supported(conversation.account)) {

View file

@ -37,7 +37,7 @@ public class Dialog : Gtk.Dialog {
title = conversation.type_ == Conversation.Type.GROUPCHAT ? _("Conference Details") : _("Contact Details"); title = conversation.type_ == Conversation.Type.GROUPCHAT ? _("Conference Details") : _("Contact Details");
if (Util.use_csd()) { if (Util.use_csd()) {
// TODO get_header_bar directly returns a HeaderBar in vala > 0.48 // TODO get_header_bar directly returns a HeaderBar in vala > 0.48
((HeaderBar) get_header_bar()).set_subtitle(Util.get_conversation_display_name(stream_interactor, conversation)); // ((HeaderBar) get_header_bar()).set_subtitle(Util.get_conversation_display_name(stream_interactor, conversation));
} }
setup_top(); setup_top();
@ -50,23 +50,24 @@ public class Dialog : Gtk.Dialog {
app.plugin_registry.register_contact_details_entry(new PermissionsProvider(stream_interactor)); app.plugin_registry.register_contact_details_entry(new PermissionsProvider(stream_interactor));
foreach (Plugins.ContactDetailsProvider provider in app.plugin_registry.contact_details_entries) { foreach (Plugins.ContactDetailsProvider provider in app.plugin_registry.contact_details_entries) {
provider.populate(conversation, contact_details, Plugins.WidgetType.GTK); provider.populate(conversation, contact_details, Plugins.WidgetType.GTK4);
} }
destroy.connect(() => { // destroy.connect(() => {
contact_details.save(); // contact_details.save();
}); // });
} }
private void setup_top() { private void setup_top() {
if (conversation.type_ == Conversation.Type.CHAT) { if (conversation.type_ == Conversation.Type.CHAT) {
name_label.visible = false; name_label.visible = false;
jid_label.margin_start = new Button().get_style_context().get_padding(StateFlags.NORMAL).left + 1; jid_label.margin_start = new Button().get_style_context().get_padding().left + 1;
name_hybrid.text = Util.get_conversation_display_name(stream_interactor, conversation); name_hybrid.text = Util.get_conversation_display_name(stream_interactor, conversation);
destroy.connect(() => { close_request.connect(() => {
if (name_hybrid.text != Util.get_conversation_display_name(stream_interactor, conversation)) { if (name_hybrid.text != Util.get_conversation_display_name(stream_interactor, conversation)) {
stream_interactor.get_module(RosterManager.IDENTITY).set_jid_handle(conversation.account, conversation.counterpart, name_hybrid.text); stream_interactor.get_module(RosterManager.IDENTITY).set_jid_handle(conversation.account, conversation.counterpart, name_hybrid.text);
} }
return false;
}); });
} else { } else {
name_hybrid.visible = false; name_hybrid.visible = false;
@ -84,18 +85,18 @@ public class Dialog : Gtk.Dialog {
ListBoxRow list_row = new ListBoxRow() { activatable=false, visible=true }; ListBoxRow list_row = new ListBoxRow() { activatable=false, visible=true };
Box row = new Box(Orientation.HORIZONTAL, 20) { margin_start=15, margin_end=15, margin_top=3, margin_bottom=3, visible=true }; Box row = new Box(Orientation.HORIZONTAL, 20) { margin_start=15, margin_end=15, margin_top=3, margin_bottom=3, visible=true };
list_row.add(row); list_row.set_child(row);
Label label_label = new Label(label) { xalign=0, yalign=0.5f, hexpand=true, visible=true }; Label label_label = new Label(label) { xalign=0, yalign=0.5f, hexpand=true, visible=true };
if (description != null && description != "") { if (description != null && description != "") {
Box box = new Box(Orientation.VERTICAL, 0) { visible=true }; Box box = new Box(Orientation.VERTICAL, 0) { visible=true };
box.add(label_label); box.append(label_label);
Label desc_label = new Label("") { xalign=0, yalign=0.5f, hexpand=true, visible=true }; Label desc_label = new Label("") { xalign=0, yalign=0.5f, hexpand=true, visible=true };
desc_label.set_markup("<span size='small'>%s</span>".printf(Markup.escape_text(description))); desc_label.set_markup("<span size='small'>%s</span>".printf(Markup.escape_text(description)));
desc_label.get_style_context().add_class("dim-label"); desc_label.get_style_context().add_class("dim-label");
box.add(desc_label); box.append(desc_label);
row.add(box); row.append(box);
} else { } else {
row.add(label_label); row.append(label_label);
} }
Widget widget = w; Widget widget = w;
@ -112,13 +113,13 @@ public class Dialog : Gtk.Dialog {
widget.margin_top = 5; widget.margin_top = 5;
row.add(widget); row.append(widget);
categories[category].add(list_row); categories[category].append(list_row);
int pref_height, pref_width; int pref_height, pref_width;
get_content_area().get_preferred_height(null, out pref_height); // get_content_area().get_preferred_height(null, out pref_height);
get_preferred_width(out pref_width, null); // get_preferred_width(out pref_width, null);
resize(pref_width, int.min(500, pref_height)); // resize(pref_width, int.min(500, pref_height));
} }
private void add_category(string category) { private void add_category(string category) {
@ -133,11 +134,11 @@ public class Dialog : Gtk.Dialog {
Box box = new Box(Orientation.VERTICAL, 5) { margin_top=12, margin_bottom=12, visible=true }; Box box = new Box(Orientation.VERTICAL, 5) { margin_top=12, margin_bottom=12, visible=true };
Label category_label = new Label("") { xalign=0, visible=true }; Label category_label = new Label("") { xalign=0, visible=true };
category_label.set_markup(@"<b>$(Markup.escape_text(category))</b>"); category_label.set_markup(@"<b>$(Markup.escape_text(category))</b>");
box.add(category_label); box.append(category_label);
Frame frame = new Frame(null) { visible=true }; Frame frame = new Frame(null) { visible=true };
frame.add(list_box); frame.set_child(list_box);
box.add(frame); box.append(frame);
main_box.add(box); main_box.append(box);
} }
} }
} }

View file

@ -15,7 +15,7 @@ public class MucConfigFormProvider : Plugins.ContactDetailsProvider, Object {
} }
public void populate(Conversation conversation, Plugins.ContactDetails contact_details, Plugins.WidgetType type) { public void populate(Conversation conversation, Plugins.ContactDetails contact_details, Plugins.WidgetType type) {
if (type != Plugins.WidgetType.GTK) return; if (type != Plugins.WidgetType.GTK4) return;
if (conversation.type_ == Conversation.Type.GROUPCHAT) { if (conversation.type_ == Conversation.Type.GROUPCHAT) {
Xmpp.XmppStream? stream = stream_interactor.get_stream(conversation.account); Xmpp.XmppStream? stream = stream_interactor.get_stream(conversation.account);
if (stream == null) return; if (stream == null) return;

View file

@ -14,13 +14,13 @@ public class PermissionsProvider : Plugins.ContactDetailsProvider, Object {
} }
public void populate(Conversation conversation, Plugins.ContactDetails contact_details, Plugins.WidgetType type) { public void populate(Conversation conversation, Plugins.ContactDetails contact_details, Plugins.WidgetType type) {
if (type != Plugins.WidgetType.GTK) return; if (type != Plugins.WidgetType.GTK4) return;
Xmpp.Jid? own_jid = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(conversation.counterpart, conversation.account); Xmpp.Jid? own_jid = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(conversation.counterpart, conversation.account);
if (own_jid == null) return; if (own_jid == null) return;
if (stream_interactor.get_module(MucManager.IDENTITY).get_role(own_jid, conversation.account) == Xmpp.Xep.Muc.Role.VISITOR){ if (stream_interactor.get_module(MucManager.IDENTITY).get_role(own_jid, conversation.account) == Xmpp.Xep.Muc.Role.VISITOR){
Button voice_request = new Button() {visible=true, label=_("Request")}; Button voice_request = new Button.with_label(_("Request")) { visible=true };
voice_request.clicked.connect(()=>stream_interactor.get_module(MucManager.IDENTITY).request_voice(conversation.account, conversation.counterpart)); voice_request.clicked.connect(()=>stream_interactor.get_module(MucManager.IDENTITY).request_voice(conversation.account, conversation.counterpart));
contact_details.add(_("Permissions"), _("Request permission to send messages"), "", voice_request); contact_details.add(_("Permissions"), _("Request permission to send messages"), "", voice_request);
} }

View file

@ -17,7 +17,7 @@ public class SettingsProvider : Plugins.ContactDetailsProvider, Object {
} }
public void populate(Conversation conversation, Plugins.ContactDetails contact_details, Plugins.WidgetType type) { public void populate(Conversation conversation, Plugins.ContactDetails contact_details, Plugins.WidgetType type) {
if (type != Plugins.WidgetType.GTK) return; if (type != Plugins.WidgetType.GTK4) return;
if (!stream_interactor.get<MucManager>().is_public_room(conversation.account, conversation.counterpart)) { if (!stream_interactor.get<MucManager>().is_public_room(conversation.account, conversation.counterpart)) {
string details_headline = conversation.type_ == Conversation.Type.GROUPCHAT ? DETAILS_HEADLINE_ROOM : DETAILS_HEADLINE_CHAT; string details_headline = conversation.type_ == Conversation.Type.GROUPCHAT ? DETAILS_HEADLINE_ROOM : DETAILS_HEADLINE_CHAT;

View file

@ -0,0 +1,36 @@
using Gtk;
namespace Dino.Ui {
public static ListItemFactory get_item_factory() {
SignalListItemFactory item_factory = new SignalListItemFactory();
item_factory.setup.connect((list_item) => { on_setup(list_item); });
item_factory.bind.connect((list_item) => { on_bind(list_item); });
return item_factory;
}
public static void on_setup(ListItem listitem) {
listitem.child = new ConversationItemWidget();
}
public static void on_bind(ListItem listitem) {
MessageViewModel view_model = (MessageViewModel) listitem.get_item();
ConversationItemWidget view = (ConversationItemWidget) listitem.get_child();
view_model.bind_property("name", view.name_label, "label", BindingFlags.SYNC_CREATE);
view_model.bind_property("time", view.time_label, "label", BindingFlags.SYNC_CREATE);
Label? label = view.content_widget as Label;
if (label == null) {
label = new Label("") { use_markup=true, xalign=0, selectable=true, wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR, hexpand=true, vexpand=true };
view.set_content_widget(label);
}
view_model.bind_property("message", label, "label", BindingFlags.SYNC_CREATE);
view_model.bind_property("encryption-icon-name", view.encrypted_image, "icon-name", BindingFlags.SYNC_CREATE);
view_model.bind_property("encryption-icon-tooltip", view.encrypted_image, "tooltip-text", BindingFlags.SYNC_CREATE);
view_model.bind_property("marked-icon-name", view.marked_image, "icon-name", BindingFlags.SYNC_CREATE);
view_model.bind_property("marked-icon-tooltip", view.marked_image, "tooltip-text", BindingFlags.SYNC_CREATE);
}
}

View file

@ -17,7 +17,7 @@ namespace Dino.Ui {
this.stream_interactor = stream_interactor; this.stream_interactor = stream_interactor;
} }
public override Object? get_widget(Plugins.WidgetType type) { public override Object? get_widget(Plugins.ConversationItemWidgetInterface outer, Plugins.WidgetType type) {
CallItem call_item = content_item as CallItem; CallItem call_item = content_item as CallItem;
CallState? call_state = stream_interactor.get_module(Calls.IDENTITY).call_states[call_item.call]; CallState? call_state = stream_interactor.get_module(Calls.IDENTITY).call_states[call_item.call];
return new CallWidget(stream_interactor, call_item.call, call_state, call_item.conversation) { visible=true }; return new CallWidget(stream_interactor, call_item.call, call_state, call_item.conversation) { visible=true };
@ -45,6 +45,7 @@ namespace Dino.Ui {
private Conversation conversation; private Conversation conversation;
public Call.State call_state { get; set; } // needs to be public for binding public Call.State call_state { get; set; } // needs to be public for binding
private uint time_update_handler_id = 0; private uint time_update_handler_id = 0;
private ArrayList<Widget> multiparty_peer_widgets = new ArrayList<Widget>();
construct { construct {
margin_top = 4; margin_top = 4;
@ -58,11 +59,11 @@ namespace Dino.Ui {
this.call = call; this.call = call;
this.conversation = conversation; this.conversation = conversation;
size_allocate.connect((allocation) => { // size_allocate.connect((allocation) => {
if (allocation.height > parent.get_allocated_height()) { // if (allocation.height > parent.get_allocated_height()) {
Idle.add(() => { parent.queue_resize(); return false; }); // Idle.add(() => { parent.queue_resize(); return false; });
} // }
}); // });
call.bind_property("state", this, "call-state"); call.bind_property("state", this, "call-state");
this.notify["call-state"].connect(update_call_state); this.notify["call-state"].connect(update_call_state);
@ -88,16 +89,20 @@ namespace Dino.Ui {
if (call.state != Call.State.IN_PROGRESS && call.state != Call.State.ENDED) return; if (call.state != Call.State.IN_PROGRESS && call.state != Call.State.ENDED) return;
if (call.counterparts.size <= 1 && conversation.type_ == Conversation.Type.CHAT) return; if (call.counterparts.size <= 1 && conversation.type_ == Conversation.Type.CHAT) return;
multiparty_peer_box.foreach((widget) => { multiparty_peer_box.remove(widget); }); foreach (Widget peer_widget in multiparty_peer_widgets) {
multiparty_peer_box.remove(peer_widget);
}
foreach (Jid counterpart in call.counterparts) { foreach (Jid counterpart in call.counterparts) {
AvatarImage image = new AvatarImage() { force_gray=true, margin_top=2, visible=true }; AvatarImage image = new AvatarImage() { force_gray=true, margin_top=2, visible=true };
image.set_conversation_participant(stream_interactor, conversation, counterpart.bare_jid); image.set_conversation_participant(stream_interactor, conversation, counterpart.bare_jid);
multiparty_peer_box.add(image); multiparty_peer_box.append(image);
multiparty_peer_widgets.add(image);
} }
AvatarImage image2 = new AvatarImage() { force_gray=true, margin_top=2, visible=true }; AvatarImage image2 = new AvatarImage() { force_gray=true, margin_top=2, visible=true };
image2.set_conversation_participant(stream_interactor, conversation, call.account.bare_jid); image2.set_conversation_participant(stream_interactor, conversation, call.account.bare_jid);
multiparty_peer_box.add(image2); multiparty_peer_box.append(image2);
multiparty_peer_widgets.add(image2);
outer_additional_box.get_style_context().add_class("multiparty-participants"); outer_additional_box.get_style_context().add_class("multiparty-participants");
@ -121,7 +126,7 @@ namespace Dino.Ui {
switch (relevant_state) { switch (relevant_state) {
case Call.State.RINGING: case Call.State.RINGING:
image.set_from_icon_name("dino-phone-ring-symbolic", IconSize.LARGE_TOOLBAR); image.set_from_icon_name("dino-phone-ring-symbolic");
if (call.direction == Call.DIRECTION_INCOMING) { if (call.direction == Call.DIRECTION_INCOMING) {
bool video = call_manager.should_we_send_video(); bool video = call_manager.should_we_send_video();
@ -146,7 +151,7 @@ namespace Dino.Ui {
break; break;
case Call.State.ESTABLISHING: case Call.State.ESTABLISHING:
case Call.State.IN_PROGRESS: case Call.State.IN_PROGRESS:
image.set_from_icon_name("dino-phone-in-talk-symbolic", IconSize.LARGE_TOOLBAR); image.set_from_icon_name("dino-phone-in-talk-symbolic");
title_label.label = _("Call started"); title_label.label = _("Call started");
string duration = get_duration_string((new DateTime.now_utc()).difference(call.local_time)); string duration = get_duration_string((new DateTime.now_utc()).difference(call.local_time));
subtitle_label.label = _("Started %s ago").printf(duration); subtitle_label.label = _("Started %s ago").printf(duration);
@ -162,13 +167,13 @@ namespace Dino.Ui {
break; break;
case Call.State.OTHER_DEVICE: case Call.State.OTHER_DEVICE:
image.set_from_icon_name("dino-phone-hangup-symbolic", IconSize.LARGE_TOOLBAR); image.set_from_icon_name("dino-phone-hangup-symbolic");
title_label.label = call.direction == Call.DIRECTION_INCOMING ? _("Incoming call") : _("Outgoing call"); title_label.label = call.direction == Call.DIRECTION_INCOMING ? _("Incoming call") : _("Outgoing call");
subtitle_label.label = _("You handled this call on another device"); subtitle_label.label = _("You handled this call on another device");
break; break;
case Call.State.ENDED: case Call.State.ENDED:
image.set_from_icon_name("dino-phone-hangup-symbolic", IconSize.LARGE_TOOLBAR); image.set_from_icon_name("dino-phone-hangup-symbolic");
title_label.label = _("Call ended"); title_label.label = _("Call ended");
string formated_end = Util.format_time(call.end_time, _("%H%M"), _("%l%M %p")); string formated_end = Util.format_time(call.end_time, _("%H%M"), _("%l%M %p"));
string duration = get_duration_string(call.end_time.difference(call.local_time)); string duration = get_duration_string(call.end_time.difference(call.local_time));
@ -177,7 +182,7 @@ namespace Dino.Ui {
_("Lasted %s").printf(duration); _("Lasted %s").printf(duration);
break; break;
case Call.State.MISSED: case Call.State.MISSED:
image.set_from_icon_name("dino-phone-missed-symbolic", IconSize.LARGE_TOOLBAR); image.set_from_icon_name("dino-phone-missed-symbolic");
title_label.label = _("Call missed"); title_label.label = _("Call missed");
if (call.direction == Call.DIRECTION_INCOMING) { if (call.direction == Call.DIRECTION_INCOMING) {
subtitle_label.label = _("You missed this call"); subtitle_label.label = _("You missed this call");
@ -187,7 +192,7 @@ namespace Dino.Ui {
} }
break; break;
case Call.State.DECLINED: case Call.State.DECLINED:
image.set_from_icon_name("dino-phone-hangup-symbolic", IconSize.LARGE_TOOLBAR); image.set_from_icon_name("dino-phone-hangup-symbolic");
title_label.label = _("Call declined"); title_label.label = _("Call declined");
if (call.direction == Call.DIRECTION_INCOMING) { if (call.direction == Call.DIRECTION_INCOMING) {
subtitle_label.label = _("You declined this call"); subtitle_label.label = _("You declined this call");
@ -197,7 +202,7 @@ namespace Dino.Ui {
} }
break; break;
case Call.State.FAILED: case Call.State.FAILED:
image.set_from_icon_name("dino-phone-hangup-symbolic", IconSize.LARGE_TOOLBAR); image.set_from_icon_name("dino-phone-hangup-symbolic");
title_label.label = _("Call failed"); title_label.label = _("Call failed");
subtitle_label.label = "Call failed to establish"; subtitle_label.label = "Call failed to establish";
break; break;

View file

@ -76,14 +76,14 @@ private class MetaChatStateItem : Plugins.MetaConversationItem {
this.jids = jids; this.jids = jids;
} }
public override Object? get_widget(Plugins.WidgetType widget_type) { public override Object? get_widget(Plugins.ConversationItemWidgetInterface outer, Plugins.WidgetType widget_type) {
label = new Label("") { xalign=0, vexpand=true, visible=true }; label = new Label("") { xalign=0, vexpand=true, visible=true };
label.get_style_context().add_class("dim-label"); label.get_style_context().add_class("dim-label");
image = new AvatarImage() { margin_top=2, valign=Align.START, visible=true }; image = new AvatarImage() { margin_top=2, valign=Align.START, visible=true };
Box image_content_box = new Box(Orientation.HORIZONTAL, 8) { visible=true }; Box image_content_box = new Box(Orientation.HORIZONTAL, 8) { visible=true };
image_content_box.add(image); image_content_box.append(image);
image_content_box.add(label); image_content_box.append(label);
update(); update();
return image_content_box; return image_content_box;

View file

@ -7,7 +7,16 @@ using Dino.Entities;
namespace Dino.Ui.ConversationSummary { namespace Dino.Ui.ConversationSummary {
public class ConversationItemSkeleton : EventBox { public class ConversationItemSkeleton : Plugins.ConversationItemWidgetInterface, Object {
public Grid main_grid { get; set; }
public Label name_label { get; set; }
public Label time_label { get; set; }
public AvatarImage avatar_image { get; set; }
public Image encryption_image { get; set; }
public Image received_image { get; set; }
public Widget? content_widget = null;
public bool show_skeleton { get; set; default=false; } public bool show_skeleton { get; set; default=false; }
public bool last_group_item { get; set; default=true; } public bool last_group_item { get; set; default=true; }
@ -20,188 +29,130 @@ public class ConversationItemSkeleton : EventBox {
public ContentMetaItem? content_meta_item = null; public ContentMetaItem? content_meta_item = null;
public Widget? widget = null; public Widget? widget = null;
private Box image_content_box = new Box(Orientation.HORIZONTAL, 8) { visible=true }; private uint time_update_timeout = 0;
private Box header_content_box = new Box(Orientation.VERTICAL, 0) { visible=true }; private ulong updated_roster_handler_id = 0;
private ItemMetaDataHeader? metadata_header = null;
private AvatarImage? image = null;
public ConversationItemSkeleton(StreamInteractor stream_interactor, Conversation conversation, Plugins.MetaConversationItem item, bool initial_item) { public ConversationItemSkeleton(StreamInteractor stream_interactor, Conversation conversation, Plugins.MetaConversationItem item, bool initial_item) {
this.stream_interactor = stream_interactor; this.stream_interactor = stream_interactor;
this.conversation = conversation; this.conversation = conversation;
this.item = item; this.item = item;
this.content_meta_item = item as ContentMetaItem; this.content_meta_item = item as ContentMetaItem;
this.get_style_context().add_class("message-box");
item.bind_property("in-edit-mode", this, "item-in-edit-mode"); Builder builder = new Builder.from_resource("/im/dino/Dino/conversation_item_widget.ui");
this.notify["item-in-edit-mode"].connect(update_edit_mode); main_grid = (Grid) builder.get_object("main_grid");
main_grid.get_style_context().add_class("message-box");
name_label = (Label) builder.get_object("name_label");
time_label = (Label) builder.get_object("time_label");
avatar_image = (AvatarImage) builder.get_object("avatar_image");
encryption_image = (Image) builder.get_object("encrypted_image");
received_image = (Image) builder.get_object("marked_image");
item.bind_property("mark", this, "item-mark", BindingFlags.SYNC_CREATE); widget = item.get_widget(this, Plugins.WidgetType.GTK4) as Widget;
this.notify["item-mark"].connect(update_error_mode);
update_error_mode();
widget = item.get_widget(Plugins.WidgetType.GTK) as Widget;
if (widget != null) { if (widget != null) {
widget.valign = Align.END; widget.valign = Align.END;
header_content_box.add(widget); set_widget(widget, Plugins.WidgetType.GTK4);
} }
image_content_box.add(header_content_box); if (item.requires_header) {
avatar_image.set_conversation_participant(stream_interactor, conversation, item.jid);
if (initial_item) {
this.add(image_content_box);
} else {
Revealer revealer = new Revealer() { transition_duration=200, transition_type=RevealerTransitionType.SLIDE_UP, reveal_child=false, visible=true };
revealer.add_with_properties(image_content_box);
this.add(revealer);
revealer.reveal_child = true;
} }
this.notify["show-skeleton"].connect(update_margin); this.notify["show-skeleton"].connect(update_margin);
this.notify["last-group-item"].connect(update_margin); this.notify["last-group-item"].connect(update_margin);
this.notify["show-skeleton"].connect(set_header);
update_margin(); update_margin();
} }
private void update_margin() { private void set_header() {
if (item.requires_header && show_skeleton && metadata_header == null) { if (!show_skeleton || !item.requires_header) return;
metadata_header = new ItemMetaDataHeader(stream_interactor, conversation, item) { visible=true };
header_content_box.add(metadata_header);
header_content_box.reorder_child(metadata_header, 0);
}
if (item.requires_avatar && show_skeleton && image == null) {
image = new AvatarImage() { margin_top=2, valign=Align.START, visible=true, allow_gray = false };
image.set_conversation_participant(stream_interactor, conversation, item.jid);
image_content_box.add(image);
image_content_box.reorder_child(image, 0);
}
if (image != null) {
image.visible = this.show_skeleton;
}
if (metadata_header != null) {
metadata_header.visible = this.show_skeleton;
}
image_content_box.margin_start = this.show_skeleton ? 15 : 58;
image_content_box.margin_end = 15;
if (this.show_skeleton && this.last_group_item) {
image_content_box.margin_top = 8;
image_content_box.margin_bottom = 8;
} else {
image_content_box.margin_top = 4;
image_content_box.margin_bottom = 4;
}
}
private void update_edit_mode() {
if (item.in_edit_mode) {
this.get_style_context().add_class("edit-mode");
} else {
this.get_style_context().remove_class("edit-mode");
}
}
private void update_error_mode() {
if (item_mark == Message.Marked.ERROR) {
this.get_style_context().add_class("error");
} else {
this.get_style_context().remove_class("error");
}
}
}
[GtkTemplate (ui = "/im/dino/Dino/conversation_content_view/item_metadata_header.ui")]
public class ItemMetaDataHeader : Box {
[GtkChild] public unowned Label name_label;
[GtkChild] public unowned Label time_label;
public Image received_image = new Image() { opacity=0.4 };
public Widget? encryption_image = null;
public static IconSize ICON_SIZE_HEADER = Gtk.icon_size_register("im.dino.Dino.HEADER_ICON", 17, 12);
private StreamInteractor stream_interactor;
private Conversation conversation;
private Plugins.MetaConversationItem item;
public Entities.Message.Marked item_mark { get; set; }
private ArrayList<Plugins.MetaConversationItem> items = new ArrayList<Plugins.MetaConversationItem>();
private uint time_update_timeout = 0;
private ulong updated_roster_handler_id = 0;
public ItemMetaDataHeader(StreamInteractor stream_interactor, Conversation conversation, Plugins.MetaConversationItem item) {
this.stream_interactor = stream_interactor;
this.conversation = conversation;
this.item = item;
items.add(item);
update_name_label(); update_name_label();
name_label.style_updated.connect(update_name_label); // name_label.style_updated.connect(update_name_label);
updated_roster_handler_id = stream_interactor.get_module(RosterManager.IDENTITY).updated_roster_item.connect((account, jid, roster_item) => { updated_roster_handler_id = stream_interactor.get_module(RosterManager.IDENTITY).updated_roster_item.connect((account, jid, roster_item) => {
if (this.conversation.account.equals(account) && this.conversation.counterpart.equals(jid)) { if (this.conversation.account.equals(account) && this.conversation.counterpart.equals(jid)) {
update_name_label(); update_name_label();
} }
}); });
conversation.notify["encryption"].connect(update_unencrypted_icon);
item.notify["encryption"].connect(update_encryption_icon); item.notify["encryption"].connect(update_encryption_icon);
update_encryption_icon(); update_encryption_icon();
this.add(received_image);
if (item.time != null) { if (item.time != null) {
update_time(); update_time();
} }
item.bind_property("mark", this, "item-mark"); item.bind_property("mark", this, "item-mark", BindingFlags.SYNC_CREATE);
this.notify["item-mark"].connect_after(update_received_mark); this.notify["item-mark"].connect_after(update_received_mark);
update_received_mark(); update_received_mark();
} }
public void set_widget(Object object, Plugins.WidgetType type) {
if (content_widget != null) content_widget.unparent();
Widget widget = (Widget) object;
content_widget = widget;
main_grid.attach(widget, 1, 1, 4, 1);
}
private void update_margin() {
avatar_image.visible = show_skeleton;
name_label.visible = show_skeleton;
time_label.visible = show_skeleton;
encryption_image.visible = show_skeleton;
received_image.visible = show_skeleton;
if (show_skeleton) {
main_grid.get_style_context().add_class("has-skeleton");
}
if (last_group_item) {
main_grid.get_style_context().add_class("last-group-item");
}
}
private void update_edit_mode() {
if (item.in_edit_mode) {
main_grid.get_style_context().add_class("edit-mode");
} else {
main_grid.get_style_context().remove_class("edit-mode");
}
}
private void update_error_mode() {
if (item_mark == Message.Marked.ERROR) {
main_grid.get_style_context().add_class("error");
} else {
main_grid.get_style_context().remove_class("error");
}
}
private void update_encryption_icon() { private void update_encryption_icon() {
encryption_image.visible = true;
Application app = GLib.Application.get_default() as Application; Application app = GLib.Application.get_default() as Application;
ContentMetaItem ci = item as ContentMetaItem; ContentMetaItem ci = item as ContentMetaItem;
if (item.encryption != Encryption.NONE && item.encryption != Encryption.UNKNOWN && ci != null) { if (item.encryption != Encryption.NONE && item.encryption != Encryption.UNKNOWN && ci != null) {
Widget? widget = null; string? icon_name = null;
foreach(var e in app.plugin_registry.encryption_list_entries) { foreach(var e in app.plugin_registry.encryption_list_entries) {
if (e.encryption == item.encryption) { if (e.encryption == item.encryption) {
widget = e.get_encryption_icon(conversation, ci.content_item) as Widget; icon_name = e.get_encryption_icon_name(conversation, ci.content_item);
break; break;
} }
} }
if (widget == null) { encryption_image.icon_name = icon_name ?? "dino-changes-prevent-symbolic";
widget = new Image.from_icon_name("dino-changes-prevent-symbolic", ICON_SIZE_HEADER) { opacity=0.4, visible = true };
}
update_encryption_image(widget);
} }
if (item.encryption == Encryption.NONE) { if (item.encryption == Encryption.NONE) {
update_unencrypted_icon(); if (conversation.encryption != Encryption.NONE) {
} encryption_image.icon_name = "dino-changes-allowed-symbolic";
} encryption_image.tooltip_text = _("Unencrypted");
Util.force_error_color(encryption_image);
private void update_unencrypted_icon() { } else if (conversation.encryption == Encryption.NONE) {
if (item.encryption != Encryption.NONE) return; encryption_image.icon_name = null;
encryption_image.visible = false;
if (conversation.encryption != Encryption.NONE && encryption_image == null) { }
Image image = new Image() { opacity=0.4, visible = true };
image.set_from_icon_name("dino-changes-allowed-symbolic", ICON_SIZE_HEADER);
image.tooltip_text = _("Unencrypted");
update_encryption_image(image);
Util.force_error_color(image);
} else if (conversation.encryption == Encryption.NONE && encryption_image != null) {
update_encryption_image(null);
}
}
private void update_encryption_image(Widget? widget) {
if (encryption_image != null) {
this.remove(encryption_image);
encryption_image = null;
}
if (widget != null) {
this.add(widget);
this.reorder_child(widget, 3);
encryption_image = widget;
} }
} }
@ -209,7 +160,7 @@ public class ItemMetaDataHeader : Box {
time_label.label = get_relative_time(item.time.to_local()).to_string(); time_label.label = get_relative_time(item.time.to_local()).to_string();
time_update_timeout = Timeout.add_seconds((int) get_next_time_change(), () => { time_update_timeout = Timeout.add_seconds((int) get_next_time_change(), () => {
if (this.parent == null) return false; if (this.main_grid.parent == null) return false;
update_time(); update_time();
return false; return false;
}); });
@ -220,41 +171,15 @@ public class ItemMetaDataHeader : Box {
} }
private void update_received_mark() { private void update_received_mark() {
bool all_received = true; switch (content_meta_item.mark) {
bool all_read = true; case Message.Marked.RECEIVED: received_image.icon_name = "dino-tick-symbolic"; break;
bool all_sent = true; case Message.Marked.READ: received_image.icon_name = "dino-double-tick-symbolic"; break;
foreach (Plugins.MetaConversationItem item in items) { case Message.Marked.WONTSEND:
if (item.mark == Message.Marked.WONTSEND) { received_image.icon_name = "dialog-warning-symbolic";
received_image.visible = true; received_image.icon_name = _("Unable to send message");
received_image.set_from_icon_name("dialog-warning-symbolic", ICON_SIZE_HEADER); // TODO error color on marked icon and time
Util.force_error_color(received_image); break;
Util.force_error_color(time_label); default: received_image.icon_name = null; break;
string error_text = _("Unable to send message");
received_image.tooltip_text = error_text;
time_label.tooltip_text = error_text;
return;
} else if (item.mark != Message.Marked.READ) {
all_read = false;
if (item.mark != Message.Marked.RECEIVED) {
all_received = false;
if (item.mark == Message.Marked.UNSENT) {
all_sent = false;
}
}
}
}
if (all_read) {
received_image.visible = true;
received_image.set_from_icon_name("dino-double-tick-symbolic", ICON_SIZE_HEADER);
} else if (all_received) {
received_image.visible = true;
received_image.set_from_icon_name("dino-tick-symbolic", ICON_SIZE_HEADER);
} else if (!all_sent) {
received_image.visible = true;
received_image.set_from_icon_name("image-loading-symbolic", ICON_SIZE_HEADER);
} else if (received_image.visible) {
received_image.set_from_icon_name("image-loading-symbolic", ICON_SIZE_HEADER);
} }
} }
@ -311,6 +236,10 @@ public class ItemMetaDataHeader : Box {
} }
} }
public Widget get_widget() {
return main_grid;
}
public override void dispose() { public override void dispose() {
if (time_update_timeout != 0) { if (time_update_timeout != 0) {
Source.remove(time_update_timeout); Source.remove(time_update_timeout);

Some files were not shown because too many files have changed in this diff Show more