diff options
author | fiaxh <git@lightrise.org> | 2022-02-14 14:55:59 +0100 |
---|---|---|
committer | fiaxh <git@lightrise.org> | 2022-07-27 20:34:20 +0200 |
commit | 7e7dcedaf31ee35499875491c9f569c575d28435 (patch) | |
tree | 0c5fee2b28baf320775fbc92b3c252e97d9d054f | |
parent | f25bfb00969a7e09996da2d5500e6718f4cc0148 (diff) | |
download | dino-7e7dcedaf31ee35499875491c9f569c575d28435.tar.gz dino-7e7dcedaf31ee35499875491c9f569c575d28435.zip |
Port from GTK3 to GTK4
160 files changed, 4526 insertions, 5450 deletions
diff --git a/cmake/FindGDK4.cmake b/cmake/FindGDK4.cmake new file mode 100644 index 00000000..74ef583e --- /dev/null +++ b/cmake/FindGDK4.cmake @@ -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)
\ No newline at end of file diff --git a/cmake/FindGTK4.cmake b/cmake/FindGTK4.cmake new file mode 100644 index 00000000..23819bca --- /dev/null +++ b/cmake/FindGTK4.cmake @@ -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) diff --git a/cmake/FindGspell.cmake b/cmake/FindGspell.cmake index 51c33c7e..de29ed1d 100644 --- a/cmake/FindGspell.cmake +++ b/cmake/FindGspell.cmake @@ -4,7 +4,7 @@ find_pkg_config_with_fallback(Gspell LIB_NAMES gspell-1 INCLUDE_NAMES gspell.h INCLUDE_DIR_SUFFIXES gspell-1 gspell-1/gspell - DEPENDS Gtk + DEPENDS GTK3 ) include(FindPackageHandleStandardArgs) diff --git a/libdino/src/entity/encryption.vala b/libdino/src/entity/encryption.vala index 193d741b..ab5a0ae0 100644 --- a/libdino/src/entity/encryption.vala +++ b/libdino/src/entity/encryption.vala @@ -1,12 +1,16 @@ namespace Dino.Entities { -public enum Encryption { + public enum Encryption { NONE, PGP, OMEMO, DTLS_SRTP, SRTP, - UNKNOWN, -} + UNKNOWN; + + public bool is_some() { + return this != NONE; + } + } }
\ No newline at end of file diff --git a/libdino/src/plugin/interfaces.vala b/libdino/src/plugin/interfaces.vala index c7c2c375..e4710732 100644 --- a/libdino/src/plugin/interfaces.vala +++ b/libdino/src/plugin/interfaces.vala @@ -12,7 +12,8 @@ public enum Priority { } public enum WidgetType { - GTK + GTK3, + GTK4 } 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 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 { @@ -45,15 +48,11 @@ public abstract class AccountSettingsEntry : Object { public abstract string name { get; } 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 void deactivate(); + + public abstract void set_account(Account account); + public abstract Object? get_widget(WidgetType type); } public interface ContactDetailsProvider : Object { @@ -76,10 +75,8 @@ public interface TextCommand : Object { public interface ConversationTitlebarEntry : Object { public abstract string id { 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 unset_conversation(); } @@ -146,10 +143,14 @@ public abstract class MetaConversationItem : Object { public bool requires_header { 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 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 class MessageAction : Object { public string icon_name; diff --git a/libdino/src/service/content_item_store.vala b/libdino/src/service/content_item_store.vala index c6c47af4..6371e00b 100644 --- a/libdino/src/service/content_item_store.vala +++ b/libdino/src/service/content_item_store.vala @@ -44,37 +44,11 @@ public class ContentItemStore : StreamInteractionModule, Object { Gee.TreeSet<ContentItem> items = new Gee.TreeSet<ContentItem>(ContentItem.compare_func); 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]; DateTime time = new DateTime.from_unix_utc(row[db.content_item.time]); - switch (provider) { - 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; - } + items.add(get_item(conversation, id, content_type, foreign_id, time)); } Gee.List<ContentItem> ret = new ArrayList<ContentItem>(); @@ -84,7 +58,42 @@ public class ContentItemStore : StreamInteractionModule, Object { 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() .with(db.content_item.content_type, "=", type) .with(db.content_item.foreign_id, "=", foreign_id); @@ -122,6 +131,26 @@ public class ContentItemStore : StreamInteractionModule, Object { 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) { long time = (long) item.time.to_unix(); QueryBuilder select = db.content_item.select() diff --git a/libdino/src/service/counterpart_interaction_manager.vala b/libdino/src/service/counterpart_interaction_manager.vala index cc5489c3..23db5762 100644 --- a/libdino/src/service/counterpart_interaction_manager.vala +++ b/libdino/src/service/counterpart_interaction_manager.vala @@ -154,7 +154,7 @@ public class CounterpartInteractionManager : StreamInteractionModule, Object { conversation.read_up_to = message; // 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; 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; diff --git a/libdino/src/service/message_correction.vala b/libdino/src/service/message_correction.vala index 322fa1c1..d5d15578 100644 --- a/libdino/src/service/message_correction.vala +++ b/libdino/src/service/message_correction.vala @@ -144,7 +144,7 @@ public class MessageCorrection : StreamInteractionModule, MessageListener { } 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) { received_correction(content_item); } diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 2326253c..4a952396 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -8,48 +8,50 @@ find_packages(MAIN_PACKAGES REQUIRED GLib GModule GObject - GTK3 + GTK4 ICU - Gspell ) set(RESOURCE_LIST 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-double-tick-symbolic.svg - icons/dino-emoticon-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-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-chat.svg icons/dino-status-dnd.svg icons/dino-status-online.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/dino-device-phone-symbolic.svg - - icons/dino-file-document-symbolic.svg - icons/dino-file-download-symbolic.svg - icons/dino-file-image-symbolic.svg - icons/dino-file-music-symbolic.svg - icons/dino-file-symbolic.svg - icons/dino-file-table-symbolic.svg - icons/dino-file-video-symbolic.svg + + icons/scalable/apps/im.dino.Dino-symbolic.svg + + icons/scalable/devices/dino-device-desktop-symbolic.svg + icons/scalable/devices/dino-device-phone-symbolic.svg + icons/scalable/devices/dino-phone-hangup-symbolic.svg + icons/scalable/devices/dino-phone-in-talk-symbolic.svg + icons/scalable/devices/dino-phone-missed-symbolic.svg + icons/scalable/devices/dino-phone-ring-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_groupchat_dialog.ui @@ -60,13 +62,13 @@ set(RESOURCE_LIST call_widget.ui chat_input.ui contact_details_dialog.ui + conversation_item_widget.ui conversation_list_titlebar.ui conversation_list_titlebar_csd.ui conversation_row.ui conversation_view.ui file_default_widget.ui file_send_overlay.ui - emojichooser.ui global_search.ui conversation_content_view/item_metadata_header.ui conversation_content_view/view.ui @@ -101,11 +103,11 @@ compile_gresources( unset(MAIN_EXTRA_OPTIONS) unset(MAIN_EXTRA_PACKAGES) -find_package(GDK3) -if(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) -endif(GDK3_WITH_X11) +# find_package(GDK3) +# if(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) +# endif(GDK3_WITH_X11) set(MAIN_EXTRA_OPTIONS ${MAIN_EXTRA_OPTIONS} --vapidir=${CMAKE_CURRENT_SOURCE_DIR}/vapi) vala_precompile(MAIN_VALA_C @@ -145,13 +147,23 @@ SOURCES src/ui/call_window/participant_widget.vala src/ui/call_window/video_settings_popover.vala + src/ui/conversation_content_view/call_widget.vala + src/ui/conversation_content_view/chat_state_populator.vala + src/ui/conversation_content_view/content_populator.vala + src/ui/conversation_content_view/conversation_item_skeleton.vala + src/ui/conversation_content_view/conversation_view.vala + src/ui/conversation_content_view/date_separator_populator.vala + src/ui/conversation_content_view/file_default_widget.vala + src/ui/conversation_content_view/file_image_widget.vala + src/ui/conversation_content_view/file_widget.vala + src/ui/conversation_content_view/message_widget.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/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 @@ -160,20 +172,8 @@ SOURCES 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/chat_state_populator.vala - src/ui/conversation_content_view/content_populator.vala - src/ui/conversation_content_view/conversation_item_skeleton.vala - src/ui/conversation_content_view/conversation_view.vala - src/ui/conversation_content_view/date_separator_populator.vala - src/ui/conversation_content_view/file_default_widget.vala - src/ui/conversation_content_view/file_image_widget.vala - src/ui/conversation_content_view/file_widget.vala - src/ui/conversation_content_view/message_widget.vala - src/ui/conversation_content_view/subscription_notification.vala + src/ui/conversation_selector/conversation_selector_row.vala src/ui/conversation_titlebar/call_entry.vala src/ui/conversation_titlebar/menu_entry.vala @@ -197,12 +197,10 @@ SOURCES src/ui/util/sizing_bin.vala src/ui/util/size_request_box.vala src/ui/util/scaling_image.vala - src/ui/util/preview_file_chooser_native.vala CUSTOM_VAPIS ${CMAKE_BINARY_DIR}/exports/xmpp-vala.vapi ${CMAKE_BINARY_DIR}/exports/qlite.vapi ${CMAKE_BINARY_DIR}/exports/dino_internal.vapi - vapi/emojichooser.vapi PACKAGES ${MAIN_PACKAGES} ${MAIN_EXTRA_PACKAGES} @@ -213,7 +211,7 @@ OPTIONS ) 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) target_include_directories(dino PRIVATE src) target_link_libraries(dino libdino ${MAIN_PACKAGES}) diff --git a/main/data/add_conversation/add_contact_dialog.ui b/main/data/add_conversation/add_contact_dialog.ui index 6185c5ba..1abc9a7a 100644 --- a/main/data/add_conversation/add_contact_dialog.ui +++ b/main/data/add_conversation/add_contact_dialog.ui @@ -1,128 +1,102 @@ <?xml version="1.0" encoding="UTF-8"?> <interface> + <requires lib="gtk" version="4.0"/> <template class="DinoUiAddContactDialog"> + <property name="title" translatable="1">Add Contact</property> <property name="valign">center</property> <property name="modal">True</property> <child type="titlebar"> <object class="GtkHeaderBar"> - <property name="title" translatable="yes">Add Contact</property> - <property name="visible">True</property> + <property name="show-title-buttons">False</property> <child> <object class="GtkButton" id="cancel_button"> - <property name="label" translatable="yes">Cancel</property> - <property name="sensitive">True</property> - <property name="visible">True</property> + <property name="label" translatable="1">Cancel</property> </object> - <packing> - <property name="pack_type">start</property> - </packing> </child> - <child> + <child type="end"> <object class="GtkButton" id="ok_button"> - <property name="has_default">True</property> - <property name="can_default">True</property> - <property name="label" translatable="yes">Add</property> - <property name="sensitive">False</property> - <property name="visible">True</property> + <property name="label" translatable="1">Add</property> + <property name="sensitive">0</property> <style> <class name="suggested-action"/> </style> </object> - <packing> - <property name="pack_type">end</property> - </packing> </child> </object> </child> - <child internal-child="vbox"> + <child internal-child="content_area"> <object class="GtkBox"> - <property name="visible">True</property> <child> <object class="GtkGrid" id="info_grid"> <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="column-spacing">10</property> - <property name="visible">True</property> <child> <object class="GtkLabel"> - <property name="label" translatable="yes">Account</property> + <property name="label" translatable="1">Account</property> <property name="xalign">1</property> - <property name="visible">True</property> + <layout> + <property name="column">0</property> + <property name="row">0</property> + </layout> </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> <object class="DinoUiAccountComboBox" id="account_combobox"> <property name="hexpand">True</property> <property name="width_request">200</property> <property name="visible">True</property> + <layout> + <property name="column">1</property> + <property name="row">0</property> + </layout> </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> <object class="GtkLabel"> <property name="label">JID</property> <property name="xalign">1</property> - <property name="visible">True</property> + <layout> + <property name="column">0</property> + <property name="row">1</property> + </layout> </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> <object class="GtkEntry" id="jid_entry"> - <property name="activates_default">True</property> - <property name="hexpand">True</property> + <property name="activates_default">1</property> + <property name="hexpand">1</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> - <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> <object class="GtkLabel"> - <property name="label" translatable="yes">Alias</property> + <property name="label" translatable="1">Alias</property> <property name="xalign">1</property> - <property name="visible">True</property> + <layout> + <property name="column">0</property> + <property name="row">2</property> + </layout> </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> <object class="GtkEntry" id="alias_entry"> - <property name="activates_default">True</property> - <property name="hexpand">True</property> + <property name="activates_default">1</property> + <property name="hexpand">1</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> - <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> </object> </child> @@ -133,4 +107,4 @@ <action-widget response="ok" default="true">ok_button</action-widget> </action-widgets> </template> -</interface> +</interface>
\ No newline at end of file diff --git a/main/data/add_conversation/add_groupchat_dialog.ui b/main/data/add_conversation/add_groupchat_dialog.ui index 29638f53..5acd6891 100644 --- a/main/data/add_conversation/add_groupchat_dialog.ui +++ b/main/data/add_conversation/add_groupchat_dialog.ui @@ -1,201 +1,165 @@ <?xml version="1.0" encoding="UTF-8"?> <interface> + <requires lib="gtk" version="4.0"/> <template class="DinoUiAddGroupchatDialog"> <property name="valign">center</property> <property name="modal">True</property> <child type="titlebar"> <object class="GtkHeaderBar"> - <property name="visible">True</property> + <property name="show-title-buttons">False</property> <child> <object class="GtkButton" id="cancel_button"> - <property name="label" translatable="yes">Cancel</property> - <property name="sensitive">True</property> - <property name="visible">True</property> + <property name="label" translatable="1">Cancel</property> </object> - <packing> - <property name="pack_type">start</property> - </packing> </child> - <child> + <child type="end"> <object class="GtkButton" id="ok_button"> - <property name="can_default">True</property> - <property name="has_default">True</property> - <property name="sensitive">False</property> - <property name="visible">True</property> + <property name="sensitive">0</property> <style> <class name="suggested-action"/> </style> </object> - <packing> - <property name="pack_type">end</property> - </packing> </child> </object> </child> - <child internal-child="vbox"> + <child internal-child="content_area"> <object class="GtkBox" id="main"> - <property name="visible">True</property> <child> <object class="GtkGrid"> <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-end">40</property> <property name="row-spacing">7</property> <property name="column-spacing">10</property> - <property name="visible">True</property> <child> <object class="GtkLabel"> - <property name="label" translatable="yes">Account</property> + <property name="label" translatable="1">Account</property> <property name="xalign">1</property> - <property name="visible">True</property> + <layout> + <property name="column">0</property> + <property name="row">0</property> + </layout> </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> <object class="GtkStack" id="accounts_stack"> - <property name="visible">True</property> <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> + <object class="GtkStackPage"> <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> - <object class="GtkLabel" id="account_label"> - <property name="xalign">0</property> - <property name="can_focus">True</property> - <property name="visible">True</property> - </object> - <packing> + <object class="GtkStackPage"> <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> + <layout> + <property name="column">1</property> + <property name="row">0</property> + </layout> </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> <object class="GtkLabel"> <property name="label">JID</property> <property name="xalign">1</property> - <property name="visible">True</property> + <layout> + <property name="column">0</property> + <property name="row">1</property> + </layout> </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> <object class="GtkEntry" id="jid_entry"> - <property name="activates_default">True</property> - <property name="hexpand">True</property> + <property name="activates_default">1</property> + <property name="hexpand">1</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> - <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> <object class="GtkLabel"> - <property name="label" translatable="yes">Nick</property> + <property name="label" translatable="1">Nick</property> <property name="xalign">1</property> - <property name="visible">True</property> + <layout> + <property name="column">0</property> + <property name="row">2</property> + </layout> </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> <object class="GtkEntry" id="nick_entry"> - <property name="activates_default">True</property> - <property name="hexpand">True</property> + <property name="activates_default">1</property> + <property name="hexpand">1</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> - <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 class="GtkLabel"> - <property name="label" translatable="yes">Password</property> + <property name="label" translatable="1">Password</property> <property name="xalign">1</property> - <property name="visible">True</property> + <layout> + <property name="column">0</property> + <property name="row">3</property> + </layout> </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> <object class="GtkEntry" id="password_entry"> - <property name="activates_default">True</property> - <property name="visibility">False</property> - <property name="hexpand">True</property> + <property name="activates_default">1</property> + <property name="visibility">0</property> + <property name="hexpand">1</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> - <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 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="visible">True</property> + <layout> + <property name="column">0</property> + <property name="row">5</property> + </layout> </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> <object class="GtkEntry" id="alias_entry"> - <property name="activates_default">True</property> - <property name="hexpand">True</property> + <property name="activates_default">1</property> + <property name="hexpand">1</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> - <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> </object> </child> @@ -206,4 +170,4 @@ <action-widget response="ok" default="true">ok_button</action-widget> </action-widgets> </template> -</interface> +</interface>
\ No newline at end of file diff --git a/main/data/add_conversation/conference_details_fragment.ui b/main/data/add_conversation/conference_details_fragment.ui index a4f6f5d3..0fdf2b8e 100644 --- a/main/data/add_conversation/conference_details_fragment.ui +++ b/main/data/add_conversation/conference_details_fragment.ui @@ -1,272 +1,238 @@ <?xml version="1.0" encoding="UTF-8"?> <interface> + <requires lib="gtk" version="4.0"/> <template class="DinoUiConferenceDetailsFragment"> <property name="visible">True</property> <child> <object class="GtkOverlay"> - <property name="visible">True</property> - <child> + <property name="child"> <object class="GtkGrid"> <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_start">40</property> <property name="row-spacing">7</property> <property name="column-spacing">10</property> - <property name="visible">True</property> <child> <object class="GtkLabel"> - <property name="label" translatable="yes">Account</property> + <property name="label" translatable="1">Account</property> <property name="xalign">1</property> - <property name="visible">True</property> <style> <class name="dim-label"/> </style> + <layout> + <property name="column">0</property> + <property name="row">0</property> + </layout> </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> <object class="GtkStack" id="accounts_stack"> - <property name="visible">True</property> <child> - <object class="GtkButton" id="accounts_button"> - <property name="relief">none</property> - <property name="visible">True</property> - <child> - <object class="GtkLabel" id="accounts_label"> - <property name="xalign">0</property> - <property name="visible">True</property> + <object class="GtkStackPage"> + <property name="name">label</property> + <property name="child"> + <object class="GtkButton" id="accounts_button"> + <property name="has-frame">False</property> + <child> + <object class="GtkLabel" id="accounts_label"> + <property name="xalign">0</property> + </object> + </child> </object> - </child> + </property> </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> + <object class="GtkStackPage"> <property name="name">entry</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> + <layout> + <property name="column">1</property> + <property name="row">0</property> + </layout> </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> <object class="GtkLabel"> <property name="label">JID</property> <property name="xalign">1</property> - <property name="visible">True</property> <style> <class name="dim-label"/> </style> + <layout> + <property name="column">0</property> + <property name="row">1</property> + </layout> </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> <object class="GtkStack" id="jid_stack"> - <property name="visible">True</property> <child> - <object class="GtkButton" id="jid_button"> - <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 class="GtkStackPage"> + <property name="name">label</property> + <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> - </child> + </property> </object> - <packing> - <property name="name">label</property> - </packing> </child> <child> - <object class="GtkEntry" id="jid_entry"> - <property name="hexpand">True</property> - <property name="width_request">200</property> - <property name="visible">True</property> - </object> - <packing> + <object class="GtkStackPage"> <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> + <layout> + <property name="column">1</property> + <property name="row">1</property> + </layout> </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> <object class="GtkLabel"> - <property name="label" translatable="yes">Nick</property> + <property name="label" translatable="1">Nick</property> <property name="xalign">1</property> - <property name="visible">True</property> <style> <class name="dim-label"/> </style> + <layout> + <property name="column">0</property> + <property name="row">2</property> + </layout> </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> <object class="GtkStack" id="nick_stack"> - <property name="visible">True</property> <child> - <object class="GtkButton" id="nick_button"> - <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 class="GtkStackPage"> + <property name="name">label</property> + <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> - </child> + </property> </object> - <packing> - <property name="name">label</property> - </packing> </child> <child> - <object class="GtkEntry" id="nick_entry"> - <property name="hexpand">True</property> - <property name="width_request">200</property> - <property name="visible">True</property> - </object> - <packing> + <object class="GtkStackPage"> <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> + <layout> + <property name="column">1</property> + <property name="row">2</property> + </layout> </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 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> <style> <class name="dim-label"/> </style> + <layout> + <property name="column">0</property> + <property name="row">3</property> + </layout> </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> <object class="GtkStack" id="password_stack"> + <property name="visible">0</property> <child> - <object class="GtkButton" id="password_button"> - <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 class="GtkStackPage"> + <property name="name">label</property> + <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> - </child> + </property> </object> - <packing> - <property name="name">label</property> - </packing> </child> <child> - <object class="GtkEntry" id="password_entry"> - <property name="hexpand">True</property> - <property name="width_request">200</property> - <property name="visibility">False</property> - <property name="visible">True</property> - </object> - <packing> + <object class="GtkStackPage"> <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> + <layout> + <property name="column">1</property> + <property name="row">3</property> + </layout> </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> </object> - <packing> - <property name="index">-1</property> - </packing> - </child> + </property> <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> + <property name="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> +<!-- <property name="shadow_type">none</property>--> <style> <class name="app-notification"/> </style> - <child> + <property name="child"> <object class="GtkBox" id="box2"> - <property name="visible">True</property> - <property name="can_focus">False</property> <property name="spacing">20</property> <child> - <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> + <object class="GtkLabel" id="notification_label"/> </child> <child> <object class="GtkButton" id="notification_button"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="relief">none</property> + <property name="focusable">1</property> + <property name="receives_default">1</property> + <property name="has-frame">False</property> <child> <object class="GtkImage" id="image2"> - <property name="visible">True</property> - <property name="can_focus">False</property> <property name="icon_name">window-close-symbolic</property> </object> </child> @@ -274,19 +240,14 @@ <class name="image-button"/> </style> </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> </child> </object> - </child> + </property> <child type="label_item"> <placeholder/> </child> </object> - </child> + </property> </object> </child> </object> diff --git a/main/data/add_conversation/list_row.ui b/main/data/add_conversation/list_row.ui index 2eabec11..06b6dc7f 100644 --- a/main/data/add_conversation/list_row.ui +++ b/main/data/add_conversation/list_row.ui @@ -1,62 +1,51 @@ <?xml version="1.0" encoding="UTF-8"?> <interface> - <template class="DinoUiListRow" parent="GtkListBoxRow"> - <property name="visible">True</property> + <requires lib="gtk" version="4.0"/> + <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> - <object class="GtkGrid" id="outer_grid"> - <property name="orientation">horizontal</property> - <property name="margin">3</property> - <property name="column-spacing">10</property> - <property name="visible">True</property> + <object class="DinoUiAvatarImage" id="image"> + <property name="allow_gray">False</property> + <property name="height">30</property> + <property name="width">30</property> + <property name="valign">center</property> + </object> + </child> + <child> + <object class="GtkGrid"> + <property name="orientation">vertical</property> <child> - <object class="DinoUiAvatarImage" id="image"> - <property name="allow_gray">False</property> - <property name="height">30</property> - <property name="width">30</property> - <property name="valign">center</property> - <property name="visible">True</property> + <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">0</property> + <property name="row">0</property> + </layout> </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> - <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 class="GtkLabel" id="via_label"> + <property name="max_width_chars">1</property> + <property name="ellipsize">end</property> + <property name="hexpand">1</property> + <property name="xalign">0</property> + <attributes> + <attribute name="scale" value="0.8"></attribute> + </attributes> + <layout> + <property name="column">0</property> + <property name="row">1</property> + </layout> </object> </child> </object> </child> - </template> + </object> </interface> diff --git a/main/data/add_conversation/select_jid_fragment.ui b/main/data/add_conversation/select_jid_fragment.ui index 50bc0e36..8ead3207 100644 --- a/main/data/add_conversation/select_jid_fragment.ui +++ b/main/data/add_conversation/select_jid_fragment.ui @@ -1,86 +1,70 @@ <?xml version="1.0" encoding="UTF-8"?> <interface> + <requires lib="gtk" version="4.0"/> <template class="DinoUiSelectJidFragment"> <property name="height_request">500</property> <property name="width_request">460</property> <property name="visible">True</property> <child> <object class="GtkGrid"> - <property name="expand">True</property> + <property name="hexpand">1</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-left">80</property> + <property name="margin-start">80</property> <property name="orientation">vertical</property> <property name="row-spacing">10</property> - <property name="visible">True</property> <child> <object class="GtkEntry" id="entry"> - <property name="activates_default">True</property> - <property name="hexpand">True</property> - <property name="visible">True</property> + <property name="activates_default">1</property> + <property name="hexpand">1</property> </object> </child> <child> <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="visible">True</property> <child> <object class="GtkFrame"> - <property name="visible">True</property> - <child> + <property name="child"> <object class="GtkScrolledWindow" id="scrolled_window"> <property name="hscrollbar_policy">never</property> - <property name="expand">True</property> - <property name="visible">True</property> - <child> + <property name="hexpand">1</property> + <property name="vexpand">1</property> + <property name="child"> <object class="GtkBox" id="box"> <property name="orientation">vertical</property> - <property name="visible">True</property> </object> - </child> + </property> </object> - </child> + </property> </object> </child> <child> - <object class="GtkToolbar"> - <property name="visible">True</property> + <object class="GtkBox"> + <property name="css-classes">toolbar</property> <style> + <class name="toolbar"/> <class name="inline-toolbar"/> </style> <child> - <object class="GtkToolItem"> - <property name="visible">True</property> + <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> + </child> + </object> + </child> + <child> + <object class="GtkButton" id="remove_button"> + <property name="sensitive">0</property> <child> - <object class="GtkBox"> - <property name="visible">True</property> - <child> - <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 class="GtkImage"> + <property name="icon-name">list-remove-symbolic</property> + <property name="icon-size">normal</property> </object> </child> </object> diff --git a/main/data/call_widget.ui b/main/data/call_widget.ui index 0c5d8bfa..41df8cfb 100644 --- a/main/data/call_widget.ui +++ b/main/data/call_widget.ui @@ -27,7 +27,6 @@ </style> <child> <object class="GtkImage" id="image"> - <property name="icon-size">5</property> <property name="opacity">0.7</property> <property name="visible">True</property> </object> @@ -73,7 +72,10 @@ <property name="halign">end</property> <property name="orientation">horizontal</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> <child> <object class="GtkButton" id="reject_call_button"> @@ -97,7 +99,10 @@ </child> <child> <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="hexpand">True</property> </object> diff --git a/main/data/chat_input.ui b/main/data/chat_input.ui index 3ddb29b9..9abd7eeb 100644 --- a/main/data/chat_input.ui +++ b/main/data/chat_input.ui @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <interface> - <requires lib="gtk+" version="3.22"/> + <requires lib="gtk" version="4.0"/> <template class="DinoUiChatInputView"> <property name="hexpand">True</property> <property name="orientation">vertical</property> @@ -12,46 +12,60 @@ <object class="GtkFrame" id="frame"> <property name="margin_start">14</property> <property name="margin_end">14</property> - <property name="visible">True</property> - <child> + <property name="child"> <object class="GtkBox" id="outer_box"> - <property name="orientation">horizontal</property> - <property name="visible">True</property> <child> <object class="GtkButton" id="file_button"> - <property name="tooltip-text" translatable="yes">Send a file</property> - <property name="can-focus">False</property> - <property name="margin-top">3</property> - <property name="relief">none</property> + <property name="icon-name">mail-attachment-symbolic</property> + <property name="tooltip-text" translatable="1">Send a file</property> + <property name="margin-top">2</property> <property name="valign">start</property> - <property name="visible">True</property> <style> <class name="flat"/> <class name="dino-chatinput-button"/> + <class name="dino-attach-button"/> <class name="image-button"/> </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> </child> <child> <object class="GtkSeparator" id="file_separator"> <property name="orientation">vertical</property> - <property name="visible">True</property> </object> </child> <child> <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> </child> </object> - </child> + </property> </object> </child> <child> @@ -60,9 +74,8 @@ <property name="margin_bottom">3</property> <property name="margin_start">14</property> <property name="margin_end">14</property> - <property name="visible">True</property> <attributes> - <attribute name="scale" value="0.8"/> + <attribute name="scale" value="0.8"></attribute> </attributes> <style> <class name="chat-input-status"/> diff --git a/main/data/contact_details_dialog.ui b/main/data/contact_details_dialog.ui index 4058bc77..322cd2df 100644 --- a/main/data/contact_details_dialog.ui +++ b/main/data/contact_details_dialog.ui @@ -1,122 +1,109 @@ <?xml version="1.0" encoding="UTF-8"?> <interface> + <requires lib="gtk" version="4.0"/> <template class="DinoUiContactDetailsDialog"> + <property name="title">Conversation Details</property> <property name="modal">True</property> <child type="titlebar"> <object class="GtkHeaderBar"> - <property name="title">Conversation Details</property> - <property name="show_close_button">True</property> - <property name="visible">True</property> </object> </child> - <child internal-child="vbox"> + <child internal-child="content_area"> <object class="GtkBox"> - <property name="expand">True</property> - <property name="visible">True</property> + <property name="hexpand">1</property> + <property name="vexpand">1</property> <child> <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="hscrollbar_policy">never</property> - <property name="expand">True</property> - <property name="visible">True</property> - <child> + <property name="hexpand">1</property> + <property name="vexpand">1</property> + <property name="child"> <object class="GtkBox"> <property name="orientation">vertical</property> - <property name="visible">True</property> <child> <object class="GtkGrid"> <property name="margin-top">20</property> <property name="margin-bottom">12</property> - <property name="margin-right">100</property> - <property name="margin-left">100</property> + <property name="margin-end">100</property> + <property name="margin-start">100</property> <property name="column-spacing">10</property> - <property name="visible">True</property> <child> <object class="DinoUiAvatarImage" id="avatar"> <property name="height">50</property> <property name="width">50</property> <property name="visible">True</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> - <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> <object class="DinoUiUtilEntryLabelHybrid" id="name_hybrid"> <property name="xalign">0</property> - <property name="expand">True</property> + <property name="hexpand">True</property> <property name="visible">True</property> + <layout> + <property name="column">1</property> + <property name="row">0</property> + </layout> </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> <object class="GtkLabel" id="name_label"> <property name="xalign">0</property> - <property name="expand">True</property> - <property name="visible">True</property> + <property name="hexpand">1</property> <attributes> - <attribute name="weight" value="PANGO_WEIGHT_BOLD"/> + <attribute name="weight" value="PANGO_WEIGHT_BOLD"></attribute> </attributes> + <layout> + <property name="column">1</property> + <property name="row">0</property> + </layout> </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> <object class="GtkLabel" id="jid_label"> <property name="xalign">0</property> <property name="yalign">0</property> - <property name="selectable">True</property> - <property name="expand">True</property> - <property name="visible">True</property> + <property name="selectable">1</property> + <property name="hexpand">1</property> + <layout> + <property name="column">1</property> + <property name="row">1</property> + </layout> </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> <object class="GtkLabel" id="account_label"> <property name="xalign">1</property> <property name="yalign">1</property> - <property name="margin">5</property> - <property name="expand">True</property> - <property name="visible">True</property> + <property name="margin-start">5</property> + <property name="margin-end">5</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> - <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> </object> </child> <child> <object class="GtkBox" id="main_box"> <property name="orientation">vertical</property> - <property name="margin-right">100</property> - <property name="margin-left">100</property> - <property name="visible">True</property> + <property name="margin-end">100</property> + <property name="margin-start">100</property> </object> </child> </object> - </child> + </property> </object> </child> </object> diff --git a/main/data/conversation_content_view/view.ui b/main/data/conversation_content_view/view.ui index 17f753f5..638129ec 100644 --- a/main/data/conversation_content_view/view.ui +++ b/main/data/conversation_content_view/view.ui @@ -1,117 +1,100 @@ <?xml version="1.0" encoding="UTF-8"?> <interface> + <requires lib="gtk" version="4.0"/> <template class="DinoUiConversationSummaryConversationView"> - <property name="expand">True</property> - <property name="homogeneous">False</property> - <property name="spacing">0</property> - <property name="visible">True</property> <child> <object class="GtkStack" id="stack"> <property name="transition_type">crossfade</property> - <property name="visible">True</property> <child> - <object class="GtkOverlay"> - <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 class="GtkStackPage"> + <property name="name">main</property> + <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> - </child> + </property> </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> + </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> - </child> - <child type="label_item"> - <placeholder/> - </child> + </property> </object> </child> </object> - </child> + </property> </object> - <packing> - <property name="name">main</property> - </packing> </child> <child> - <object class="GtkBox"> - <property name="visible">True</property> - </object> - <packing> + <object class="GtkStackPage"> <property name="name">void</property> - </packing> + <property name="child"> + <object class="GtkBox"/> + </property> + </object> </child> </object> </child> </template> -</interface> +</interface>
\ No newline at end of file diff --git a/main/data/conversation_item_widget.ui b/main/data/conversation_item_widget.ui new file mode 100644 index 00000000..e796006e --- /dev/null +++ b/main/data/conversation_item_widget.ui @@ -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>
\ No newline at end of file diff --git a/main/data/conversation_list_titlebar_csd.ui b/main/data/conversation_list_titlebar_csd.ui index 073f0a05..c99c302a 100644 --- a/main/data/conversation_list_titlebar_csd.ui +++ b/main/data/conversation_list_titlebar_csd.ui @@ -1,42 +1,31 @@ <?xml version="1.0" encoding="UTF-8"?> <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="show_close_button">True</property> - <property name="visible">True</property> <style> <class name="dino-left"/> </style> <child> <object class="GtkMenuButton" id="add_button"> - <property name="tooltip_text" translatable="yes">Start Conversation</property> - <property name="visible">True</property> + <property name="tooltip_text" translatable="True">Start Conversation</property> <child> <object class="GtkImage"> - <property name="visible">True</property> <property name="icon-name">list-add-symbolic</property> - <property name="icon-size">1</property> + <property name="icon-size">normal</property> </object> </child> </object> - <packing> - <property name="pack_type">start</property> - </packing> </child> - <child> + <child type="end"> <object class="GtkMenuButton" id="menu_button"> - <property name="visible">True</property> <child> <object class="GtkImage"> - <property name="visible">True</property> <property name="icon-name">open-menu-symbolic</property> - <property name="icon-size">1</property> + <property name="icon-size">normal</property> </object> </child> </object> - <packing> - <property name="pack_type">end</property> - </packing> </child> - </template> + </object> </interface> diff --git a/main/data/conversation_row.ui b/main/data/conversation_row.ui index ff08eac2..c164a7b5 100644 --- a/main/data/conversation_row.ui +++ b/main/data/conversation_row.ui @@ -6,12 +6,13 @@ <object class="GtkRevealer" id="main_revealer"> <property name="transition-type">slide-down</property> <property name="transition-duration">200</property> - <property name="reveal-child">False</property> + <property name="reveal-child">True</property> <property name="visible">True</property> <child> <object class="GtkBox"> <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-end">14</property> <property name="visible">True</property> @@ -25,7 +26,7 @@ </child> <child> <object class="GtkBox"> - <property name="margin-left">10</property> + <property name="margin-start">10</property> <property name="orientation">vertical</property> <property name="visible">True</property> <child> @@ -37,8 +38,8 @@ <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="margin-right">7</property> + <property name="hexpand">True</property> + <property name="margin-end">7</property> <property name="xalign">0</property> <property name="visible">True</property> </object> @@ -86,7 +87,8 @@ <object class="GtkLabel" id="message_label"> <property name="max_width_chars">1</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="xalign">0</property> <attributes> diff --git a/main/data/conversation_view.ui b/main/data/conversation_view.ui index c968f992..9515a7bd 100644 --- a/main/data/conversation_view.ui +++ b/main/data/conversation_view.ui @@ -1,75 +1,76 @@ <?xml version="1.0" encoding="UTF-8"?> <interface> - <template class="DinoUiConversationView" parent="GtkOverlay"> + <template class="DinoUiConversationView"> <property name="visible">True</property> <child> - <object class="GtkBox"> - <property name="orientation">vertical</property> - <property name="visible">True</property> - <style> - <class name="dino-conversation"/> - </style> + <object class="GtkOverlay" id="overlay"> <child> - <object class="GtkOverlay"> - <property name="visible">True</property> + <object class="GtkBox"> + <property name="orientation">vertical</property> + <style> + <class name="dino-conversation"/> + </style> <child> - <object class="DinoUiConversationSummaryConversationView" id="conversation_frame"> - <property name="visible">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> + <object class="GtkOverlay" id="conversation_overlay"> + <property name="vexpand">True</property> <child> - <object class="GtkButton" id="goto_end_button"> - <property name="width-request">30</property> - <property name="height-request">30</property> - <property name="vexpand">False</property> + <object class="DinoUiConversationSummaryConversationView" id="conversation_frame"> + </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> - <style> - <class name="circular-button"/> - </style> + <property name="margin-end">30</property> + <property name="margin-bottom">30</property> <child> - <object class="GtkImage"> - <property name="icon-name">go-down-symbolic</property> - <property name="icon-size">1</property> + <object class="GtkButton" id="goto_end_button"> + <property name="width-request">30</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> + <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> </child> </object> </child> </object> - + </child> + <child> + <object class="DinoUiChatInputView" id="chat_input"> + <property name="visible">True</property> + </object> </child> </object> </child> - <child> - <object class="DinoUiChatInputView" id="chat_input"> - <property name="visible">True</property> - </object> - </child> - </object> - </child> - <child type="overlay"> - <object class="GtkRevealer" id="white_revealer"> - <property name="transition-type">crossfade</property> - <property name="transition-duration">200</property> - <property name="reveal-child">False</property> - <property name="visible">False</property> - <child> - <object class="GtkBox"> - <property name="opacity">0.7</property> - <property name="visible">true</property> - <style> - <class name="dino-white-overlay"/> - </style> + <child type="overlay"> + <object class="GtkRevealer" id="white_revealer"> + <property name="transition-type">crossfade</property> + <property name="transition-duration">200</property> + <property name="reveal-child">False</property> + <property name="visible">False</property> + <child> + <object class="GtkBox"> + <property name="opacity">0.7</property> + <property name="visible">true</property> + <style> + <class name="dino-white-overlay"/> + </style> + </object> + </child> </object> </child> </object> diff --git a/main/data/emojichooser.ui b/main/data/emojichooser.ui deleted file mode 100644 index d47a2c22..00000000 --- a/main/data/emojichooser.ui +++ /dev/null @@ -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 & 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 & 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 & 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 & 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 & 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 & 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 & 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 & 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 & 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 & 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> diff --git a/main/data/file_default_widget.ui b/main/data/file_default_widget.ui index 6315e230..77ddebd1 100644 --- a/main/data/file_default_widget.ui +++ b/main/data/file_default_widget.ui @@ -1,110 +1,93 @@ <?xml version="1.0" encoding="UTF-8"?> <interface> - <template class="DinoUiFileDefaultWidget" parent="GtkEventBox"> - <property name="halign">start</property> - <style> - <class name="file-box-outer"/> - </style> + <requires lib="gtk" version="4.0"/> + <template class="DinoUiFileDefaultWidget"> + <property name="halign">start</property> + <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> - <object class="DinoUiSizingBin"> - <property name="target-width">500</property> - <property name="max-width">500</property> - <property name="hexpand">True</property> - <property name="visible">True</property> + <object class="GtkBox"> + <property name="hexpand">1</property> + <property name="spacing">10</property> + <style> + <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> - <object class="GtkBox"> - <property name="orientation">horizontal</property> - <property name="halign">fill</property> - <property name="hexpand">true</property> - <property name="spacing">10</property> - <property name="visible">True</property> - <style> - <class name="file-box"/> - </style> - <child> - <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> + <object class="GtkStackPage"> + <property name="name">download_image</property> + <property name="child"> + <object class="GtkImage"> + <property name="icon-name">dino-file-download-symbolic</property> + <property name="pixel-size">30</property> + <property name="opacity">0.7</property> + </object> + </property> + </object> </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> - </template> -</interface> + </object> + </child> + </template> +</interface>
\ No newline at end of file diff --git a/main/data/file_send_overlay.ui b/main/data/file_send_overlay.ui index 00cc649b..15ff35ef 100644 --- a/main/data/file_send_overlay.ui +++ b/main/data/file_send_overlay.ui @@ -1,67 +1,51 @@ <?xml version="1.0" encoding="UTF-8"?> <interface> - <template class="DinoUiFileSendOverlay"> - <property name="hexpand">False</property> - <property name="vexpand">False</property> + <requires lib="gtk" version="4.0"/> + <object class="GtkBox" id="main_box"> + <property name="hexpand">0</property> + <property name="vexpand">0</property> <property name="halign">center</property> <property name="valign">center</property> - <property name="visible">True</property> <style> <class name="background"/> <class name="dino-file-overlay"/> </style> <child> - <object class="DinoUiSizeRequestBox"> - <property name="size-request-mode">height-for-width</property> + <object class="GtkBox"> <property name="orientation">vertical</property> - <property name="margin-right">20</property> - <property name="margin-left">20</property> - <property name="visible">True</property> + <property name="margin-end">20</property> + <property name="margin-start">20</property> <child> <object class="GtkBox"> <property name="margin-top">10</property> <property name="margin-bottom">10</property> - <property name="orientation">horizontal</property> - <property name="visible">True</property> <child> <object class="GtkLabel"> - <property name="label" translatable="yes">Send a file</property> - <property name="hexpand">True</property> - <property name="visible">True</property> + <property name="label" translatable="1">Send a file</property> + <property name="hexpand">1</property> <attributes> - <attribute name="weight" value="PANGO_WEIGHT_BOLD"/> + <attribute name="weight" value="PANGO_WEIGHT_BOLD"></attribute> </attributes> </object> - <packing> - <property name="pack_type">start</property> - </packing> </child> <child> <object class="GtkButton" id="close_button"> - <property name="can_default">True</property> - <property name="can_focus">True</property> - <property name="visible">True</property> + <property name="focusable">1</property> <style> <class name="titlebutton"/> <class name="close"/> </style> <child> <object class="GtkImage"> - <property name="visible">True</property> <property name="icon-name">window-close-symbolic</property> - <property name="icon-size">1</property> </object> </child> </object> - <packing> - <property name="pack_type">end</property> - </packing> </child> </object> </child> <child> <object class="DinoUiSizingBin" id="file_widget_insert"> - <property name="visible">True</property> </object> </child> <child> @@ -69,29 +53,21 @@ <property name="margin-top">15</property> <property name="margin-bottom">15</property> <property name="spacing">5</property> - <property name="visible">True</property> <child> - <object class="GtkLabel" id="info_label"> - <property name="visible">True</property> - </object> + <object class="GtkLabel" id="info_label"/> </child> <child> <object class="GtkButton" id="send_button"> - <property name="label" translatable="yes">Send</property> - <property name="can_default">True</property> - <property name="can_focus">True</property> - <property name="visible">True</property> + <property name="label" translatable="1">Send</property> + <property name="focusable">1</property> <style> <class name="suggested-action"/> </style> </object> - <packing> - <property name="pack_type">end</property> - </packing> </child> </object> </child> </object> </child> - </template> -</interface> + </object> +</interface>
\ No newline at end of file diff --git a/main/data/global_search.ui b/main/data/global_search.ui index 4814f236..a29c48de 100644 --- a/main/data/global_search.ui +++ b/main/data/global_search.ui @@ -1,170 +1,158 @@ <?xml version="1.0" encoding="UTF-8"?> <interface> - <template class="DinoUiGlobalSearch" parent="GtkOverlay"> - <property name="visible">True</property> - <child> + <requires lib="gtk" version="4.0"/> + <object class="GtkOverlay" id="overlay"> + <property name="child"> <object class="GtkBox"> <property name="orientation">vertical</property> - <property name="visible">True</property> <child> <object class="GtkSearchEntry" id="search_entry"> - <property name="visible">True</property> - <property name="margin">12</property> + <property name="margin-start">12</property> + <property name="margin-end">12</property> + <property name="margin-top">12</property> + <property name="margin-bottom">12</property> </object> </child> <child> <object class="GtkStack" id="results_empty_stack"> - <property name="visible">True</property> <child> - <object class="GtkBox"> - <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 class="GtkStackPage"> + <property name="name">empty</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">system-search-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 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> + </child> </object> - </child> + </property> </object> - <packing> - <property name="name">empty</property> - </packing> </child> <child> - <object class="GtkBox"> - <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">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 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> - </child> + </property> </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> + <object class="GtkStackPage"> + <property name="name">results</property> + <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="GtkBox" id="results_box"> - <property name="orientation">vertical</property> - <property name="spacing">25</property> - <property name="margin">10</property> - <property name="valign">start</property> - <property name="visible">True</property> + <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> - </child> + </property> </object> - <packing> - <property name="name">results</property> - </packing> </child> </object> </child> </object> - </child> + </property> <child type="overlay"> <object class="GtkFrame" id="auto_complete_overlay"> - <property name="visible">True</property> <property name="margin-top">42</property> - <property name="margin-left">12</property> - <property name="margin-right">12</property> + <property name="margin-start">12</property> + <property name="margin-end">12</property> <property name="valign">start</property> <style> <class name="auto-complete"/> </style> - <child> + <property name="child"> <object class="GtkListBox" id="auto_complete_list"> - <property name="visible">True</property> <property name="selection-mode">browse</property> </object> - </child> + </property> </object> </child> - </template> + </object> </interface> diff --git a/main/data/icons/im.dino.Dino-symbolic.svg b/main/data/icons/scalable/apps/im.dino.Dino-symbolic.svg index 00680734..00680734 100644 --- a/main/data/icons/im.dino.Dino-symbolic.svg +++ b/main/data/icons/scalable/apps/im.dino.Dino-symbolic.svg diff --git a/main/data/icons/dino-device-desktop-symbolic.svg b/main/data/icons/scalable/devices/dino-device-desktop-symbolic.svg index 4e4e02ab..4e4e02ab 100644 --- a/main/data/icons/dino-device-desktop-symbolic.svg +++ b/main/data/icons/scalable/devices/dino-device-desktop-symbolic.svg diff --git a/main/data/icons/dino-device-phone-symbolic.svg b/main/data/icons/scalable/devices/dino-device-phone-symbolic.svg index 64a69e17..64a69e17 100644 --- a/main/data/icons/dino-device-phone-symbolic.svg +++ b/main/data/icons/scalable/devices/dino-device-phone-symbolic.svg diff --git a/main/data/icons/dino-phone-hangup-symbolic.svg b/main/data/icons/scalable/devices/dino-phone-hangup-symbolic.svg index ecd230ac..ecd230ac 100644 --- a/main/data/icons/dino-phone-hangup-symbolic.svg +++ b/main/data/icons/scalable/devices/dino-phone-hangup-symbolic.svg diff --git a/main/data/icons/dino-phone-in-talk-symbolic.svg b/main/data/icons/scalable/devices/dino-phone-in-talk-symbolic.svg index 351035da..351035da 100644 --- a/main/data/icons/dino-phone-in-talk-symbolic.svg +++ b/main/data/icons/scalable/devices/dino-phone-in-talk-symbolic.svg diff --git a/main/data/icons/dino-phone-missed-symbolic.svg b/main/data/icons/scalable/devices/dino-phone-missed-symbolic.svg index 228f073e..228f073e 100644 --- a/main/data/icons/dino-phone-missed-symbolic.svg +++ b/main/data/icons/scalable/devices/dino-phone-missed-symbolic.svg diff --git a/main/data/icons/dino-phone-ring-symbolic.svg b/main/data/icons/scalable/devices/dino-phone-ring-symbolic.svg index 06b8dcbf..06b8dcbf 100644 --- a/main/data/icons/dino-phone-ring-symbolic.svg +++ b/main/data/icons/scalable/devices/dino-phone-ring-symbolic.svg diff --git a/main/data/icons/dino-phone-symbolic.svg b/main/data/icons/scalable/devices/dino-phone-symbolic.svg index 0020dddc..0020dddc 100644 --- a/main/data/icons/dino-phone-symbolic.svg +++ b/main/data/icons/scalable/devices/dino-phone-symbolic.svg diff --git a/main/data/icons/dino-emoticon-symbolic.svg b/main/data/icons/scalable/emotes/dino-emoticon-symbolic.svg index 5656303d..5656303d 100644 --- a/main/data/icons/dino-emoticon-symbolic.svg +++ b/main/data/icons/scalable/emotes/dino-emoticon-symbolic.svg diff --git a/main/data/icons/dino-file-document-symbolic.svg b/main/data/icons/scalable/mimetypes/dino-file-document-symbolic.svg index 8901a817..8901a817 100644 --- a/main/data/icons/dino-file-document-symbolic.svg +++ b/main/data/icons/scalable/mimetypes/dino-file-document-symbolic.svg diff --git a/main/data/icons/dino-file-download-symbolic.svg b/main/data/icons/scalable/mimetypes/dino-file-download-symbolic.svg index 5dada23d..5dada23d 100644 --- a/main/data/icons/dino-file-download-symbolic.svg +++ b/main/data/icons/scalable/mimetypes/dino-file-download-symbolic.svg diff --git a/main/data/icons/dino-file-image-symbolic.svg b/main/data/icons/scalable/mimetypes/dino-file-image-symbolic.svg index 0cae7f18..0cae7f18 100644 --- a/main/data/icons/dino-file-image-symbolic.svg +++ b/main/data/icons/scalable/mimetypes/dino-file-image-symbolic.svg diff --git a/main/data/icons/dino-file-music-symbolic.svg b/main/data/icons/scalable/mimetypes/dino-file-music-symbolic.svg index ef7ae56c..ef7ae56c 100644 --- a/main/data/icons/dino-file-music-symbolic.svg +++ b/main/data/icons/scalable/mimetypes/dino-file-music-symbolic.svg diff --git a/main/data/icons/dino-file-symbolic.svg b/main/data/icons/scalable/mimetypes/dino-file-symbolic.svg index 748c4e7b..748c4e7b 100644 --- a/main/data/icons/dino-file-symbolic.svg +++ b/main/data/icons/scalable/mimetypes/dino-file-symbolic.svg diff --git a/main/data/icons/dino-file-table-symbolic.svg b/main/data/icons/scalable/mimetypes/dino-file-table-symbolic.svg index c04b1244..c04b1244 100644 --- a/main/data/icons/dino-file-table-symbolic.svg +++ b/main/data/icons/scalable/mimetypes/dino-file-table-symbolic.svg diff --git a/main/data/icons/dino-file-video-symbolic.svg b/main/data/icons/scalable/mimetypes/dino-file-video-symbolic.svg index c4afc836..c4afc836 100644 --- a/main/data/icons/dino-file-video-symbolic.svg +++ b/main/data/icons/scalable/mimetypes/dino-file-video-symbolic.svg diff --git a/main/data/icons/dino-changes-allowed-symbolic.svg b/main/data/icons/scalable/status/dino-changes-allowed-symbolic.svg index 46f4487c..46f4487c 100644 --- a/main/data/icons/dino-changes-allowed-symbolic.svg +++ b/main/data/icons/scalable/status/dino-changes-allowed-symbolic.svg diff --git a/main/data/icons/dino-changes-prevent-symbolic.svg b/main/data/icons/scalable/status/dino-changes-prevent-symbolic.svg index 56312122..56312122 100644 --- a/main/data/icons/dino-changes-prevent-symbolic.svg +++ b/main/data/icons/scalable/status/dino-changes-prevent-symbolic.svg diff --git a/main/data/icons/dino-double-tick-symbolic.svg b/main/data/icons/scalable/status/dino-double-tick-symbolic.svg index abbf5142..abbf5142 100644 --- a/main/data/icons/dino-double-tick-symbolic.svg +++ b/main/data/icons/scalable/status/dino-double-tick-symbolic.svg diff --git a/main/data/icons/dino-microphone-off-symbolic.svg b/main/data/icons/scalable/status/dino-microphone-off-symbolic.svg index 7e5b853d..7e5b853d 100644 --- a/main/data/icons/dino-microphone-off-symbolic.svg +++ b/main/data/icons/scalable/status/dino-microphone-off-symbolic.svg diff --git a/main/data/icons/dino-microphone-symbolic.svg b/main/data/icons/scalable/status/dino-microphone-symbolic.svg index fbf0784a..fbf0784a 100644 --- a/main/data/icons/dino-microphone-symbolic.svg +++ b/main/data/icons/scalable/status/dino-microphone-symbolic.svg diff --git a/main/data/icons/dino-security-high-symbolic.svg b/main/data/icons/scalable/status/dino-security-high-symbolic.svg index d930b901..d930b901 100644 --- a/main/data/icons/dino-security-high-symbolic.svg +++ b/main/data/icons/scalable/status/dino-security-high-symbolic.svg diff --git a/main/data/icons/dino-tick-symbolic.svg b/main/data/icons/scalable/status/dino-tick-symbolic.svg index bc067e02..bc067e02 100644 --- a/main/data/icons/dino-tick-symbolic.svg +++ b/main/data/icons/scalable/status/dino-tick-symbolic.svg diff --git a/main/data/icons/dino-video-off-symbolic.svg b/main/data/icons/scalable/status/dino-video-off-symbolic.svg index d438e065..d438e065 100644 --- a/main/data/icons/dino-video-off-symbolic.svg +++ b/main/data/icons/scalable/status/dino-video-off-symbolic.svg diff --git a/main/data/icons/dino-video-symbolic.svg b/main/data/icons/scalable/status/dino-video-symbolic.svg index 60a1c742..60a1c742 100644 --- a/main/data/icons/dino-video-symbolic.svg +++ b/main/data/icons/scalable/status/dino-video-symbolic.svg diff --git a/main/data/manage_accounts/account_row.ui b/main/data/manage_accounts/account_row.ui index 965171a7..6cacbeaf 100644 --- a/main/data/manage_accounts/account_row.ui +++ b/main/data/manage_accounts/account_row.ui @@ -1,30 +1,30 @@ <?xml version="1.0" encoding="UTF-8"?> <interface> + <requires lib="gtk" version="4.0"/> <template class="DinoUiManageAccountsAccountRow"> <property name="visible">True</property> <child> <object class="GtkGrid"> - <property name="orientation">horizontal</property> - <property name="margin">6</property> + <property name="margin-start">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="visible">True</property> <child> <object class="DinoUiAvatarImage" id="image"> <property name="height">40</property> <property name="width">40</property> - <property name="visible">True</property> </object> </child> <child> <object class="GtkLabel" id="jid_label"> - <property name="halign">0.5</property> <property name="xalign">0</property> - <property name="expand">True</property> - <property name="visible">True</property> + <property name="hexpand">1</property> </object> </child> <child> <object class="GtkImage" id="icon"> + <property name="visible">False</property> <property name="icon-name">dialog-warning-symbolic</property> </object> </child> diff --git a/main/data/manage_accounts/add_account_dialog.ui b/main/data/manage_accounts/add_account_dialog.ui index ad141b4a..d37a98f5 100644 --- a/main/data/manage_accounts/add_account_dialog.ui +++ b/main/data/manage_accounts/add_account_dialog.ui @@ -1,640 +1,548 @@ <?xml version="1.0" encoding="UTF-8"?> <interface> + <requires lib="gtk" version="4.0"/> <template class="DinoUiManageAccountsAddAccountDialog"> <property name="default_width">400</property> <property name="modal">True</property> - <child internal-child="vbox"> + <child internal-child="content_area"> <object class="GtkBox"> - <property name="visible">True</property> <child> <object class="GtkOverlay"> - <property name="visible">True</property> - <child> + <property name="child"> <object class="GtkBox"> - <property name="expand">True</property> - <property name="visible">True</property> + <property name="hexpand">1</property> + <property name="vexpand">1</property> <child> <object class="GtkStack" id="stack"> <property name="transition_type">slide-left</property> - <property name="expand">True</property> - <property name="visible">True</property> + <property name="hexpand">1</property> + <property name="vexpand">1</property> <child> - <object class="GtkBox" id="sign_in_jid_box"> - <property name="orientation">vertical</property> - <property name="margin">20</property> - <property name="margin-start">50</property> - <property name="margin-end">50</property> - <property name="spacing">20</property> - <property name="visible">True</property> - <child> - <object class="GtkLabel"> - <property name="label" translatable="yes">Sign in</property> - <property name="visible">True</property> - <attributes> - <attribute name="scale" value="1.3"/> - </attributes> - </object> - </child> - <child> - <object class="GtkBox" id="info_grid"> + <object class="GtkStackPage"> + <property name="name">login_jid</property> + <property name="child"> + <object class="GtkBox" id="sign_in_jid_box"> <property name="orientation">vertical</property> - <property name="visible">True</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">50</property> + <property name="margin-end">50</property> + <property name="spacing">20</property> <child> <object class="GtkLabel"> - <property name="label">JID</property> - <property name="xalign">0</property> - <property name="visible">True</property> + <property name="label" translatable="1">Sign in</property> <attributes> - <attribute name="scale" value="0.9"/> + <attribute name="scale" value="1.3"></attribute> </attributes> </object> </child> <child> - <object class="GtkEntry" id="jid_entry"> - <property name="activates_default">True</property> - <property name="hexpand">True</property> - <property name="width_request">200</property> - <property name="visible">True</property> + <object class="GtkBox" id="info_grid"> + <property name="orientation">vertical</property> + <child> + <object class="GtkLabel"> + <property name="label">JID</property> + <property name="xalign">0</property> + <attributes> + <attribute name="scale" value="0.9"></attribute> + </attributes> + </object> + </child> + <child> + <object class="GtkEntry" id="jid_entry"> + <property name="activates_default">1</property> + <property name="hexpand">1</property> + <property name="width_request">200</property> + </object> + </child> + <child> + <object class="GtkLabel" id="sign_in_jid_error_label"> + <property name="xalign">0</property> + <property name="margin-top">7</property> + <attributes> + <attribute name="scale" value="0.9"></attribute> + </attributes> + </object> + </child> </object> </child> <child> - <object class="GtkLabel" id="sign_in_jid_error_label"> - <property name="xalign">0</property> - <property name="margin-top">7</property> - <property name="visible">True</property> + <object class="GtkBox"> + <child> + <object class="GtkButton" id="sign_in_jid_serverlist_button"> + <property name="label" translatable="1">Create account</property> + </object> + </child> + <child> + <object class="GtkButton" id="sign_in_jid_continue_button"> + <property name="halign">end</property> + <property name="hexpand">True</property> + <property name="sensitive">0</property> + <style> + <class name="text-button"/> + <class name="suggested-action"/> + </style> + <child> + <object class="GtkStack" id="sign_in_jid_continue_stack"> + <child> + <object class="GtkStackPage"> + <property name="name">label</property> + <property name="child"> + <object class="GtkLabel"> + <property name="label" translatable="1">Next</property> + </object> + </property> + </object> + </child> + <child> + <object class="GtkStackPage"> + <property name="name">spinner</property> + <property name="child"> + <object class="GtkSpinner"> + <property name="spinning">True</property> + </object> + </property> + </object> + </child> + </object> + </child> + </object> + </child> + </object> + </child> + </object> + </property> + </object> + </child> + <child> + <object class="GtkStackPage"> + <property name="name">tls_error</property> + <property name="child"> + <object class="GtkBox" id="sign_in_tls_box"> + <property name="orientation">vertical</property> + <property name="margin-top">30</property> + <property name="margin-bottom">20</property> + <property name="margin-start">50</property> + <property name="margin-end">50</property> + <property name="spacing">20</property> + <child> + <object class="GtkImage"> + <property name="icon-name">channel-insecure-symbolic</property> + <property name="pixel-size">72</property> + <property name="margin-top">10</property> + <style> + <class name="dim-label"/> + </style> + </object> + </child> + <child> + <object class="GtkLabel"> + <property name="label" translatable="1">Could not establish a secure connection</property> <attributes> - <attribute name="scale" value="0.9"/> + <attribute name="scale" value="1.1"></attribute> </attributes> </object> </child> - </object> - </child> - <child> - <object class="GtkBox"> - <property name="orientation">horizontal</property> - <property name="visible">True</property> <child> - <object class="GtkButton" id="sign_in_jid_serverlist_button"> - <property name="label" translatable="yes">Create account</property> - <property name="visible">True</property> + <object class="GtkLabel" id="sign_in_tls_label"> + <property name="justify">fill</property> + <property name="wrap">1</property> + <property name="use-markup">1</property> + <property name="hexpand">1</property> </object> - <packing> - <property name="pack_type">start</property> - </packing> </child> <child> - <object class="GtkButton" id="sign_in_jid_continue_button"> - <property name="sensitive">False</property> - <property name="can_default">True</property> - <property name="visible">True</property> - <style> - <class name="text-button"/> - <class name="suggested-action"/> - </style> + <object class="GtkBox"> <child> - <object class="GtkStack" id="sign_in_jid_continue_stack"> - <property name="visible">True</property> - <child> - <object class="GtkLabel"> - <property name="label" translatable="yes">Next</property> - <property name="visible">True</property> - </object> - <packing> - <property name="name">label</property> - </packing> - </child> - <child> - <object class="GtkSpinner"> - <property name="active">True</property> - <property name="visible">True</property> - </object> - <packing> - <property name="name">spinner</property> - </packing> - </child> + <object class="GtkButton" id="sign_in_tls_back_button"> + <property name="label" translatable="1">Back</property> </object> - </child> </object> - <packing> - <property name="pack_type">end</property> - </packing> </child> </object> - </child> + </property> </object> - <packing> - <property name="name">login_jid</property> - </packing> </child> <child> - <object class="GtkBox" id="sign_in_tls_box"> - <property name="orientation">vertical</property> - <property name="margin-top">30</property> - <property name="margin-bottom">20</property> - <property name="margin-start">50</property> - <property name="margin-end">50</property> - <property name="spacing">20</property> - <property name="visible">True</property> - <child> - <object class="GtkImage"> - <property name="visible">True</property> - <property name="icon-name">channel-insecure-symbolic</property> - <property name="icon-size">4</property> - <property name="pixel-size">72</property> - <property name="margin-top">10</property> - <style> - <class name="dim-label"/> - </style> - </object> - </child> - <child> - <object class="GtkLabel"> - <property name="label" translatable="yes">Could not establish a secure connection</property> - <property name="visible">True</property> - <attributes> - <attribute name="scale" value="1.1"/> - </attributes> - </object> - </child> - <child> - <object class="GtkLabel" id="sign_in_tls_label"> - <property name="justify">fill</property> - <property name="wrap">True</property> - <property name="use-markup">True</property> - <property name="hexpand">True</property> - <property name="visible">True</property> - </object> - </child> - <child> - <object class="GtkBox"> - <property name="orientation">horizontal</property> - <property name="visible">True</property> + <object class="GtkStackPage"> + <property name="name">login_password</property> + <property name="child"> + <object class="GtkBox" id="sign_in_password_box"> + <property name="orientation">vertical</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">50</property> + <property name="margin-end">50</property> + <property name="spacing">20</property> + <child> + <object class="GtkLabel" id="sign_in_password_title"> + <attributes> + <attribute name="scale" value="1.3"></attribute> + </attributes> + </object> + </child> + <child> + <object class="GtkBox"> + <property name="orientation">vertical</property> + <child> + <object class="GtkLabel"> + <property name="label" translatable="1">Password</property> + <property name="xalign">0</property> + <property name="margin-top">7</property> + <attributes> + <attribute name="scale" value="0.9"></attribute> + </attributes> + </object> + </child> + <child> + <object class="GtkEntry" id="password_entry"> + <property name="activates_default">1</property> + <property name="hexpand">1</property> + <property name="input_purpose">password</property> + <property name="width_request">200</property> + <property name="visibility">0</property> + </object> + </child> + <child> + <object class="GtkLabel" id="sign_in_password_error_label"> + <property name="xalign">0</property> + <property name="margin-top">7</property> + <attributes> + <attribute name="scale" value="0.9"></attribute> + </attributes> + </object> + </child> + </object> + </child> <child> - <object class="GtkButton" id="sign_in_tls_back_button"> - <property name="label" translatable="yes">Back</property> - <property name="visible">True</property> + <object class="GtkBox"> + <child> + <object class="GtkButton" id="sign_in_password_back_button"> + <property name="label" translatable="1">Back</property> + </object> + </child> + <child> + <object class="GtkButton" id="sign_in_password_continue_button"> + <property name="halign">end</property> + <property name="hexpand">True</property> + <property name="sensitive">0</property> + <style> + <class name="text-button"/> + <class name="suggested-action"/> + </style> + <child> + <object class="GtkStack" id="sign_in_password_continue_stack"> + <child> + <object class="GtkStackPage"> + <property name="name">label</property> + <property name="child"> + <object class="GtkLabel"> + <property name="label" translatable="1">Connect</property> + </object> + </property> + </object> + </child> + <child> + <object class="GtkStackPage"> + <property name="name">spinner</property> + <property name="child"> + <object class="GtkSpinner"> + <property name="spinning">True</property> + </object> + </property> + </object> + </child> + </object> + </child> + </object> + </child> </object> - <packing> - <property name="pack_type">start</property> - </packing> </child> </object> - </child> + </property> </object> - <packing> - <property name="name">tls_error</property> - </packing> </child> <child> - <object class="GtkBox" id="sign_in_password_box"> - <property name="orientation">vertical</property> - <property name="margin">20</property> - <property name="margin-start">50</property> - <property name="margin-end">50</property> - <property name="spacing">20</property> - <property name="visible">True</property> - <child> - <object class="GtkLabel" id="sign_in_password_title"> - <property name="visible">True</property> - <attributes> - <attribute name="scale" value="1.3"/> - </attributes> - </object> - </child> - <child> - <object class="GtkBox"> + <object class="GtkStackPage"> + <property name="name">server</property> + <property name="child"> + <object class="GtkBox" id="create_account_box"> <property name="orientation">vertical</property> - <property name="visible">True</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">50</property> + <property name="margin-end">50</property> + <child> + <object class="GtkLabel"> + <property name="label" translatable="1">Create account</property> + <property name="margin-bottom">20</property> + <attributes> + <attribute name="scale" value="1.3"></attribute> + </attributes> + </object> + </child> <child> <object class="GtkLabel"> - <property name="label" translatable="yes">Password</property> <property name="xalign">0</property> - <property name="margin-top">7</property> - <property name="visible">True</property> + <property name="label" translatable="1">Choose a public server</property> <attributes> - <attribute name="scale" value="0.9"/> + <attribute name="weight" value="PANGO_WEIGHT_BOLD"></attribute> </attributes> </object> </child> <child> - <object class="GtkEntry" id="password_entry"> - <property name="activates_default">True</property> - <property name="hexpand">True</property> - <property name="input_purpose">password</property> - <property name="width_request">200</property> - <property name="visible">True</property> - <property name="visibility">False</property> + <object class="GtkFrame"> + <property name="child"> + <object class="GtkScrolledWindow"> + <property name="max_content_height">300</property> + <property name="propagate_natural_height">1</property> + <property name="hscrollbar_policy">never</property> + <property name="child"> + <object class="GtkListBox" id="server_list_box"/> + </property> + </object> + </property> </object> </child> <child> - <object class="GtkLabel" id="sign_in_password_error_label"> + <object class="GtkLabel"> <property name="xalign">0</property> - <property name="margin-top">7</property> - <property name="visible">True</property> + <property name="margin-top">20</property> + <property name="label" translatable="1">Or specify a server address</property> <attributes> - <attribute name="scale" value="0.9"/> + <attribute name="weight" value="PANGO_WEIGHT_BOLD"></attribute> </attributes> </object> </child> - <!--<child>--> - <!--<object class="GtkLabel">--> - <!--<property name="label" translatable="yes">Local alias</property>--> - <!--<property name="xalign">0</property>--> - <!--<property name="margin-top">7</property>--> - <!--<property name="visible">True</property>--> - <!--<attributes>--> - <!--<attribute name="scale" value="0.9"/>--> - <!--</attributes>--> - <!--</object>--> - <!--</child>--> - <!--<child>--> - <!--<object class="GtkEntry" id="alias_entry">--> - <!--<property name="activates_default">True</property>--> - <!--<property name="hexpand">True</property>--> - <!--<property name="width_request">200</property>--> - <!--<property name="visible">True</property>--> - <!--</object>--> - <!--</child>--> - </object> - </child> - <child> - <object class="GtkBox"> - <property name="orientation">horizontal</property> - <property name="visible">True</property> <child> - <object class="GtkButton" id="sign_in_password_back_button"> - <property name="label" translatable="yes">Back</property> - <property name="visible">True</property> + <object class="GtkEntry" id="server_entry"> + <property name="activates_default">1</property> </object> - <packing> - <property name="pack_type">start</property> - </packing> </child> <child> - <object class="GtkButton" id="sign_in_password_continue_button"> - <property name="sensitive">False</property> - <property name="can_default">True</property> - <property name="visible">True</property> - <style> - <class name="text-button"/> - <class name="suggested-action"/> - </style> + <object class="GtkBox"> + <property name="margin-top">30</property> <child> - <object class="GtkStack" id="sign_in_password_continue_stack"> - <property name="visible">True</property> - <child> - <object class="GtkLabel"> - <property name="label" translatable="yes">Connect</property> - <property name="visible">True</property> - </object> - <packing> - <property name="name">label</property> - </packing> - </child> + <object class="GtkButton" id="login_button"> + <property name="label" translatable="1">Sign in instead</property> + </object> + </child> + <child> + <object class="GtkButton" id="select_server_continue"> + <property name="halign">end</property> + <property name="hexpand">True</property> + <property name="sensitive">0</property> + <style> + <class name="text-button"/> + <class name="suggested-action"/> + </style> <child> - <object class="GtkSpinner"> - <property name="active">True</property> - <property name="visible">True</property> + <object class="GtkStack" id="select_server_continue_stack"> + <child> + <object class="GtkStackPage"> + <property name="name">label</property> + <property name="child"> + <object class="GtkLabel"> + <property name="label" translatable="1">Next</property> + </object> + </property> + </object> + </child> + <child> + <object class="GtkStackPage"> + <property name="name">spinner</property> + <property name="child"> + <object class="GtkSpinner"> + <property name="spinning">True</property> + </object> + </property> + </object> + </child> </object> - <packing> - <property name="name">spinner</property> - </packing> </child> </object> - </child> </object> - <packing> - <property name="pack_type">end</property> - </packing> </child> </object> - </child> + </property> </object> - <packing> - <property name="name">login_password</property> - </packing> </child> <child> - <object class="GtkBox" id="create_account_box"> - <property name="orientation">vertical</property> - <property name="margin">20</property> - <property name="margin-start">50</property> - <property name="margin-end">50</property> - <property name="visible">True</property> - <child> - <object class="GtkLabel"> - <property name="label" translatable="yes">Create account</property> + <object class="GtkStackPage"> + <property name="name">form</property> + <property name="child"> + <object class="GtkBox" id="register_box"> + <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="visible">True</property> - <attributes> - <attribute name="scale" value="1.3"/> - </attributes> - </object> - </child> - <child> - <object class="GtkLabel"> - <property name="visible">True</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Choose a public server</property> - <attributes> - <attribute name="weight" value="PANGO_WEIGHT_BOLD"/> - </attributes> - </object> - </child> - <child> - <object class="GtkFrame"> - <property name="visible">True</property> + <property name="margin-start">50</property> + <property name="margin-end">50</property> + <property name="orientation">vertical</property> <child> - <object class="GtkScrolledWindow"> - <property name="max_content_height">300</property> - <property name="propagate_natural_height">True</property> - <property name="hscrollbar_policy">never</property> - <property name="visible">True</property> - <child> - <object class="GtkListBox" id="server_list_box"> - <property name="visible">True</property> - </object> - </child> + <object class="GtkLabel" id="register_title"> + <property name="margin-bottom">10</property> + <attributes> + <attribute name="scale" value="1.3"></attribute> + </attributes> </object> </child> - </object> - </child> - <child> - <object class="GtkLabel"> - <property name="visible">True</property> - <property name="xalign">0</property> - <property name="margin-top">20</property> - <property name="label" translatable="yes">Or specify a server address</property> - <attributes> - <attribute name="weight" value="PANGO_WEIGHT_BOLD"/> - </attributes> - </object> - </child> - <child> - <object class="GtkEntry" id="server_entry"> - <property name="activates_default">True</property> - <property name="can_default">True</property> - <property name="visible">True</property> - </object> - </child> - <child> - <object class="GtkBox"> - <property name="visible">True</property> - <property name="margin-top">30</property> - <property name="orientation">horizontal</property> <child> - <object class="GtkButton" id="login_button"> - <property name="label" translatable="yes">Sign in instead</property> - <property name="visible">True</property> + <object class="GtkBox" id="form_box"> + <property name="orientation">vertical</property> </object> - <packing> - <property name="pack_type">start</property> - </packing> </child> <child> - <object class="GtkButton" id="select_server_continue"> - <property name="sensitive">False</property> - <property name="can_default">True</property> - <property name="visible">True</property> - <style> - <class name="text-button"/> - <class name="suggested-action"/> - </style> + <object class="GtkBox"> + <property name="margin-top">30</property> <child> - <object class="GtkStack" id="select_server_continue_stack"> - <property name="visible">True</property> - <child> - <object class="GtkLabel"> - <property name="label" translatable="yes">Next</property> - <property name="visible">True</property> - </object> - <packing> - <property name="name">label</property> - </packing> - </child> + <object class="GtkButton" id="register_form_back"> + <property name="label" translatable="1">Pick another server</property> + </object> + </child> + <child> + <object class="GtkButton" id="register_form_continue"> + <property name="halign">end</property> + <property name="hexpand">True</property> + <style> + <class name="text-button"/> + <class name="suggested-action"/> + </style> <child> - <object class="GtkSpinner"> - <property name="active">True</property> - <property name="visible">True</property> + <object class="GtkStack" id="register_form_continue_stack"> + <child> + <object class="GtkStackPage"> + <property name="name">label</property> + <property name="child"> + <object class="GtkLabel" id="register_form_continue_label"> + <property name="label" translatable="1">Next</property> + </object> + </property> + </object> + </child> + <child> + <object class="GtkStackPage"> + <property name="name">spinner</property> + <property name="child"> + <object class="GtkSpinner"> + <property name="spinning">True</property> + </object> + </property> + </object> + </child> </object> - <packing> - <property name="name">spinner</property> - </packing> </child> </object> - </child> </object> - <packing> - <property name="pack_type">end</property> - </packing> </child> </object> - </child> + </property> </object> - <packing> - <property name="name">server</property> - </packing> </child> <child> - <object class="GtkBox" id="register_box"> - <property name="margin">20</property> - <property name="margin-start">50</property> - <property name="margin-end">50</property> - <property name="orientation">vertical</property> - <property name="visible">True</property> - <child> - <object class="GtkLabel" id="register_title"> + <object class="GtkStackPage"> + <property name="name">success</property> + <property name="child"> + <object class="GtkBox" id="success_box"> + <property name="margin-start">50</property> + <property name="margin-end">50</property> + <property name="margin-top">50</property> + <property name="margin-bottom">50</property> + <property name="margin-top">10</property> <property name="margin-bottom">10</property> - <property name="visible">True</property> - <attributes> - <attribute name="scale" value="1.3"/> - </attributes> - </object> - </child> - <child> - <object class="GtkBox" id="form_box"> <property name="orientation">vertical</property> - <property name="visible">True</property> - </object> - </child> - <child> - <object class="GtkBox"> - <property name="visible">True</property> - <property name="margin-top">30</property> - <property name="orientation">horizontal</property> + <property name="valign">center</property> <child> - <object class="GtkButton" id="register_form_back"> - <property name="label" translatable="yes">Pick another server</property> - <property name="visible">True</property> + <object class="GtkImage"> + <property name="icon-name">dino-party-popper-symbolic</property> + <property name="pixel-size">72</property> + <property name="margin-bottom">10</property> + <style> + <class name="dim-label"/> + </style> </object> - <packing> - <property name="pack_type">start</property> - </packing> </child> <child> - <object class="GtkButton" id="register_form_continue"> - <property name="can_default">True</property> - <property name="visible">True</property> + <object class="GtkLabel"> + <property name="label" translatable="1">All set up!</property> + <attributes> + <attribute name="weight" value="PANGO_WEIGHT_BOLD"></attribute> + <attribute name="scale" value="1.3"></attribute> + </attributes> <style> - <class name="text-button"/> - <class name="suggested-action"/> + <class name="dim-label"/> </style> - <child> - <object class="GtkStack" id="register_form_continue_stack"> - <property name="visible">True</property> - <child> - <object class="GtkLabel" id="register_form_continue_label"> - <property name="label" translatable="yes">Next</property> - <property name="visible">True</property> - </object> - <packing> - <property name="name">label</property> - </packing> - </child> - <child> - <object class="GtkSpinner"> - <property name="active">True</property> - <property name="visible">True</property> - </object> - <packing> - <property name="name">spinner</property> - </packing> - </child> - </object> - </child> </object> - <packing> - <property name="pack_type">end</property> - </packing> </child> - </object> - </child> - </object> - <packing> - <property name="name">form</property> - </packing> - </child> - <child> - <object class="GtkBox" id="success_box"> - <property name="margin">50</property> - <property name="margin-top">10</property> - <property name="margin-bottom">10</property> - <property name="orientation">vertical</property> - <property name="valign">center</property> - <property name="visible">True</property> - <child> - <object class="GtkImage"> - <property name="visible">True</property> - <property name="icon-name">dino-party-popper-symbolic</property> - <property name="icon-size">4</property> - <property name="pixel-size">72</property> - <property name="margin-bottom">10</property> - <style> - <class name="dim-label"/> - </style> - </object> - </child> - <child> - <object class="GtkLabel"> - <property name="label" translatable="yes">All set up!</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" id="success_description"> - <property name="wrap">True</property> - <property name="margin">5</property> - <property name="xalign">0.5</property> - <property name="yalign">0.5</property> - <property name="use-markup">True</property> - <property name="justify">center</property> - <property name="visible">True</property> - <style> - <class name="dim-label"/> - </style> - </object> - </child> - <child> - <object class="GtkBox"> - <property name="orientation">horizontal</property> - <property name="halign">center</property> - <property name="margin-top">20</property> - <property name="visible">True</property> <child> - <object class="GtkButton" id="success_continue_button"> - <property name="can_default">True</property> - <property name="label" translatable="yes">Finish</property> - <property name="visible">True</property> + <object class="GtkLabel" id="success_description"> + <property name="wrap">1</property> + <property name="margin-start">5</property> + <property name="margin-end">5</property> + <property name="margin-top">5</property> + <property name="margin-bottom">5</property> + <property name="use-markup">1</property> + <property name="justify">center</property> <style> - <class name="text-button"/> - <class name="suggested-action"/> + <class name="dim-label"/> </style> </object> </child> + <child> + <object class="GtkBox"> + <property name="halign">center</property> + <property name="margin-top">20</property> + <child> + <object class="GtkButton" id="success_continue_button"> + <property name="label" translatable="1">Finish</property> + <style> + <class name="text-button"/> + <class name="suggested-action"/> + </style> + </object> + </child> + </object> + </child> </object> - </child> + </property> </object> - <packing> - <property name="name">success</property> - </packing> </child> </object> </child> </object> - <packing> - <property name="index">-1</property> - </packing> - </child> + </property> <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> + <property name="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> +<!-- <property name="shadow_type">none</property>--> <style> <class name="app-notification"/> </style> - <child> + <property name="child"> <object class="GtkBox" id="box2"> - <property name="visible">True</property> - <property name="can_focus">False</property> <property name="spacing">20</property> <child> - <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> + <object class="GtkLabel" id="notification_label"/> </child> </object> - </child> + </property> <child type="label_item"> <placeholder/> </child> </object> - </child> + </property> </object> </child> </object> @@ -642,4 +550,4 @@ </object> </child> </template> -</interface> +</interface>
\ No newline at end of file diff --git a/main/data/manage_accounts/dialog.ui b/main/data/manage_accounts/dialog.ui index 8d50d449..9077c69e 100644 --- a/main/data/manage_accounts/dialog.ui +++ b/main/data/manage_accounts/dialog.ui @@ -1,78 +1,74 @@ <?xml version="1.0" encoding="UTF-8"?> <interface> + <requires lib="gtk" version="4.0"/> <template class="DinoUiManageAccountsDialog"> + <property name="title" translatable="1">Accounts</property> <property name="width-request">700</property> <property name="height-request">400</property> <property name="modal">True</property> <child type="titlebar"> - <object class="GtkHeaderBar"> - <property name="title" translatable="yes">Accounts</property> - <property name="show_close_button">True</property> - <property name="visible">True</property> - </object> + <object class="GtkHeaderBar"/> </child> - <child internal-child="vbox"> - <object class="GtkBox"> - <property name="visible">True</property> + <child internal-child="content_area"> + <object class="GtkBox"> + <property name="visible">True</property> + <child> + <object class="GtkStack" id="main_stack"> <child> - <object class="GtkStack" id="main_stack"> - <property name="visible">True</property> - <child> + <object class="GtkStackPage"> + <property name="name">accounts_exist</property> + <property name="child"> <object class="GtkBox"> - <property name="can_focus">True</property> - <property name="expand">True</property> - <property name="visible">True</property> + <property name="focusable">1</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> <child> <object class="GtkBox"> - <property name="expand">True</property> - <property name="orientation">horizontal</property> - <property name="margin">15</property> + <property name="hexpand">True</property> + <property name="vexpand">True</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="visible">True</property> <child> <object class="GtkBox"> <property name="orientation">vertical</property> <property name="width-request">250</property> - <property name="vexpand">True</property> - <property name="hexpand">False</property> - <property name="visible">True</property> + <property name="vexpand">1</property> + <property name="hexpand">0</property> <child> <object class="GtkScrolledWindow"> - <property name="shadow-type">in</property> - <property name="expand">True</property> - <property name="visible">True</property> - <child> +<!-- <property name="shadow-type">in</property>--> + <property name="hexpand">1</property> + <property name="vexpand">1</property> + <property name="child"> <object class="GtkBox"> <property name="orientation">vertical</property> - <property name="visible">True</property> <child> <object class="GtkListBox" id="account_list"> - <property name="expand">True</property> - <property name="visible">True</property> + <property name="hexpand">1</property> + <property name="vexpand">1</property> </object> </child> </object> - </child> + </property> </object> </child> <child> - <object class="GtkToolbar"> + <object class="GtkBox"> + <property name="css-classes">toolbar</property> <style> <class name="inline-toolbar"/> </style> - <property name="icon-size">menu</property> - <property name="toolbar-style">icons</property> - <property name="visible">True</property> <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="visible">True</property> </object> </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="visible">True</property> </object> </child> </object> @@ -81,23 +77,19 @@ </child> <child> <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="row-spacing">5</property> - <property name="visible">True</property> <child> <object class="GtkBox"> - <property name="visible">True</property> <property name="halign">end</property> <child> - <object class="GtkBox"> - <property name="visible">True</property> - </object> + <object class="GtkBox"/> </child> <child> <object class="GtkButton" id="image_button"> - <property name="relief">none</property> - <property name="visible">True</property> + <property name="has-frame">False</property> <style> <class name="image-button"/> </style> @@ -105,145 +97,124 @@ <object class="DinoUiAvatarImage" id="image"> <property name="height">50</property> <property name="width">50</property> - <property name="xalign">1</property> - <property name="visible">True</property> +<!-- <property name="xalign">1</property>--> <property name="allow_gray">False</property> </object> </child> </object> </child> + <layout> + <property name="column">0</property> + <property name="row">0</property> + <property name="row-span">2</property> + </layout> </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> <object class="GtkLabel" id="jid_label"> <property name="xalign">0</property> <property name="yalign">1</property> - <property name="visible">True</property> <attributes> - <attribute name="weight" value="PANGO_WEIGHT_BOLD"/> + <attribute name="weight" value="PANGO_WEIGHT_BOLD"></attribute> </attributes> + <layout> + <property name="column">1</property> + <property name="row">0</property> + </layout> </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> <object class="GtkLabel" id="state_label"> <property name="xalign">0</property> <property name="yalign">0</property> - <property name="visible">True</property> <style> <class name="dim-label"/> </style> + <layout> + <property name="column">1</property> + <property name="row">1</property> + </layout> </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> <object class="GtkSwitch" id="active_switch"> - <property name="visible">True</property> <property name="halign">end</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> - <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> <object class="GtkLabel"> - <property name="label" translatable="yes">Password</property> + <property name="label" translatable="1">Password</property> <property name="xalign">1</property> - <property name="visible">True</property> <style> <class name="dim-label"/> </style> + <layout> + <property name="column">0</property> + <property name="row">2</property> + </layout> </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> <object class="DinoUiUtilEntryLabelHybrid" id="password_hybrid"> <property name="xalign">0</property> <property name="width_request">200</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> - <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> <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="visible">True</property> <style> <class name="dim-label"/> </style> + <layout> + <property name="column">0</property> + <property name="row">3</property> + </layout> </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> <object class="DinoUiUtilEntryLabelHybrid" id="alias_hybrid"> <property name="xalign">0</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> - <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> </object> </child> </object> </child> </object> - <packing> - <property name="name">accounts_exist</property> - </packing> - </child> - <child> + </property> + </object> + </child> + <child> + <object class="GtkStackPage"> + <property name="name">no_accounts</property> + <property name="child"> <object class="GtkBox"> <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-users-symbolic</property> - <property name="icon-size">4</property> <property name="pixel-size">72</property> <style> <class name="dim-label"/> @@ -252,10 +223,7 @@ </child> <child> <object class="GtkLabel"> - <property name="label" translatable="yes">No accounts configured</property> - <property name="xalign">0.5</property> - <property name="yalign">0.5</property> - <property name="visible">True</property> + <property name="label" translatable="1">No accounts configured</property> <style> <class name="dim-label"/> </style> @@ -263,9 +231,8 @@ </child> <child> <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="visible">True</property> <style> <class name="text-button"/> <class name="suggested-action"/> @@ -273,13 +240,12 @@ </object> </child> </object> - <packing> - <property name="name">no_accounts</property> - </packing> - </child> + </property> </object> </child> </object> + </child> + </object> </child> </template> </interface> diff --git a/main/data/menu_encryption.ui b/main/data/menu_encryption.ui index 9e63b17d..5f478fe2 100644 --- a/main/data/menu_encryption.ui +++ b/main/data/menu_encryption.ui @@ -2,32 +2,21 @@ <interface> <requires lib="gtk+" version="3.16"/> <object class="GtkPopoverMenu" id="menu_encryption"> - <property name="can_focus">False</property> - <child> + <property name="child"> <object class="GtkBox" id="encryption_box"> - <property name="visible">True</property> - <property name="can_focus">False</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> - <object class="GtkRadioButton" id="button_unencrypted"> - <property name="label" translatable="yes">Unencrypted</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="active">True</property> - <property name="draw_indicator">True</property> + <object class="GtkCheckButton" id="button_unencrypted"> + <property name="label" translatable="1">Unencrypted</property> + <property name="focusable">1</property> + <property name="active">1</property> </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> </child> </object> - <packing> - <property name="submenu">main</property> - </packing> - </child> + </property> </object> -</interface> +</interface>
\ No newline at end of file diff --git a/main/data/message_item_widget_edit_mode.ui b/main/data/message_item_widget_edit_mode.ui index b33d8aa3..907dbbf0 100644 --- a/main/data/message_item_widget_edit_mode.ui +++ b/main/data/message_item_widget_edit_mode.ui @@ -6,27 +6,21 @@ <property name="spacing">5</property> <property name="margin_top">5</property> <property name="margin_bottom">5</property> - <property name="visible">True</property> <child> <object class="GtkFrame" id="frame"> - <property name="visible">True</property> <style> <class name="dino-chatinput"/> </style> <child> <object class="GtkBox"> - <property name="visible">True</property> <child> - <object class="DinoUiChatTextView" id="chat_text_view"> - <property name="visible">True</property> - </object> + <object class="DinoUiChatTextView" id="chat_text_view" /> </child> <child> <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="valign">start</property> - <property name="visible">True</property> <style> <class name="dino-chatinput-button"/> <class name="flat"/> @@ -35,7 +29,6 @@ <object class="GtkImage"> <property name="icon-name">dino-emoticon-symbolic</property> <property name="icon-size">1</property> - <property name="visible">True</property> </object> </child> </object> @@ -48,17 +41,14 @@ <object class="GtkBox"> <property name="spacing">5</property> <property name="halign">end</property> - <property name="visible">True</property> <child> <object class="GtkButton" id="cancel_button"> <property name="label" translatable="yes">Cancel</property> - <property name="visible">True</property> </object> </child> <child> <object class="GtkButton" id="send_button"> <property name="label" translatable="yes">Update message</property> - <property name="visible">True</property> <style> <class name="suggested-action"/> </style> diff --git a/main/data/occupant_list.ui b/main/data/occupant_list.ui index d899eb5f..6cbf5910 100644 --- a/main/data/occupant_list.ui +++ b/main/data/occupant_list.ui @@ -2,30 +2,23 @@ <interface> <requires lib="gtk+" version="3.22"/> <template class="DinoUiOccupantMenuList"> - <property name="visible">True</property> <child> <object class="GtkBox"> <property name="orientation">vertical</property> - <property name="visible">True</property> <child> - <object class="GtkToolItem"> - <property name="visible">True</property> - <child> - <object class="GtkSearchEntry" id="search_entry"> - <property name="margin">8</property> - <property name="visible">True</property> - </object> - </child> + <object class="GtkSearchEntry" id="search_entry"> + <property name="margin_top">8</property> + <property name="margin_bottom">8</property> + <property name="margin_start">8</property> + <property name="margin_end">8</property> </object> </child> <child> <object class="GtkScrolledWindow"> <property name="max_content_height">500</property> <property name="propagate_natural_height">True</property> - <property name="visible">True</property> <child> <object class="GtkListBox" id="list_box"> - <property name="visible">True</property> </object> </child> </object> diff --git a/main/data/occupant_list_item.ui b/main/data/occupant_list_item.ui index e2b35ad6..1915aee6 100644 --- a/main/data/occupant_list_item.ui +++ b/main/data/occupant_list_item.ui @@ -1,45 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> <interface> - <template class="DinoUiOccupantMenuListRow" parent="GtkListBoxRow"> - <property name="visible">True</property> + <requires lib="gtk" version="4.0"/> + <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> - <object class="GtkGrid"> - <property name="orientation">horizontal</property> - <property name="margin-top">3</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 class="DinoUiAvatarImage" id="image"> + <property name="height">30</property> + <property name="width">30</property> </object> </child> - </template> -</interface> + <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>
\ No newline at end of file diff --git a/main/data/search_autocomplete.ui b/main/data/search_autocomplete.ui index 94ec5d7f..5146ac5a 100644 --- a/main/data/search_autocomplete.ui +++ b/main/data/search_autocomplete.ui @@ -5,18 +5,17 @@ <property name="visible">True</property> <child> <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-end">6</property> <property name="height">24</property> <property name="width">24</property> - <property name="visible">True</property> <property name="allow_gray">False</property> </object> </child> <child> <object class="GtkLabel" id="label"> - <property name="visible">True</property> <property name="ellipsize">end</property> </object> </child> diff --git a/main/data/settings_dialog.ui b/main/data/settings_dialog.ui index d5b7ac92..84d56c1d 100644 --- a/main/data/settings_dialog.ui +++ b/main/data/settings_dialog.ui @@ -1,81 +1,67 @@ <?xml version="1.0" encoding="UTF-8"?> <interface> <template class="DinoUiSettingsDialog"> + <property name="title" translatable="yes">Settings</property> <property name="modal">True</property> <child type="titlebar"> <object class="GtkHeaderBar"> - <property name="title" translatable="yes">Settings</property> - <property name="show_close_button">True</property> - <property name="visible">True</property> </object> </child> - <child internal-child="vbox"> + <child internal-child="content_area"> <object class="GtkBox"> - <property name="visible">True</property> <child> <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="visible">True</property> <child> <object class="GtkCheckButton" id="typing_checkbutton"> <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> - <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> <object class="GtkCheckButton" id="marker_checkbutton"> <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> - <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> <object class="GtkCheckButton" id="notification_checkbutton"> <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> - <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> <object class="GtkCheckButton" id="emoji_checkbutton"> <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> - <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> <object class="GtkCheckButton" id="check_spelling_checkbutton"> <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> - <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> </object> </child> diff --git a/main/data/theme.css b/main/data/theme.css index 059585b7..3bdbb19f 100644 --- a/main/data/theme.css +++ b/main/data/theme.css @@ -27,8 +27,8 @@ window.dino-main .dino-conversation viewport /* Some themes set this */ { } @keyframes highlight { - from { background: alpha(@warning_color, 0.5) } - to { background: transparent } + from { background: alpha(@warning_color, 0.5); } + to { background: transparent; } } window.dino-main .dino-conversation .highlight-once { @@ -38,10 +38,23 @@ window.dino-main .dino-conversation .highlight-once { 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); } +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 { background-color: alpha(@theme_fg_color, 0.8); color: @theme_base_color; @@ -142,7 +155,7 @@ window.dino-main .incoming-call-box { window.dino-main .multiparty-participants { 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 { @@ -171,34 +184,40 @@ window.dino-main .dino-chatinput frame box { background: transparent; } -window.dino-main button.dino-attach-button { - min-width: 24px; /* Make button the same with as avatars */ +window.dino-main .dino-attach-button { + 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; background: transparent; box-shadow: none; min-height: 0; padding: 7px 5px; - color: alpha(@theme_fg_color, 0.6); + color: alpha(@theme_fg_color, 0.7); 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; } -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); } -window.dino-main button.dino-chatinput-button:active, -window.dino-main button.dino-chatinput-button:checked { +window.dino-main .dino-attach-button:active, +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); } -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); } @@ -357,7 +376,7 @@ box.dino-input-error label.input-status-highlight-once { .dino-call-window .participant-name { color: white; - text-shadow: black; + text-shadow: 1px 1px 2px black; } .dino-call-window .text-no-controls { diff --git a/main/data/unified_main_content.ui b/main/data/unified_main_content.ui index 03c206c1..3fb7b6e5 100644 --- a/main/data/unified_main_content.ui +++ b/main/data/unified_main_content.ui @@ -1,94 +1,42 @@ <?xml version="1.0" encoding="UTF-8"?> <interface> + <requires lib="gtk" version="4.0"/> <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="orientation">horizontal</property> - <property name="visible">True</property> <child> <object class="GtkStack" id="left_stack"> - <property name="visible">True</property> <child> - <object class="GtkScrolledWindow" id="scrolled"> - <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> + <object class="GtkStackPage"> <property name="name">content</property> - </packing> - </child> - <child> - <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> + <property name="child"> + <object class="GtkScrolledWindow"> + <property name="hscrollbar_policy">never</property> + <child> + <object class="DinoUiConversationSelector" id="conversation_list"> + </object> + </child> </object> - </child> + </property> </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="GtkStackPage"> + <property name="name">placeholder</property> + <property name="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> + <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"> - <property name="icon-name">im.dino.Dino-symbolic</property> - <property name="pixel-size">144</property> - <property name="margin-bottom">30</property> - <property name="visible">True</property> + <object class="GtkImage" id="conversation_list_placeholder_image"> + <property name="valign">start</property> <style> <class name="dim-label"/> </style> @@ -96,52 +44,87 @@ </child> <child> <object class="GtkLabel"> - <property name="visible">True</property> - <property name="label" translatable="yes">You have no open chats</property> - <property name="xalign">0.5</property> - <property name="yalign">0.5</property> + <property name="wrap">1</property> + <property name="margin-top">70</property> + <property name="margin-end">50</property> + <property name="valign">end</property> + <property name="label" translatable="1">Click here to start a conversation or join a channel.</property> <style> <class name="dim-label"/> </style> - <attributes> - <attribute name="scale" value="1.2"/> - </attributes> </object> </child> </object> - <packing> + </property> + </object> + </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> - </packing> + <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> - </child> + </property> <child type="overlay"> <object class="GtkRevealer" id="search_revealer"> - <property name="visible">True</property> <property name="halign">end</property> <property name="transition-type">slide-left</property> <style> <class name="dino-sidebar"/> </style> - <child> - <object class="GtkFrame"> - <property name="visible">True</property> + <property name="child"> + <object class="GtkFrame" id="search_frame"> <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> - </child> + </property> </object> </child> </object> - <packing> - <property name="resize">True</property> - <property name="shrink">False</property> - </packing> </child> </object> -</interface> +</interface>
\ No newline at end of file diff --git a/main/data/unified_window_placeholder.ui b/main/data/unified_window_placeholder.ui index 88186f62..32fcd819 100644 --- a/main/data/unified_window_placeholder.ui +++ b/main/data/unified_window_placeholder.ui @@ -1,5 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <interface> + <requires lib="gtk" version="4.0"/> <template class="DinoUiMainWindowPlaceholder"> <property name="valign">center</property> <property name="visible">True</property> @@ -8,14 +9,15 @@ <property name="orientation">vertical</property> <property name="valign">center</property> <property name="halign">center</property> - <property name="hexpand">True</property> - <property name="visible">True</property> + <property name="hexpand">1</property> <child> <object class="GtkImage"> <property name="icon-name">im.dino.Dino-symbolic</property> <property name="pixel-size">144</property> - <property name="margin">30</property> - <property name="visible">True</property> + <property name="margin-start">30</property> + <property name="margin-end">30</property> + <property name="margin-top">30</property> + <property name="margin-bottom">30</property> <style> <class name="dim-label"/> </style> @@ -23,22 +25,18 @@ </child> <child> <object class="GtkLabel" id="title_label"> - <property name="xalign">0.5</property> - <property name="yalign">0.5</property> + <property name="visible">0</property> <style> <class name="dim-label"/> </style> <attributes> - <attribute name="scale" value="1.3"/> + <attribute name="scale" value="1.3"></attribute> </attributes> </object> </child> <child> <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="visible">True</property> <style> <class name="dim-label"/> </style> @@ -48,7 +46,6 @@ <object class="GtkButton" id="primary_button"> <property name="margin-top">15</property> <property name="halign">center</property> - <property name="visible">True</property> <style> <class name="text-button"/> <class name="suggested-action"/> @@ -57,8 +54,9 @@ </child> <child> <object class="GtkButton" id="secondary_button"> + <property name="visible">0</property> <property name="halign">center</property> - <property name="no_show_all">True</property> +<!-- <property name="no_show_all">True</property>--> <style> <class name="text-button"/> </style> diff --git a/main/src/emojichooser.c b/main/src/emojichooser.c deleted file mode 100644 index ba401ddc..00000000 --- a/main/src/emojichooser.c +++ /dev/null @@ -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)); -} diff --git a/main/src/emojichooser.h b/main/src/emojichooser.h deleted file mode 100644 index e7dc8660..00000000 --- a/main/src/emojichooser.h +++ /dev/null @@ -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 diff --git a/main/src/main.vala b/main/src/main.vala index afa1f52b..bf8759b6 100644 --- a/main/src/main.vala +++ b/main/src/main.vala @@ -14,7 +14,7 @@ void main(string[] args) { Intl.textdomain(GETTEXT_PACKAGE); 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 }; Plugins.Loader loader = new Plugins.Loader(app); loader.load_all(); diff --git a/main/src/ui/add_conversation/add_conference_dialog.vala b/main/src/ui/add_conversation/add_conference_dialog.vala index a03f3db6..e078bc62 100644 --- a/main/src/ui/add_conversation/add_conference_dialog.vala +++ b/main/src/ui/add_conversation/add_conference_dialog.vala @@ -10,14 +10,13 @@ namespace Dino.Ui { public class AddConferenceDialog : Gtk.Dialog { private Stack stack = new Stack(); - private Button cancel_button; + private Button cancel_button = new Button() { visible=true }; 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 ConferenceDetailsFragment details_fragment; private ConferenceList conference_list; + private ListBox conference_list_box; private StreamInteractor stream_interactor; @@ -29,7 +28,7 @@ public class AddConferenceDialog : Gtk.Dialog { stack.visible = true; stack.vhomogeneous = false; - get_content_area().add(stack); + get_content_area().append(stack); setup_headerbar(); setup_jid_add_view(); @@ -40,8 +39,7 @@ public class AddConferenceDialog : Gtk.Dialog { private void show_jid_add_view() { // Rewire headerbar (if CSD) if (Util.use_csd()) { - if (cancel_image.get_parent() != null) cancel_button.remove(cancel_image); - cancel_button.add(cancel_label); + cancel_button.set_label(_("Cancel")); cancel_button.clicked.disconnect(show_jid_add_view); cancel_button.clicked.connect(on_cancel); ok_button.label = _("Next"); @@ -59,8 +57,7 @@ public class AddConferenceDialog : Gtk.Dialog { private void show_conference_details_view() { // Rewire headerbar (if CSD) if (Util.use_csd()) { - if (cancel_label.get_parent() != null) cancel_button.remove(cancel_label); - cancel_button.add(cancel_image); + cancel_button.set_icon_name("go-previous-symbolic"); cancel_button.clicked.disconnect(on_cancel); cancel_button.clicked.connect(show_jid_add_view); ok_button.label = _("Join"); @@ -73,31 +70,30 @@ public class AddConferenceDialog : Gtk.Dialog { stack.transition_type = StackTransitionType.SLIDE_LEFT; stack.set_visible_child_name("details"); - animate_window_resize(); +// animate_window_resize(); } private void setup_headerbar() { - cancel_button = new Button() { visible=true }; - - ok_button = new Button() { can_focus=true, can_default=true, visible=true }; + ok_button = new Button() { can_focus=true, visible=true }; ok_button.get_style_context().add_class("suggested-action"); if (Util.use_csd()) { 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_end(ok_button); - ok_button.has_default = true; +// ok_button.has_default = true; } } private void setup_jid_add_view() { 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) => { AddGroupchatDialog dialog = new AddGroupchatDialog(stream_interactor); dialog.set_transient_for(this); @@ -109,23 +105,23 @@ public class AddConferenceDialog : Gtk.Dialog { }); 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"); 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 }; - 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.clicked.connect(on_next_button_clicked); 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); - box.add(cancel_button); - box.add(ok_button); - wrap_box.add(box); + box.append(cancel_button); + box.append(ok_button); + 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()); Box wrap_box = new Box(Orientation.VERTICAL, 0) { visible=true }; - wrap_box.add(details_fragment); + wrap_box.append(details_fragment); 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 }; - 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"); details_fragment.notify["done"].connect(() => { ok_button.sensitive = select_fragment.done; }); 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); - box.add(cancel_button); - box.add(ok_button); + box.append(cancel_button); + box.append(ok_button); - wrap_box.add(box); + wrap_box.append(box); } stack.add_named(wrap_box, "details"); } @@ -164,8 +160,9 @@ public class AddConferenceDialog : Gtk.Dialog { private void on_next_button_clicked() { 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) { details_fragment.account = conference_row.account; details_fragment.jid = conference_row.bookmark.jid.to_string(); @@ -184,10 +181,11 @@ public class AddConferenceDialog : Gtk.Dialog { } private void animate_window_resize() { - int def_height, curr_width, curr_height; - get_size(out curr_width, out curr_height); - stack.get_preferred_height(null, out def_height); - int difference = def_height - curr_height; + int curr_height = get_size(Orientation.VERTICAL); + int curr_width = get_size(Orientation.HORIZONTAL); + var natural_size = new Requisition(); + stack.get_preferred_size(null, out natural_size); + int difference = natural_size.height - curr_height; Timer timer = new Timer(); Timeout.add((int) (stack.transition_duration / 30), () => { @@ -195,7 +193,8 @@ public class AddConferenceDialog : Gtk.Dialog { timer.elapsed(out microsec); ulong millisec = microsec / 1000; 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; }); } diff --git a/main/src/ui/add_conversation/add_groupchat_dialog.vala b/main/src/ui/add_conversation/add_groupchat_dialog.vala index a71c0a15..786c1a0b 100644 --- a/main/src/ui/add_conversation/add_groupchat_dialog.vala +++ b/main/src/ui/add_conversation/add_groupchat_dialog.vala @@ -30,11 +30,12 @@ protected class AddGroupchatDialog : Gtk.Dialog { cancel_button.clicked.connect(() => { close(); }); 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(); if (!alias_entry_changed) { try { @@ -44,17 +45,15 @@ protected class AddGroupchatDialog : Gtk.Dialog { alias_entry.text = jid_entry.text; } } - return false; } - private bool check_ok() { + private void check_ok() { try { Jid parsed_jid = new Jid(jid_entry.text); ok_button.sensitive = parsed_jid != null && parsed_jid.localpart != null && parsed_jid.resourcepart == null; } catch (InvalidJidError e) { ok_button.sensitive = false; } - return false; } private void on_ok_button_clicked() { diff --git a/main/src/ui/add_conversation/conference_details_fragment.vala b/main/src/ui/add_conversation/conference_details_fragment.vala index 88f9cc73..721c660e 100644 --- a/main/src/ui/add_conversation/conference_details_fragment.vala +++ b/main/src/ui/add_conversation/conference_details_fragment.vala @@ -114,12 +114,12 @@ protected class ConferenceDetailsFragment : Box { account_combobox.changed.connect(() => { 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); - nick_entry.key_release_event.connect(on_nick_key_release_event); - password_entry.key_release_event.connect(on_password_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); +// password_entry.key_release_event.connect(on_password_key_release_event); - jid_entry.key_release_event.connect(() => { done = true; return false; }); // just for notifying - nick_entry.key_release_event.connect(() => { done = true; return false; }); +// jid_entry.key_release_event.connect(() => { done = true; return false; }); // just for notifying +// nick_entry.key_release_event.connect(() => { done = true; return 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); } - private bool on_jid_key_release_event(EventKey event) { - jid_label.label = jid_entry.text; - if (event.keyval == Key.Return) jid_stack.set_visible_child_name("label"); - return false; - } - - private bool on_nick_key_release_event(EventKey event) { - nick_label.label = nick_entry.text; - if (event.keyval == Key.Return) nick_stack.set_visible_child_name("label"); - return false; - } - - private bool on_password_key_release_event(EventKey event) { - string filler = ""; - for (int i = 0; i < password_entry.text.length; i++) filler += password_entry.get_invisible_char().to_string(); - password_label.label = filler; - if (event.keyval == Key.Return) password_stack.set_visible_child_name("label"); - return false; - } +// private bool on_jid_key_release_event(EventKey event) { +// jid_label.label = jid_entry.text; +// if (event.keyval == Key.Return) jid_stack.set_visible_child_name("label"); +// return false; +// } +// +// private bool on_nick_key_release_event(EventKey event) { +// nick_label.label = nick_entry.text; +// if (event.keyval == Key.Return) nick_stack.set_visible_child_name("label"); +// return false; +// } +// +// private bool on_password_key_release_event(EventKey event) { +// string filler = ""; +// for (int i = 0; i < password_entry.text.length; i++) filler += password_entry.get_invisible_char().to_string(); +// password_label.label = filler; +// if (event.keyval == Key.Return) password_stack.set_visible_child_name("label"); +// return false; +// } private void set_active_stack(Stack stack) { stack.set_visible_child_name("entry"); diff --git a/main/src/ui/add_conversation/conference_list.vala b/main/src/ui/add_conversation/conference_list.vala index fa71f98f..a19630e4 100644 --- a/main/src/ui/add_conversation/conference_list.vala +++ b/main/src/ui/add_conversation/conference_list.vala @@ -7,20 +7,22 @@ using Dino.Entities; namespace Dino.Ui { -protected class ConferenceList : FilterableList { +protected class ConferenceList { public signal void conversation_selected(Conversation? conversation); 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, HashMap<Jid, Widget>> widgets = new HashMap<Account, HashMap<Jid, Widget>>(Account.hash_func, Account.equals_func); public ConferenceList(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; - set_filter_func(filter); - set_header_func(header); - set_sort_func(sort); +// list_box.set_filter_func(filter); + list_box.set_header_func(header); +// list_box.set_sort_func(sort); stream_interactor.get_module(MucManager.IDENTITY).bookmarks_updated.connect((account, conferences) => { lists[account] = conferences; @@ -44,18 +46,18 @@ protected class ConferenceList : FilterableList { } var widget = new ConferenceListRow(stream_interactor, conference, account); widgets[account][conference.jid] = widget; - add(widget); + list_box.append(widget); } private void remove_conference(Account account, Jid 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); } } public void refresh_conferences() { - @foreach((widget) => { remove(widget); }); +// @foreach((widget) => { remove(widget); }); foreach (Account account in lists.keys) { foreach (Conference conference in lists[account]) { add_conference(account, conference); @@ -78,25 +80,8 @@ protected class ConferenceList : FilterableList { } } - private bool filter(ListBoxRow r) { - if (r.get_type().is_a(typeof(ListRow))) { - 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); + public ListBox get_list_box() { + return list_box; } } diff --git a/main/src/ui/add_conversation/list_row.vala b/main/src/ui/add_conversation/list_row.vala index 97c2b2fe..326dba98 100644 --- a/main/src/ui/add_conversation/list_row.vala +++ b/main/src/ui/add_conversation/list_row.vala @@ -6,16 +6,27 @@ using Xmpp; namespace Dino.Ui { -[GtkTemplate (ui = "/im/dino/Dino/add_conversation/list_row.ui")] -public class ListRow : ListBoxRow { +public class ListRow : Widget { - [GtkChild] public unowned AvatarImage image; - [GtkChild] public unowned Label name_label; - [GtkChild] public unowned Label via_label; + public Grid outer_grid; + public AvatarImage image; + public Label name_label; + public Label via_label; public Jid? jid; 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.from_jid(StreamInteractor stream_interactor, Jid jid, Account account, bool show_account) { diff --git a/main/src/ui/add_conversation/roster_list.vala b/main/src/ui/add_conversation/roster_list.vala index fd4d4ade..a89d24e8 100644 --- a/main/src/ui/add_conversation/roster_list.vala +++ b/main/src/ui/add_conversation/roster_list.vala @@ -6,22 +6,23 @@ using Xmpp; namespace Dino.Ui { -protected class RosterList : FilterableList { +protected class RosterList { public signal void conversation_selected(Conversation? conversation); private StreamInteractor stream_interactor; private Gee.List<Account> accounts; 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); public RosterList(StreamInteractor stream_interactor, Gee.List<Account> accounts) { this.stream_interactor = stream_interactor; this.accounts = accounts; - set_filter_func(filter); - set_header_func(header); - set_sort_func(sort); +// set_filter_func(filter); + list_box.set_header_func(header); +// set_sort_func(sort); handler_ids += stream_interactor.get_module(RosterManager.IDENTITY).removed_roster_item.connect( (account, jid, roster_item) => { if (accounts.contains(account)) { @@ -33,7 +34,7 @@ protected class RosterList : FilterableList { 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); }); @@ -42,7 +43,7 @@ protected class RosterList : FilterableList { private void on_removed_roster_item(Account account, Jid jid, Roster.Item roster_item) { if (rows.has_key(account) && rows[account].has_key(jid)) { - remove(rows[account][jid]); + list_box.remove(rows[account][jid]); rows[account].unset(jid); } } @@ -51,9 +52,9 @@ protected class RosterList : FilterableList { on_removed_roster_item(account, jid, roster_item); ListRow row = new ListRow.from_jid(stream_interactor, roster_item.jid, account, accounts.size > 1); rows[account][jid] = row; - add(row); - invalidate_sort(); - invalidate_filter(); + list_box.append(row); + list_box.invalidate_sort(); + list_box.invalidate_filter(); } private void fetch_roster_items(Account account) { @@ -69,25 +70,8 @@ protected class RosterList : FilterableList { } } - private bool filter(ListBoxRow r) { - if (r.get_type().is_a(typeof(ListRow))) { - 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); + public ListBox get_list_box() { + return list_box; } } diff --git a/main/src/ui/add_conversation/select_contact_dialog.vala b/main/src/ui/add_conversation/select_contact_dialog.vala index d78a17c1..4bf5b193 100644 --- a/main/src/ui/add_conversation/select_contact_dialog.vala +++ b/main/src/ui/add_conversation/select_contact_dialog.vala @@ -14,6 +14,7 @@ public class SelectContactDialog : Gtk.Dialog { public Button ok_button; private RosterList roster_list; + private ListBox roster_list_box; private SelectJidFragment select_jid_fragment; private StreamInteractor stream_interactor; private Gee.List<Account> accounts; @@ -45,7 +46,7 @@ public class SelectContactDialog : Gtk.Dialog { if (Util.use_csd()) { 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_end(ok_button); @@ -54,15 +55,15 @@ public class SelectContactDialog : Gtk.Dialog { cancel_button.halign = Align.START; ok_button.halign = Align.END; - box.add(cancel_button); - box.add(ok_button); + box.append(cancel_button); + box.append(ok_button); - get_content_area().add(box); + get_content_area().append(box); } cancel_button.clicked.connect(() => { close(); }); 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); close(); }); @@ -70,21 +71,22 @@ public class SelectContactDialog : Gtk.Dialog { private void setup_view() { roster_list = new RosterList(stream_interactor, accounts); - roster_list.row_activated.connect(() => { ok_button.clicked(); }); - select_jid_fragment = new SelectJidFragment(stream_interactor, roster_list, accounts); + roster_list_box = roster_list.get_list_box(); + 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) => { AddContactDialog add_contact_dialog = new AddContactDialog(stream_interactor); add_contact_dialog.set_transient_for(this); add_contact_dialog.present(); }); 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); }); select_jid_fragment.notify["done"].connect(() => { ok_button.sensitive = select_jid_fragment.done; }); - get_content_area().add(select_jid_fragment); + get_content_area().append(select_jid_fragment); } } diff --git a/main/src/ui/add_conversation/select_jid_fragment.vala b/main/src/ui/add_conversation/select_jid_fragment.vala index bc349b6c..58f019c1 100644 --- a/main/src/ui/add_conversation/select_jid_fragment.vala +++ b/main/src/ui/add_conversation/select_jid_fragment.vala @@ -12,7 +12,7 @@ public class SelectJidFragment : Gtk.Box { public signal void add_jid(); public signal void remove_jid(ListRow row); public bool done { - get { return filterable_list.get_selected_row() != null; } + get { return list.get_selected_row() != null; } private set {} } @@ -22,43 +22,45 @@ public class SelectJidFragment : Gtk.Box { [GtkChild] private unowned Button remove_button; private StreamInteractor stream_interactor; - private FilterableList filterable_list; private Gee.List<Account> accounts; - 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.filterable_list = filterable_list; + this.list = list; this.accounts = accounts; - filterable_list.visible = true; - filterable_list.activate_on_single_click = false; - filterable_list.vexpand = true; - box.add(filterable_list); + list.activate_on_single_click = false; + list.vexpand = true; + box.append(list); - filterable_list.set_sort_func(sort); - filterable_list.row_selected.connect(check_buttons_active); - filterable_list.row_selected.connect(() => { done = true; }); // just for notifying + list.set_sort_func(sort); + list.set_filter_func(filter); + list.row_selected.connect(check_buttons_active); + list.row_selected.connect(() => { done = true; }); // just for notifying entry.changed.connect(() => { set_filter(entry.text); }); 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) { 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(); - string[] ? values = str == "" ? null : str.split(" "); - filterable_list.set_filter_values(values); + filter_values = str == "" ? null : str.split(" "); + list.invalidate_filter(); + try { Jid parsed_jid = new Jid(str); if (parsed_jid != null && parsed_jid.localpart != null) { foreach (Account account in accounts) { AddListRow row = new AddListRow(stream_interactor, parsed_jid, account); - filterable_list.add(row); + list.append(row); added_rows.add(row); } } @@ -68,7 +70,7 @@ public class SelectJidFragment : Gtk.Box { } 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)); remove_button.sensitive = active; } @@ -81,7 +83,29 @@ public class SelectJidFragment : Gtk.Box { } else if (al2 != null && al1 == null) { 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 { @@ -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); -} - } diff --git a/main/src/ui/application.vala b/main/src/ui/application.vala index 6c02087f..f83e403b 100644 --- a/main/src/ui/application.vala +++ b/main/src/ui/application.vala @@ -35,7 +35,7 @@ public class Dino.Ui.Application : Gtk.Application, Dino.Application { CssProvider provider = new CssProvider(); 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(); 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"); if (desktop_env == null || !desktop_env.down().contains("gnome")) { 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); window = new MainWindow(this, stream_interactor, db, config); 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(); }); @@ -205,9 +205,9 @@ public class Dino.Ui.Application : Gtk.Application, Dino.Application { if (!use_csd()) { // Hack to prevent CRITICAL in Gtk when trying to destroy non-existant headerbar Widget shortcuts_hack = dialog.get_titlebar(); - dialog.destroy.connect_after(() => { - shortcuts_hack = null; - }); +// dialog.destroy.connect_after(() => { +// shortcuts_hack = null; +// }); dialog.set_titlebar(null); } 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.license_type = License.GPL_3_0; - dialog.response.connect((response_id) => { - if (response_id == Gtk.ResponseType.CANCEL || response_id == Gtk.ResponseType.DELETE_EVENT) { - dialog.destroy(); - } - }); +// dialog.response.connect((response_id) => { +// if (response_id == Gtk.ResponseType.CANCEL || response_id == Gtk.ResponseType.DELETE_EVENT) { +// dialog.destroy(); +// } +// }); if (!use_csd()) { dialog.set_titlebar(null); @@ -315,7 +315,7 @@ public class Dino.Ui.Application : Gtk.Application, Dino.Application { conference_fragment.account = account; } Box content_area = dialog.get_content_area(); - content_area.add(conference_fragment); + content_area.append(conference_fragment); conference_fragment.joined.connect(() => { dialog.destroy(); }); diff --git a/main/src/ui/avatar_image.vala b/main/src/ui/avatar_image.vala index f7731373..a304f5a2 100644 --- a/main/src/ui/avatar_image.vala +++ b/main/src/ui/avatar_image.vala @@ -5,7 +5,7 @@ using Xmpp.Util; namespace Dino.Ui { -public class AvatarImage : Misc { +public class AvatarImage : Widget { public int height { get; set; default = 35; } public int width { get; set; default = 35; } public bool allow_gray { get; set; default = true; } @@ -34,17 +34,24 @@ public class AvatarImage : Misc { disconnect_stream_interactor(); } - public override void get_preferred_width(out int minimum_width, out int natural_width) { - minimum_width = width; - natural_width = width; + public override void measure(Orientation orientation, int for_size, out int minimum, out int natural, out int minimum_baseline, out int natural_baseline) { + if (orientation == Orientation.HORIZONTAL) { + 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) { - minimum_height = height; - natural_height = height; + public override void snapshot(Snapshot snapshot) { + Cairo.Context context = snapshot.append_cairo(Graphene.Rect.alloc().init(0, 0, width, 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; int width = this.width, height = this.height, base_factor = 1; if (use_image_surface == -1) { diff --git a/main/src/ui/call_window/audio_settings_popover.vala b/main/src/ui/call_window/audio_settings_popover.vala index f7e490cd..f5af90ff 100644 --- a/main/src/ui/call_window/audio_settings_popover.vala +++ b/main/src/ui/call_window/audio_settings_popover.vala @@ -14,11 +14,11 @@ public class Dino.Ui.AudioSettingsPopover : Gtk.Popover { private HashMap<ListBoxRow, Plugins.MediaDevice> row_speaker_device = new HashMap<ListBoxRow, Plugins.MediaDevice>(); public AudioSettingsPopover() { - Box box = new Box(Orientation.VERTICAL, 15) { margin=18, visible=true }; - box.add(create_microphone_box()); - box.add(create_speaker_box()); + Box box = new Box(Orientation.VERTICAL, 15) { visible=true }; + box.append(create_microphone_box()); + box.append(create_speaker_box()); - this.add(box); + this.set_child(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); 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) { - micro_box.add(new Label(_("No microphone found."))); + micro_box.append(new Label(_("No microphone found."))); } else { 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); 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) { 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) { image.opacity = 0; } @@ -48,21 +48,21 @@ public class Dino.Ui.AudioSettingsPopover : Gtk.Popover { image.opacity = 1; } }); - Box device_box = new Box(Orientation.HORIZONTAL, 0) { spacing=7, margin=7, visible=true }; - device_box.add(image); + Box device_box = new Box(Orientation.HORIZONTAL, 0) { spacing=7, visible=true }; + device_box.append(image); 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) { 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.attributes = new Pango.AttrList(); 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 }; - list_box_row.add(device_box); - micro_list_box.add(list_box_row); + list_box_row.set_child(device_box); + micro_list_box.append(list_box_row); row_microphone_device[list_box_row] = device; } @@ -71,7 +71,7 @@ public class Dino.Ui.AudioSettingsPopover : Gtk.Popover { microphone_selected(row_microphone_device[row]); micro_list_box.unselect_row(row); }); - micro_box.add(micro_frame); + micro_box.append(micro_frame); } 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); 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) { - speaker_box.add(new Label(_("No speaker found."))); + speaker_box.append(new Label(_("No speaker found."))); } else { 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); @@ -93,10 +93,10 @@ public class Dino.Ui.AudioSettingsPopover : Gtk.Popover { }); 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) { 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) { image.opacity = 0; } @@ -107,21 +107,21 @@ public class Dino.Ui.AudioSettingsPopover : Gtk.Popover { image.opacity = 1; } }); - Box device_box = new Box(Orientation.HORIZONTAL, 0) { spacing=7, margin=7, visible=true }; - device_box.add(image); + Box device_box = new Box(Orientation.HORIZONTAL, 0) { spacing=7, visible=true }; + device_box.append(image); 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) { 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.attributes = new Pango.AttrList(); 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 }; - list_box_row.add(device_box); - speaker_list_box.add(list_box_row); + list_box_row.set_child(device_box); + speaker_list_box.append(list_box_row); 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_list_box.unselect_row(row); }); - speaker_box.add(speaker_frame); + speaker_box.append(speaker_frame); } return speaker_box; diff --git a/main/src/ui/call_window/call_bottom_bar.vala b/main/src/ui/call_window/call_bottom_bar.vala index b3fa2093..ddc196f2 100644 --- a/main/src/ui/call_window/call_bottom_bar.vala +++ b/main/src/ui/call_window/call_bottom_bar.vala @@ -9,63 +9,58 @@ public class Dino.Ui.CallBottomBar : Gtk.Box { public bool audio_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; } 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 Image audio_image = new Image() { visible=true }; - private MenuButton audio_settings_button = new MenuButton() { halign=Align.END, valign=Align.END }; + private Image audio_image = new Image() { pixel_size=22 }; + private MenuButton audio_settings_button = new MenuButton() { icon_name="go-up-symbolic", halign=Align.END, valign=Align.END }; 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 Overlay video_button_overlay = new Overlay() { visible=true }; - private Image video_image = new Image() { visible=true }; - private MenuButton video_settings_button = new MenuButton() { halign=Align.END, valign=Align.END }; + private Image video_image = new Image() { pixel_size=22 }; + private MenuButton video_settings_button = new MenuButton() { icon_name="go-up-symbolic", halign=Align.END, valign=Align.END }; 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 }; public CallBottomBar() { 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.clicked.connect(() => { audio_enabled = !audio_enabled; }); 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_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.use_popover = true; - main_buttons.add(audio_button_overlay); + main_buttons.append(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.clicked.connect(() => { video_enabled = !video_enabled; }); 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_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.use_popover = true; - main_buttons.add(video_button_overlay); + main_buttons.append(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("destructive-action"); 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"); stack.add_named(main_buttons, "control-buttons"); stack.add_named(label, "label"); - this.add(stack); + this.append(stack); this.notify["audio-enabled"].connect(on_audio_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; audio_settings_popover = new AudioSettingsPopover(); - audio_settings_button.popover = audio_settings_popover; - - audio_settings_popover.set_relative_to(audio_settings_button); - audio_settings_popover.microphone_selected.connect(() => { audio_settings_button.active = false; }); - audio_settings_popover.speaker_selected.connect(() => { audio_settings_button.active = false; }); + audio_settings_popover.microphone_selected.connect(() => { audio_settings_button.popdown(); }); + audio_settings_popover.speaker_selected.connect(() => { audio_settings_button.popdown(); }); return audio_settings_popover; } 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); } @@ -106,28 +98,24 @@ public class Dino.Ui.CallBottomBar : Gtk.Box { if (!show) return null; video_settings_popover = new VideoSettingsPopover(); - - video_settings_button.popover = video_settings_popover; - - video_settings_popover.set_relative_to(video_settings_button); - video_settings_popover.camera_selected.connect(() => { video_settings_button.active = false; }); + video_settings_popover.camera_selected.connect(() => { video_settings_button.popdown(); }); return video_settings_popover; } 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); } public void on_audio_enabled_changed() { 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().remove_class("transparent-white-button"); } 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().add_class("transparent-white-button"); } @@ -135,12 +123,12 @@ public class Dino.Ui.CallBottomBar : Gtk.Box { public void on_video_enabled_changed() { 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().remove_class("transparent-white-button"); } 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().add_class("transparent-white-button"); } @@ -152,6 +140,6 @@ public class Dino.Ui.CallBottomBar : Gtk.Box { } 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? } }
\ No newline at end of file diff --git a/main/src/ui/call_window/call_connection_details_window.vala b/main/src/ui/call_window/call_connection_details_window.vala index 1d5265c9..0905908c 100644 --- a/main/src/ui/call_window/call_connection_details_window.vala +++ b/main/src/ui/call_window/call_connection_details_window.vala @@ -4,16 +4,16 @@ namespace Dino.Ui { 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 CallContentDetails audio_details = new CallContentDetails("Audio") { visible=true }; private CallContentDetails video_details = new CallContentDetails("Video"); public CallConnectionDetailsWindow() { - box.add(audio_details); - box.add(video_details); - add(box); + box.append(audio_details); + box.append(video_details); + set_child(box); } public void update_content(PeerInfo peer_info) { diff --git a/main/src/ui/call_window/call_encryption_button.vala b/main/src/ui/call_window/call_encryption_button.vala index a7081954..095db2b4 100644 --- a/main/src/ui/call_window/call_encryption_button.vala +++ b/main/src/ui/call_window/call_encryption_button.vala @@ -2,61 +2,63 @@ using Dino.Entities; using Gtk; 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; public bool controls_active { get; set; default=false; } - public CallEncryptionButton() { - this.opacity = 0; - add(encryption_image); - this.set_popover(popover); + public MenuButton button; - 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) { if (encrypted) { - encryption_image.set_from_icon_name(icon_name ?? "changes-prevent-symbolic", IconSize.BUTTON); - get_style_context().remove_class("unencrypted"); + button.icon_name = icon_name ?? "changes-prevent-symbolic"; + button.get_style_context().remove_class("unencrypted"); } else { - encryption_image.set_from_icon_name(icon_name ?? "changes-allow-symbolic", IconSize.BUTTON); - get_style_context().add_class("unencrypted"); + button.icon_name = icon_name ?? "changes-allow-symbolic"; + button.get_style_context().add_class("unencrypted"); } has_been_set = true; update_opacity(); } 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); - this.set_popover(popover); + Popover popover = new Popover(); + button.set_popover(popover); 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; } 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; } - Box box = new Box(Orientation.VERTICAL, 10) { margin=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 box = new Box(Orientation.VERTICAL, 10) { 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) { - box.add(create_media_encryption_grid(audio_encryption)); + box.append(create_media_encryption_grid(audio_encryption)); } else { - box.add(new Label("<b>Audio</b>") { use_markup=true, xalign=0, visible=true }); - box.add(create_media_encryption_grid(audio_encryption)); - box.add(new Label("<b>Video</b>") { use_markup=true, xalign=0, visible=true }); - box.add(create_media_encryption_grid(video_encryption)); + box.append(new Label("<b>Audio</b>") { use_markup=true, xalign=0, visible=true }); + box.append(create_media_encryption_grid(audio_encryption)); + box.append(new Label("<b>Video</b>") { use_markup=true, xalign=0, visible=true }); + box.append(create_media_encryption_grid(video_encryption)); } - popover.add(box); + popover.set_child(box); } 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) { diff --git a/main/src/ui/call_window/call_window.vala b/main/src/ui/call_window/call_window.vala index ab969597..5facd574 100644 --- a/main/src/ui/call_window/call_window.vala +++ b/main/src/ui/call_window/call_window.vala @@ -15,13 +15,15 @@ namespace Dino.Ui { public Grid grid = new Grid() { 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 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 Box own_video_box = new Box(Orientation.HORIZONTAL, 0) { halign=Align.END, valign=Align.END, visible=true }; private Widget? own_video = null; private HashMap<string, ParticipantWidget> participant_widgets = new HashMap<string, ParticipantWidget>(); private ArrayList<string> participants = new ArrayList<string>(); + private EventControllerMotion this_motion_events = new EventControllerMotion(); + private int own_video_width = 150; private int own_video_height = 100; @@ -31,32 +33,36 @@ namespace Dino.Ui { construct { header_bar.get_style_context().add_class("call-header-bar"); - header_bar.custom_title = new Box(Orientation.VERTICAL, 0); - header_bar.spacing = 0; - header_bar_revealer.add(header_bar); - bottom_bar_revealer.add(bottom_bar); + header_bar.title_widget = new Box(Orientation.VERTICAL, 0); +// header_bar.spacing = 0; + header_bar_revealer.set_child(header_bar); + bottom_bar_revealer.set_child(bottom_bar); own_video_box.get_style_context().add_class("own-video"); 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(bottom_bar_revealer); overlay.add_overlay(header_bar_revealer); overlay.get_child_position.connect(on_get_child_position); - add(overlay); + set_child(overlay); } public CallWindow() { this.bind_property("controls-active", bottom_bar_revealer, "reveal-child", BindingFlags.SYNC_CREATE); - this.motion_notify_event.connect(reveal_control_elements); - this.enter_notify_event.connect(reveal_control_elements); - this.leave_notify_event.connect(reveal_control_elements); - this.configure_event.connect(reveal_control_elements); // upon resizing + ((Widget) this).add_controller(this_motion_events); + this_motion_events.motion.connect(reveal_control_elements); + this_motion_events.enter.connect(reveal_control_elements); + this_motion_events.leave.connect(reveal_control_elements); + + this.notify["default-width"].connect(reveal_control_elements); + this.notify["default-height"].connect(reveal_control_elements); - this.configure_event.connect(reposition_participant_widgets); + 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 }); @@ -103,11 +109,10 @@ namespace Dino.Ui { } } - private bool reposition_participant_widgets() { - int width, height; - this.get_size(out width,out height); + private void reposition_participant_widgets() { + int width = get_size(Orientation.HORIZONTAL); + int height = get_size(Orientation.VERTICAL); 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) { @@ -144,14 +149,14 @@ namespace Dino.Ui { } 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_; 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_box.add(own_video); + own_video_box.append(own_video); } public void set_own_video_ratio(int width, int height) { @@ -165,7 +170,7 @@ namespace Dino.Ui { } 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) { @@ -192,13 +197,12 @@ namespace Dino.Ui { bottom_bar.show_counterpart_ended(text); } - private bool reveal_control_elements() { + private void reveal_control_elements() { if (!bottom_bar_revealer.child_revealed) { controls_active = true; } timeout_hide_control_elements(); - return false; } 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) { if (widget == own_video_box) { - int width, height; - this.get_size(out width,out height); + int width = get_size(Orientation.HORIZONTAL); + int height = get_size(Orientation.VERTICAL); allocation = Gdk.Rectangle(); allocation.width = own_video_width; @@ -252,8 +256,8 @@ namespace Dino.Ui { public OutsideHeaderBar(HeaderBar header_bar) { this.header_bar = header_bar; - size_allocate.connect_after(on_header_bar_size_allocate); - header_bar.size_allocate.connect(on_header_bar_size_allocate); +// size_allocate.connect_after(on_header_bar_size_allocate); +// header_bar.size_allocate.connect(on_header_bar_size_allocate); } public void on_header_bar_size_allocate() { @@ -263,7 +267,7 @@ namespace Dino.Ui { Allocation alloc; get_allocation(out alloc); alloc.height = header_bar_alloc.height; - set_allocation(alloc); +// set_allocation(alloc); } } }
\ No newline at end of file diff --git a/main/src/ui/call_window/call_window_controller.vala b/main/src/ui/call_window/call_window_controller.vala index 276fff98..6ff02964 100644 --- a/main/src/ui/call_window/call_window_controller.vala +++ b/main/src/ui/call_window/call_window_controller.vala @@ -31,7 +31,7 @@ public class Dino.Ui.CallWindowController : Object { this.stream_interactor = stream_interactor; 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 @@ -70,9 +70,10 @@ public class Dino.Ui.CallWindowController : Object { call_window.destroy(); this.dispose(); }); - call_window_handler_ids += call_window.destroy.connect(() => { + call_window_handler_ids += call_window.close_request.connect(() => { call_state.end(); this.dispose(); + return false; }); bottom_bar_handler_ids += call_window.bottom_bar.notify["audio-enabled"].connect(() => { 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); update_own_video(); }); - call_window_handler_ids += call_window.configure_event.connect((event) => { - if (window_width == -1 || window_height == -1) return false; - int current_height = this.call_window.get_allocated_height(); + call_window_handler_ids += call_window.notify["default-width"].connect((event) => { + if (call_window.default_width == -1) return; int current_width = this.call_window.get_allocated_width(); - if (window_width != current_width || window_height != current_height) { - 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); + if (window_width != current_width) { + 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; + } + }); + 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; } - return false; }); - call_window_handler_ids += call_window.realize.connect(() => { + call_window_handler_ids += ((Widget)call_window).realize.connect(() => { capture_window_size(); }); @@ -138,7 +145,7 @@ public class Dino.Ui.CallWindowController : Object { Gee.List<Account> acc_list = new ArrayList<Account>(Account.equals_func); acc_list.add(call.account); 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.ok_button.label = _("Invite"); 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) => { - 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? icon_name = null; bool show_keys = true; @@ -235,24 +242,26 @@ public class Dino.Ui.CallWindowController : Object { return true; }); 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(); - 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()); participant_widgets[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) => { if (window_size_changed || participant_widgets.size > 1) return; if (width == 0 || height == 0) return; 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 { - call_window.resize((int) (width * 704 / height), 704); + call_window.default_width = (int) (width * 704 / height); + call_window.default_height = 704; } capture_window_size(); }); diff --git a/main/src/ui/call_window/participant_widget.vala b/main/src/ui/call_window/participant_widget.vala index f18ae242..ecd6cbb3 100644 --- a/main/src/ui/call_window/participant_widget.vala +++ b/main/src/ui/call_window/participant_widget.vala @@ -6,15 +6,19 @@ using Gtk; namespace Dino.Ui { - public class ParticipantWidget : Gtk.Overlay { + public class ParticipantWidget : Box { + public Overlay overlay = new Overlay(); public Widget main_widget; 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 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 menu_button = new MenuButton() { relief=ReliefStyle.NONE, visible=true }; - public Button invite_button = new Button.from_icon_name("dino-account-plus") { relief=ReliefStyle.NONE, 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 CallEncryptionButtonController encryption_button_controller; + 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 string? participant_name; @@ -26,19 +30,38 @@ namespace Dino.Ui { public signal void debug_information_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) { + encryption_button_controller = new CallEncryptionButtonController(encryption_button); + 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.pack_start(invite_button); header_bar.pack_start(encryption_button); header_bar.pack_end(menu_button); - menu_button.image = new Image.from_icon_name("open-menu-symbolic", IconSize.MENU); - menu_button.set_popover(create_menu()); + create_menu(); + 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["may-show-invite-button"].connect(reveal_or_hide_controls); @@ -48,7 +71,7 @@ namespace Dino.Ui { this.is_highest_row = is_highest; 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) { header_bar.get_style_context().add_class("call-header-background"); Gtk.Settings? gtk_settings = Gtk.Settings.get_default(); @@ -78,37 +101,35 @@ namespace Dino.Ui { } else { avatar.set_text("?", false); } - box.add(avatar); + box.append(avatar); set_participant_widget(box); } private void set_participant_widget(Widget widget) { - widget.expand = true; - if (main_widget != null) this.remove(main_widget); + widget.hexpand = widget.vexpand = true; main_widget = widget; - this.add(main_widget); + overlay.set_child(main_widget); } - private PopoverMenu create_menu() { - PopoverMenu menu = new PopoverMenu(); - Box box = new Box(Orientation.VERTICAL, 0) { margin=10, visible=true }; - ModelButton debug_information_button = new ModelButton() { text=_("Debug information"), visible=true }; - debug_information_button.clicked.connect(() => debug_information_clicked()); - box.add(debug_information_button); - menu.add(box); - return menu; + private void create_menu() { + Menu menu_model = new Menu(); + menu_model.append(_("Debug information"), "menu.debuginfo"); + Gtk.PopoverMenu popover_menu = new Gtk.PopoverMenu.from_model(menu_model); + menu_button.popover = popover_menu; } public void set_status(string state) { + subtitle_label.visible = true; + if (state == "requested") { - header_bar.subtitle = _("Calling…"); + subtitle_label.label = _("Calling…"); } else if (state == "ringing") { - header_bar.subtitle = _("Ringing…"); + subtitle_label.label = _("Ringing…"); } else if (state == "establishing") { - header_bar.subtitle = _("Connecting…"); + subtitle_label.label = _("Connecting…"); } else { - header_bar.subtitle = ""; + subtitle_label.visible = false; } } diff --git a/main/src/ui/call_window/video_settings_popover.vala b/main/src/ui/call_window/video_settings_popover.vala index 7dd5ec9f..c931c466 100644 --- a/main/src/ui/call_window/video_settings_popover.vala +++ b/main/src/ui/call_window/video_settings_popover.vala @@ -11,10 +11,10 @@ public class Dino.Ui.VideoSettingsPopover : Gtk.Popover { private HashMap<ListBoxRow, Plugins.MediaDevice> row_device = new HashMap<ListBoxRow, Plugins.MediaDevice>(); public VideoSettingsPopover() { - Box box = new Box(Orientation.VERTICAL, 15) { margin=18, visible=true }; - box.add(create_camera_box()); + Box box = new Box(Orientation.VERTICAL, 15) { visible=true }; + box.append(create_camera_box()); - this.add(box); + this.set_child(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); 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) { - camera_box.add(new Label(_("No camera found.")) { visible=true }); + camera_box.append(new Label(_("No camera found.")) { visible=true }); } else { ListBox list_box = new ListBox() { activate_on_single_click=true, selection_mode=SelectionMode.SINGLE, visible=true }; list_box.set_header_func(listbox_header_func); Frame frame = new Frame(null) { visible=true }; - frame.add(list_box); + frame.set_child(list_box); foreach (Plugins.MediaDevice device in devices) { 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) { image.opacity = 0; } @@ -44,21 +44,21 @@ public class Dino.Ui.VideoSettingsPopover : Gtk.Popover { image.opacity = 1; } }); - Box device_box = new Box(Orientation.HORIZONTAL, 0) { spacing=7, margin=7, visible=true }; - device_box.add(image); + Box device_box = new Box(Orientation.HORIZONTAL, 0) { spacing=7, visible=true }; + device_box.append(image); 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) { 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.attributes = new Pango.AttrList(); 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 }; - list_box_row.add(device_box); - list_box.add(list_box_row); + list_box_row.set_child(device_box); + list_box.append(list_box_row); row_device[list_box_row] = device; } @@ -67,7 +67,7 @@ public class Dino.Ui.VideoSettingsPopover : Gtk.Popover { camera_selected(row_device[row]); list_box.unselect_row(row); }); - camera_box.add(frame); + camera_box.append(frame); } return camera_box; diff --git a/main/src/ui/chat_input/chat_input_controller.vala b/main/src/ui/chat_input/chat_input_controller.vala index d7a69c3d..41891519 100644 --- a/main/src/ui/chat_input/chat_input_controller.vala +++ b/main/src/ui/chat_input/chat_input_controller.vala @@ -34,9 +34,12 @@ public class ChatInputController : Object { reset_input_field_status(); - chat_input.chat_text_view.text_view.buffer.changed.connect(on_text_input_changed); - chat_input.chat_text_view.text_view.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); + 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.buffer.changed.connect(on_text_input_changed); chat_text_view_controller.send_text.connect(send_text); @@ -50,7 +53,7 @@ public class ChatInputController : Object { status_description_label.activate_link.connect((uri) => { if (uri == OPEN_CONVERSATION_DETAILS_URI){ 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(); } return true; @@ -136,7 +139,7 @@ public class ChatInputController : Object { case "/ping": Xmpp.XmppStream? stream = stream_interactor.get_stream(conversation.account); 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) { 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) { - if (event.keyval == Gdk.Key.Up && chat_input.chat_text_view.text_view.buffer.text == "") { + private bool on_text_input_key_press(uint keyval, uint keycode, Gdk.ModifierType state) { + if (keyval == Gdk.Key.Up && chat_input.chat_text_view.text_view.buffer.text == "") { activate_last_message_correction(); return true; } else { diff --git a/main/src/ui/chat_input/chat_text_view.vala b/main/src/ui/chat_input/chat_text_view.vala index 2f8393d2..b1f719b6 100644 --- a/main/src/ui/chat_input/chat_text_view.vala +++ b/main/src/ui/chat_input/chat_text_view.vala @@ -30,61 +30,64 @@ public class ChatTextViewController : Object { } } -public class ChatTextView : ScrolledWindow { +public class ChatTextView : Box { public signal void send_text(); 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 SmileyConverter smiley_converter; - public EditHistory edit_history; - private SpellChecker spell_checker; +// private SpellChecker spell_checker; construct { - max_content_height = 300; - propagate_natural_height = true; - this.add(text_view); + scrolled_window.set_child(text_view); + this.append(scrolled_window); 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); - this.vadjustment.notify["upper"].connect_after(on_upper_notify); - text_view.key_press_event.connect(on_text_input_key_press); +// scrolled_window.get_vscrollbar().get_preferred_size(out vscrollbar_min_size, null); + scrolled_window.vadjustment.notify["upper"].connect(on_upper_notify); - 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) { - 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) { - base.get_preferred_height(out min_height, out nat_height); - min_height = 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); +// min_height = nat_height; +// } 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 - 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) { - if (event.keyval in new uint[]{Key.Return, Key.KP_Enter}) { - if ((event.state & ModifierType.SHIFT_MASK) > 0) { + private bool on_text_input_key_press(uint keyval, uint keycode, Gdk.ModifierType state) { + if (keyval in new uint[]{ Key.Return, Key.KP_Enter }) { + if ((state & ModifierType.SHIFT_MASK) > 0) { text_view.buffer.insert_at_cursor("\n", 1); } else if (text_view.buffer.text.strip() != "") { send_text(); - edit_history.reset_history(); } return true; } - if (event.keyval == Key.Escape) { + if (keyval == Key.Escape) { cancel_input(); } return false; diff --git a/main/src/ui/chat_input/edit_history.vala b/main/src/ui/chat_input/edit_history.vala deleted file mode 100644 index 70f6d400..00000000 --- a/main/src/ui/chat_input/edit_history.vala +++ /dev/null @@ -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; - } -} - -} diff --git a/main/src/ui/chat_input/encryption_button.vala b/main/src/ui/chat_input/encryption_button.vala index 11466931..e5831802 100644 --- a/main/src/ui/chat_input/encryption_button.vala +++ b/main/src/ui/chat_input/encryption_button.vala @@ -5,27 +5,25 @@ using Dino.Entities; namespace Dino.Ui { -public class EncryptionButton : MenuButton { +public class EncryptionButton { public signal void encryption_changed(Plugins.EncryptionListEntry? encryption_entry); + private MenuButton menu_button; private Conversation? conversation; - private RadioButton? button_unencrypted; - private Map<RadioButton, Plugins.EncryptionListEntry> encryption_radios = new HashMap<RadioButton, Plugins.EncryptionListEntry>(); + private CheckButton? button_unencrypted; + private Map<CheckButton, Plugins.EncryptionListEntry> encryption_radios = new HashMap<CheckButton, Plugins.EncryptionListEntry>(); private string? current_icon; private StreamInteractor stream_interactor; - public EncryptionButton(StreamInteractor stream_interactor) { + public EncryptionButton(StreamInteractor stream_interactor, MenuButton menu_button) { this.stream_interactor = stream_interactor; - - use_popover = true; - image = new Image.from_icon_name("changes-allow-symbolic", IconSize.BUTTON); - get_style_context().add_class("flat"); + this.menu_button = menu_button; 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; - 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); 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; 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; btn.toggled.connect(encryption_button_toggled); 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() { - foreach (RadioButton e in encryption_radios.keys) { + foreach (CheckButton e in encryption_radios.keys) { if (e.get_active()) { conversation.encryption = encryption_radios[e].encryption; encryption_changed(encryption_radios[e]); @@ -62,7 +61,7 @@ public class EncryptionButton : MenuButton { } 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) { e.set_active(true); encryption_changed(encryption_radios[e]); @@ -76,7 +75,7 @@ public class EncryptionButton : MenuButton { private void set_icon(string icon) { if (icon != current_icon) { - image = new Image.from_icon_name(icon, IconSize.BUTTON); + menu_button.set_icon_name(icon); current_icon = icon; } } @@ -87,23 +86,23 @@ public class EncryptionButton : MenuButton { private void update_visibility() { if (conversation.encryption != Encryption.NONE) { - visible = true; + menu_button.visible = true; return; } switch (conversation.type_) { case Conversation.Type.CHAT: - visible = true; + menu_button.visible = true; break; case Conversation.Type.GROUPCHAT_PM: - visible = false; + menu_button.visible = false; break; 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; } } - public new void set_conversation(Conversation conversation) { + public void set_conversation(Conversation conversation) { this.conversation = conversation; update_encryption_menu_state(); update_encryption_menu_icon(); @@ -111,4 +110,4 @@ public class EncryptionButton : MenuButton { } } -} +}
\ No newline at end of file diff --git a/main/src/ui/chat_input/occupants_tab_completer.vala b/main/src/ui/chat_input/occupants_tab_completer.vala index 6d2a7434..e50fe831 100644 --- a/main/src/ui/chat_input/occupants_tab_completer.vala +++ b/main/src/ui/chat_input/occupants_tab_completer.vala @@ -27,16 +27,18 @@ public class OccupantsTabCompletor { this.stream_interactor = stream_interactor; 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) { 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 (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; int start_index = int.max(text.last_index_of(" "), text.last_index_of("\n")) + 1; string word = text.substring(start_index); @@ -51,11 +53,11 @@ public class OccupantsTabCompletor { index = -1; } } - if (event.keyval != Key.ISO_Group_Shift && active) { - text_input.buffer.text = next_completion(event.keyval == Key.ISO_Left_Tab); + if (keyval != Key.ISO_Group_Shift && active) { + text_input.buffer.text = next_completion(keyval == Key.ISO_Left_Tab); return true; } - } else if (event.keyval != Key.Shift_L && active) { + } else if (keyval != Key.Shift_L && active) { active = false; } } diff --git a/main/src/ui/chat_input/smiley_converter.vala b/main/src/ui/chat_input/smiley_converter.vala index 8f4dee9a..fe280d99 100644 --- a/main/src/ui/chat_input/smiley_converter.vala +++ b/main/src/ui/chat_input/smiley_converter.vala @@ -33,11 +33,13 @@ class SmileyConverter { public SmileyConverter(TextView 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) { - if (event.keyval == Key.space || event.keyval == Key.Return) { + public bool on_text_input_key_press(uint keyval, uint keycode, Gdk.ModifierType state) { + if (keyval == Key.space || keyval == Key.Return) { check_convert(); } return false; diff --git a/main/src/ui/chat_input/view.vala b/main/src/ui/chat_input/view.vala index 5142eb10..6524a825 100644 --- a/main/src/ui/chat_input/view.vala +++ b/main/src/ui/chat_input/view.vala @@ -23,6 +23,8 @@ public class View : Box { [GtkChild] public unowned ChatTextView chat_text_view; [GtkChild] public unowned Box outer_box; [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 Label chat_input_status; @@ -31,29 +33,13 @@ public class View : Box { public View init(StreamInteractor 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"); - - encryption_widget.get_style_context().add_class("dino-chatinput-button"); - - // Emoji button for emoji picker (recents don't work < 3.22.19, category icons don't work <3.23.2) - 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); + 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); Util.force_css(frame, "* { border-radius: 3px; }"); diff --git a/main/src/ui/contact_details/blocking_provider.vala b/main/src/ui/contact_details/blocking_provider.vala index bf59a083..76e5d000 100644 --- a/main/src/ui/contact_details/blocking_provider.vala +++ b/main/src/ui/contact_details/blocking_provider.vala @@ -14,7 +14,7 @@ public class BlockingProvider : Plugins.ContactDetailsProvider, Object { } 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 (stream_interactor.get_module(BlockingManager.IDENTITY).is_supported(conversation.account)) { diff --git a/main/src/ui/contact_details/dialog.vala b/main/src/ui/contact_details/dialog.vala index f29d068e..b07ab9c9 100644 --- a/main/src/ui/contact_details/dialog.vala +++ b/main/src/ui/contact_details/dialog.vala @@ -37,7 +37,7 @@ public class Dialog : Gtk.Dialog { title = conversation.type_ == Conversation.Type.GROUPCHAT ? _("Conference Details") : _("Contact Details"); if (Util.use_csd()) { // 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(); @@ -50,23 +50,24 @@ public class Dialog : Gtk.Dialog { app.plugin_registry.register_contact_details_entry(new PermissionsProvider(stream_interactor)); 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(() => { - contact_details.save(); - }); +// destroy.connect(() => { +// contact_details.save(); +// }); } private void setup_top() { if (conversation.type_ == Conversation.Type.CHAT) { 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); - destroy.connect(() => { + close_request.connect(() => { 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); } + return false; }); } else { name_hybrid.visible = false; @@ -84,18 +85,18 @@ public class Dialog : Gtk.Dialog { 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 }; - list_row.add(row); + list_row.set_child(row); Label label_label = new Label(label) { xalign=0, yalign=0.5f, hexpand=true, visible=true }; if (description != null && description != "") { 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 }; desc_label.set_markup("<span size='small'>%s</span>".printf(Markup.escape_text(description))); desc_label.get_style_context().add_class("dim-label"); - box.add(desc_label); - row.add(box); + box.append(desc_label); + row.append(box); } else { - row.add(label_label); + row.append(label_label); } Widget widget = w; @@ -112,13 +113,13 @@ public class Dialog : Gtk.Dialog { widget.margin_top = 5; - row.add(widget); - categories[category].add(list_row); + row.append(widget); + categories[category].append(list_row); int pref_height, pref_width; - get_content_area().get_preferred_height(null, out pref_height); - get_preferred_width(out pref_width, null); - resize(pref_width, int.min(500, pref_height)); +// get_content_area().get_preferred_height(null, out pref_height); +// get_preferred_width(out pref_width, null); +// resize(pref_width, int.min(500, pref_height)); } 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 }; Label category_label = new Label("") { xalign=0, visible=true }; 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.add(list_box); - box.add(frame); - main_box.add(box); + frame.set_child(list_box); + box.append(frame); + main_box.append(box); } } } diff --git a/main/src/ui/contact_details/muc_config_form_provider.vala b/main/src/ui/contact_details/muc_config_form_provider.vala index 5b4184c5..1244a759 100644 --- a/main/src/ui/contact_details/muc_config_form_provider.vala +++ b/main/src/ui/contact_details/muc_config_form_provider.vala @@ -15,7 +15,7 @@ public class MucConfigFormProvider : Plugins.ContactDetailsProvider, Object { } 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) { Xmpp.XmppStream? stream = stream_interactor.get_stream(conversation.account); if (stream == null) return; diff --git a/main/src/ui/contact_details/permissions_provider.vala b/main/src/ui/contact_details/permissions_provider.vala index 1fd460ad..d87658ff 100644 --- a/main/src/ui/contact_details/permissions_provider.vala +++ b/main/src/ui/contact_details/permissions_provider.vala @@ -14,13 +14,13 @@ public class PermissionsProvider : Plugins.ContactDetailsProvider, Object { } 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); if (own_jid == null) return; 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)); contact_details.add(_("Permissions"), _("Request permission to send messages"), "", voice_request); } diff --git a/main/src/ui/contact_details/settings_provider.vala b/main/src/ui/contact_details/settings_provider.vala index 262029a2..42c690e5 100644 --- a/main/src/ui/contact_details/settings_provider.vala +++ b/main/src/ui/contact_details/settings_provider.vala @@ -17,7 +17,7 @@ public class SettingsProvider : Plugins.ContactDetailsProvider, Object { } 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)) { string details_headline = conversation.type_ == Conversation.Type.GROUPCHAT ? DETAILS_HEADLINE_ROOM : DETAILS_HEADLINE_CHAT; diff --git a/main/src/ui/conversation_content/conversation_item_factory.vala b/main/src/ui/conversation_content/conversation_item_factory.vala new file mode 100644 index 00000000..29a83785 --- /dev/null +++ b/main/src/ui/conversation_content/conversation_item_factory.vala @@ -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); + } +}
\ No newline at end of file diff --git a/main/src/ui/conversation_content_view/call_widget.vala b/main/src/ui/conversation_content_view/call_widget.vala index e45792e2..645c31c1 100644 --- a/main/src/ui/conversation_content_view/call_widget.vala +++ b/main/src/ui/conversation_content_view/call_widget.vala @@ -17,7 +17,7 @@ namespace Dino.Ui { 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; 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 }; @@ -45,6 +45,7 @@ namespace Dino.Ui { private Conversation conversation; public Call.State call_state { get; set; } // needs to be public for binding private uint time_update_handler_id = 0; + private ArrayList<Widget> multiparty_peer_widgets = new ArrayList<Widget>(); construct { margin_top = 4; @@ -58,11 +59,11 @@ namespace Dino.Ui { this.call = call; this.conversation = conversation; - size_allocate.connect((allocation) => { - if (allocation.height > parent.get_allocated_height()) { - Idle.add(() => { parent.queue_resize(); return false; }); - } - }); +// size_allocate.connect((allocation) => { +// if (allocation.height > parent.get_allocated_height()) { +// Idle.add(() => { parent.queue_resize(); return false; }); +// } +// }); call.bind_property("state", this, "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.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) { AvatarImage image = new AvatarImage() { force_gray=true, margin_top=2, visible=true }; 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 }; 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"); @@ -121,7 +126,7 @@ namespace Dino.Ui { switch (relevant_state) { 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) { bool video = call_manager.should_we_send_video(); @@ -146,7 +151,7 @@ namespace Dino.Ui { break; case Call.State.ESTABLISHING: 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"); string duration = get_duration_string((new DateTime.now_utc()).difference(call.local_time)); subtitle_label.label = _("Started %s ago").printf(duration); @@ -162,13 +167,13 @@ namespace Dino.Ui { break; 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"); subtitle_label.label = _("You handled this call on another device"); break; 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"); 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)); @@ -177,7 +182,7 @@ namespace Dino.Ui { _("Lasted %s").printf(duration); break; 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"); if (call.direction == Call.DIRECTION_INCOMING) { subtitle_label.label = _("You missed this call"); @@ -187,7 +192,7 @@ namespace Dino.Ui { } break; 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"); if (call.direction == Call.DIRECTION_INCOMING) { subtitle_label.label = _("You declined this call"); @@ -197,7 +202,7 @@ namespace Dino.Ui { } break; 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"); subtitle_label.label = "Call failed to establish"; break; diff --git a/main/src/ui/conversation_content_view/chat_state_populator.vala b/main/src/ui/conversation_content_view/chat_state_populator.vala index 247c83fe..0665caac 100644 --- a/main/src/ui/conversation_content_view/chat_state_populator.vala +++ b/main/src/ui/conversation_content_view/chat_state_populator.vala @@ -76,14 +76,14 @@ private class MetaChatStateItem : Plugins.MetaConversationItem { 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.get_style_context().add_class("dim-label"); image = new AvatarImage() { margin_top=2, valign=Align.START, visible=true }; Box image_content_box = new Box(Orientation.HORIZONTAL, 8) { visible=true }; - image_content_box.add(image); - image_content_box.add(label); + image_content_box.append(image); + image_content_box.append(label); update(); return image_content_box; diff --git a/main/src/ui/conversation_content_view/conversation_item_skeleton.vala b/main/src/ui/conversation_content_view/conversation_item_skeleton.vala index 077440d6..3e4ce88b 100644 --- a/main/src/ui/conversation_content_view/conversation_item_skeleton.vala +++ b/main/src/ui/conversation_content_view/conversation_item_skeleton.vala @@ -7,7 +7,16 @@ using Dino.Entities; 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 last_group_item { get; set; default=true; } @@ -20,188 +29,130 @@ public class ConversationItemSkeleton : EventBox { public ContentMetaItem? content_meta_item = null; public Widget? widget = null; - private Box image_content_box = new Box(Orientation.HORIZONTAL, 8) { visible=true }; - private Box header_content_box = new Box(Orientation.VERTICAL, 0) { visible=true }; - private ItemMetaDataHeader? metadata_header = null; - private AvatarImage? image = null; + private uint time_update_timeout = 0; + private ulong updated_roster_handler_id = 0; public ConversationItemSkeleton(StreamInteractor stream_interactor, Conversation conversation, Plugins.MetaConversationItem item, bool initial_item) { this.stream_interactor = stream_interactor; this.conversation = conversation; this.item = item; 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"); - this.notify["item-in-edit-mode"].connect(update_edit_mode); - item.bind_property("mark", this, "item-mark", BindingFlags.SYNC_CREATE); - this.notify["item-mark"].connect(update_error_mode); - update_error_mode(); + Builder builder = new Builder.from_resource("/im/dino/Dino/conversation_item_widget.ui"); + 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"); - widget = item.get_widget(Plugins.WidgetType.GTK) as Widget; + widget = item.get_widget(this, Plugins.WidgetType.GTK4) as Widget; if (widget != null) { widget.valign = Align.END; - header_content_box.add(widget); + set_widget(widget, Plugins.WidgetType.GTK4); } - image_content_box.add(header_content_box); - - 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; + if (item.requires_header) { + avatar_image.set_conversation_participant(stream_interactor, conversation, item.jid); } - this.notify["show-skeleton"].connect(update_margin); this.notify["last-group-item"].connect(update_margin); + this.notify["show-skeleton"].connect(set_header); update_margin(); } - private void update_margin() { - if (item.requires_header && show_skeleton && metadata_header == null) { - 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); - } + private void set_header() { + if (!show_skeleton || !item.requires_header) return; + + 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) => { + if (this.conversation.account.equals(account) && this.conversation.counterpart.equals(jid)) { + update_name_label(); + } + }); + + item.notify["encryption"].connect(update_encryption_icon); + update_encryption_icon(); - if (image != null) { - image.visible = this.show_skeleton; + if (item.time != null) { + update_time(); } - if (metadata_header != null) { - metadata_header.visible = this.show_skeleton; + + item.bind_property("mark", this, "item-mark", BindingFlags.SYNC_CREATE); + this.notify["item-mark"].connect_after(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"); } - 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; + if (last_group_item) { + main_grid.get_style_context().add_class("last-group-item"); } } private void update_edit_mode() { if (item.in_edit_mode) { - this.get_style_context().add_class("edit-mode"); + main_grid.get_style_context().add_class("edit-mode"); } else { - this.get_style_context().remove_class("edit-mode"); + main_grid.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"); + main_grid.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(); - 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) => { - if (this.conversation.account.equals(account) && this.conversation.counterpart.equals(jid)) { - update_name_label(); - } - }); - - conversation.notify["encryption"].connect(update_unencrypted_icon); - item.notify["encryption"].connect(update_encryption_icon); - update_encryption_icon(); - - this.add(received_image); - - if (item.time != null) { - update_time(); + main_grid.get_style_context().remove_class("error"); } - - item.bind_property("mark", this, "item-mark"); - this.notify["item-mark"].connect_after(update_received_mark); - update_received_mark(); } private void update_encryption_icon() { + encryption_image.visible = true; + Application app = GLib.Application.get_default() as Application; ContentMetaItem ci = item as ContentMetaItem; 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) { 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; } } - if (widget == null) { - 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) { - update_unencrypted_icon(); + encryption_image.icon_name = icon_name ?? "dino-changes-prevent-symbolic"; } - } - - private void update_unencrypted_icon() { - if (item.encryption != Encryption.NONE) return; - - 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; + if (item.encryption == Encryption.NONE) { + if (conversation.encryption != Encryption.NONE) { + encryption_image.icon_name = "dino-changes-allowed-symbolic"; + encryption_image.tooltip_text = _("Unencrypted"); + Util.force_error_color(encryption_image); + } else if (conversation.encryption == Encryption.NONE) { + encryption_image.icon_name = null; + encryption_image.visible = false; + } } } @@ -209,7 +160,7 @@ public class ItemMetaDataHeader : Box { time_label.label = get_relative_time(item.time.to_local()).to_string(); 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(); return false; }); @@ -220,41 +171,15 @@ public class ItemMetaDataHeader : Box { } private void update_received_mark() { - bool all_received = true; - bool all_read = true; - bool all_sent = true; - foreach (Plugins.MetaConversationItem item in items) { - if (item.mark == Message.Marked.WONTSEND) { - received_image.visible = true; - received_image.set_from_icon_name("dialog-warning-symbolic", ICON_SIZE_HEADER); - Util.force_error_color(received_image); - Util.force_error_color(time_label); - 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); - + switch (content_meta_item.mark) { + case Message.Marked.RECEIVED: received_image.icon_name = "dino-tick-symbolic"; break; + case Message.Marked.READ: received_image.icon_name = "dino-double-tick-symbolic"; break; + case Message.Marked.WONTSEND: + received_image.icon_name = "dialog-warning-symbolic"; + received_image.icon_name = _("Unable to send message"); + // TODO error color on marked icon and time + break; + default: received_image.icon_name = null; break; } } @@ -311,6 +236,10 @@ public class ItemMetaDataHeader : Box { } } + public Widget get_widget() { + return main_grid; + } + public override void dispose() { if (time_update_timeout != 0) { Source.remove(time_update_timeout); diff --git a/main/src/ui/conversation_content_view/conversation_view.vala b/main/src/ui/conversation_content_view/conversation_view.vala index d6cbed62..8d46281f 100644 --- a/main/src/ui/conversation_content_view/conversation_view.vala +++ b/main/src/ui/conversation_content_view/conversation_view.vala @@ -8,7 +8,7 @@ using Dino.Entities; namespace Dino.Ui.ConversationSummary { [GtkTemplate (ui = "/im/dino/Dino/conversation_content_view/view.ui")] -public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins.NotificationCollection { +public class ConversationView : Widget, Plugins.ConversationItemCollection, Plugins.NotificationCollection { public Conversation? conversation { get; private set; } @@ -19,8 +19,7 @@ public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins [GtkChild] private unowned Image button1_icon; [GtkChild] private unowned Box notifications; [GtkChild] private unowned Box main; - [GtkChild] private unowned EventBox main_event_box; - [GtkChild] private unowned EventBox main_wrap_event_box; + [GtkChild] private unowned Box main_wrap_box; [GtkChild] private unowned Stack stack; private StreamInteractor stream_interactor; @@ -41,9 +40,13 @@ public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins private bool firstLoad = true; private bool at_current_content = true; private bool reload_messages = true; - ConversationItemSkeleton currently_highlighted = null; + Widget currently_highlighted = null; ContentMetaItem? current_meta_item = null; - int last_y_root = -1; + double last_y = -1; + + construct { + this.layout_manager = new BinLayout(); + } public ConversationView init(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; @@ -64,20 +67,21 @@ public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins // we connect to the parent event box that also wraps the overlaying message_menu_box. // This eliminates the unwanted leave events emitted on the main_event_box when hovering // the overlaying menu buttons. - main_wrap_event_box.events = EventMask.ENTER_NOTIFY_MASK; - main_wrap_event_box.events = EventMask.LEAVE_NOTIFY_MASK; - main_wrap_event_box.leave_notify_event.connect(on_leave_notify_event); - main_wrap_event_box.enter_notify_event.connect(on_enter_notify_event); + EventControllerMotion main_wrap_motion_events = new EventControllerMotion(); + main_wrap_box.add_controller(main_wrap_motion_events); + main_wrap_motion_events.leave.connect(on_leave_notify_event); + main_wrap_motion_events.enter.connect(update_highlight); // The buttons of the overlaying message_menu_box may partially overlap the adjacent // conversation items. We connect to the main_event_box directly to avoid emitting // the pointer motion events as long as the pointer is above the message menu. // This ensures that the currently highlighted item remains unchanged when the pointer // reaches the overlapping part of a button. - main_event_box.events = EventMask.POINTER_MOTION_MASK; - main_event_box.motion_notify_event.connect(on_motion_notify_event); + EventControllerMotion main_motion_events = new EventControllerMotion(); + main.add_controller(main_motion_events); + main_motion_events.motion.connect(update_highlight); button1.clicked.connect(() => { - current_meta_item.get_item_actions(Plugins.WidgetType.GTK)[0].callback(button1, current_meta_item, currently_highlighted.widget); + current_meta_item.get_item_actions(Plugins.WidgetType.GTK4)[0].callback(button1, current_meta_item, currently_highlighted); update_message_menu(); }); @@ -102,66 +106,51 @@ public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins } } - private bool on_enter_notify_event(Gdk.EventCrossing event) { - update_highlight((int)event.x_root, (int)event.y_root); - return false; - } - - private bool on_leave_notify_event(Gdk.EventCrossing event) { + private void on_leave_notify_event() { if (currently_highlighted != null) { - currently_highlighted.unset_state_flags(StateFlags.PRELIGHT); + currently_highlighted.get_style_context().remove_class("highlight"); currently_highlighted = null; } message_menu_box.visible = false; - return false; } - private bool on_motion_notify_event(Gdk.EventMotion event) { - update_highlight((int)event.x_root, (int)event.y_root); - return false; - } - - private void update_highlight(int x_root, int y_root) { - if (currently_highlighted != null && (last_y_root - y_root).abs() <= 2) { + private void update_highlight(double x, double y) { + if (currently_highlighted != null && (last_y - y).abs() <= 2) { return; } - last_y_root = y_root; - - int toplevel_window_pos_x, toplevel_window_pos_y, dest_x, dest_y; - Widget toplevel_widget = this.get_toplevel(); - // Obtain the position of the main application window relative to the root window - toplevel_widget.get_window().get_origin(out toplevel_window_pos_x, out toplevel_window_pos_y); - // Get the pointer location relative to the `main` box - toplevel_widget.translate_coordinates(main, x_root - toplevel_window_pos_x, y_root - toplevel_window_pos_y, out dest_x, out dest_y); + last_y = y; // Get widget under pointer int h = 0; - ConversationItemSkeleton? w = null; - foreach (Widget widget in main.get_children()) { - h += widget.get_allocated_height(); - if (h >= dest_y) { - w = widget as ConversationItemSkeleton; + Widget? w = null; + Plugins.MetaConversationItem? meta_item = null; + foreach (Plugins.MetaConversationItem item in meta_items) { + Widget widget = widgets[item]; + h += widget.get_allocated_height() + widget.margin_top + widget.margin_bottom; + if (h >= y) { + w = widget; break; } }; - if (currently_highlighted != null) currently_highlighted.unset_state_flags(StateFlags.PRELIGHT); + if (currently_highlighted != null) currently_highlighted.get_style_context().remove_class("highlight"); + + currently_highlighted = null; + current_meta_item = null; if (w == null) { - currently_highlighted = null; - current_meta_item = null; update_message_menu(); return; } // Get widget coordinates in main - int widget_x, widget_y; + double widget_x, widget_y; w.translate_coordinates(main, 0, 0, out widget_x, out widget_y); // Get MessageItem foreach (Plugins.MetaConversationItem item in item_item_skeletons.keys) { - if (item_item_skeletons[item] == w) { + if (item_item_skeletons[item].get_widget() == w) { current_meta_item = item as ContentMetaItem; } } @@ -170,11 +159,11 @@ public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins if (current_meta_item != null) { // Highlight widget - w.set_state_flags(StateFlags.PRELIGHT, true); currently_highlighted = w; + currently_highlighted.get_style_context().add_class("highlight"); // Move message menu - message_menu_box.margin_top = widget_y - 10; + message_menu_box.margin_top = (int)(widget_y - 10); } } @@ -184,11 +173,11 @@ public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins return; } - var actions = current_meta_item.get_item_actions(Plugins.WidgetType.GTK); + var actions = current_meta_item.get_item_actions(Plugins.WidgetType.GTK4); message_menu_box.visible = actions != null && actions.size > 0; if (actions != null && actions.size == 1) { button1.visible = true; - button1_icon.set_from_icon_name(actions[0].icon_name, IconSize.SMALL_TOOLBAR); + button1_icon.set_from_icon_name(actions[0].icon_name); } } @@ -235,15 +224,14 @@ public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins reload_messages = false; Timeout.add(700, () => { int h = 0, i = 0; - bool @break = false; - main.@foreach((widget) => { - if (widget == w || @break) { - @break = true; - return; + foreach (Plugins.MetaConversationItem item in meta_items) { + Widget widget = widgets[item]; + if (widget == w) { + break; } h += widget.get_allocated_height(); i++; - }); + } scrolled.vadjustment.value = h - scrolled.vadjustment.page_size * 1/3; w.get_style_context().add_class("highlight-once"); reload_messages = true; @@ -270,9 +258,9 @@ public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins // Init for new conversation foreach (Plugins.ConversationItemPopulator populator in app.plugin_registry.conversation_addition_populators) { - populator.init(conversation, this, Plugins.WidgetType.GTK); + populator.init(conversation, this, Plugins.WidgetType.GTK4); } - content_populator.init(this, conversation, Plugins.WidgetType.GTK); + content_populator.init(this, conversation, Plugins.WidgetType.GTK4); subscription_notification.init(conversation, this); animate = false; @@ -286,7 +274,7 @@ public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins } Application app = GLib.Application.get_default() as Application; foreach (Plugins.NotificationPopulator populator in app.plugin_registry.notification_populators) { - populator.init(conversation, this, Plugins.WidgetType.GTK); + populator.init(conversation, this, Plugins.WidgetType.GTK4); } Idle.add(() => { on_value_notify(); return false; }); } @@ -318,7 +306,7 @@ public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins private void remove_item(Plugins.MetaConversationItem item) { ConversationItemSkeleton? skeleton = item_item_skeletons[item]; if (skeleton != null) { - main.remove(skeleton); + main.remove(skeleton.get_widget()); widgets.unset(item); item_skeletons.remove(skeleton); item_item_skeletons.unset(item); @@ -331,21 +319,21 @@ public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins } public void on_add_meta_notification(Plugins.MetaConversationNotification notification) { - Widget? widget = (Widget) notification.get_widget(Plugins.WidgetType.GTK); + Widget? widget = (Widget) notification.get_widget(Plugins.WidgetType.GTK4); if (widget != null) { add_notification(widget); } } public void on_remove_meta_notification(Plugins.MetaConversationNotification notification){ - Widget? widget = (Widget) notification.get_widget(Plugins.WidgetType.GTK); + Widget? widget = (Widget) notification.get_widget(Plugins.WidgetType.GTK4); if (widget != null) { remove_notification(widget); } } public void add_notification(Widget widget) { - notifications.add(widget); + notifications.append(widget); Timeout.add(20, () => { notification_revealer.transition_duration = 200; notification_revealer.reveal_child = true; @@ -362,15 +350,14 @@ public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins Plugins.MetaConversationItem? lower_item = meta_items.lower(item); // Fill datastructure - ConversationItemSkeleton item_skeleton = new ConversationItemSkeleton(stream_interactor, conversation, item, !animate) { visible=true }; + ConversationItemSkeleton item_skeleton = new ConversationItemSkeleton(stream_interactor, conversation, item, !animate); item_item_skeletons[item] = item_skeleton; int index = lower_item != null ? item_skeletons.index_of(item_item_skeletons[lower_item]) + 1 : 0; item_skeletons.insert(index, item_skeleton); // Insert widget - widgets[item] = item_skeleton; - main.add(item_skeleton); - main.reorder_child(item_skeleton, index); + widgets[item] = item_skeleton.get_widget(); + widgets[item].insert_after(main, item_item_skeletons.has_key(lower_item) ? item_item_skeletons[lower_item].get_widget() : null); if (lower_item != null) { if (can_merge(item, lower_item)) { @@ -405,7 +392,7 @@ public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins } } } - return item_skeleton; + return item_skeleton.get_widget(); } private bool can_merge(Plugins.MetaConversationItem upper_item /*more recent, displayed below*/, Plugins.MetaConversationItem lower_item /*less recent, displayed above*/) { @@ -420,7 +407,11 @@ public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins private void on_upper_notify() { if (was_upper == null || scrolled.vadjustment.value > was_upper - was_page_size - 1) { // scrolled down or content smaller than page size if (at_current_content) { - scrolled.vadjustment.value = scrolled.vadjustment.upper - scrolled.vadjustment.page_size; // scroll down + Idle.add(() => { + // If we do this directly without Idle.add, scrolling down doesn't work properly + scrolled.vadjustment.value = scrolled.vadjustment.upper - scrolled.vadjustment.page_size; // scroll down + return false; + }); } } else if (scrolled.vadjustment.value < scrolled.vadjustment.upper - scrolled.vadjustment.page_size - 1) { scrolled.vadjustment.value = scrolled.vadjustment.upper - was_upper + scrolled.vadjustment.value; // stay at same content @@ -482,12 +473,14 @@ public class ConversationView : Box, Plugins.ConversationItemCollection, Plugins meta_items.clear(); item_skeletons.clear(); item_item_skeletons.clear(); + foreach (Widget widget in widgets.values) { + main.remove(widget); + } widgets.clear(); - main.@foreach((widget) => { main.remove(widget); }); } private void clear_notifications() { - notifications.@foreach((widget) => { notifications.remove(widget); }); +// notifications.@foreach((widget) => { notifications.remove(widget); }); notification_revealer.transition_duration = 0; notification_revealer.set_reveal_child(false); } diff --git a/main/src/ui/conversation_content_view/date_separator_populator.vala b/main/src/ui/conversation_content_view/date_separator_populator.vala index 5f6838e5..40bf0693 100644 --- a/main/src/ui/conversation_content_view/date_separator_populator.vala +++ b/main/src/ui/conversation_content_view/date_separator_populator.vala @@ -61,7 +61,7 @@ public class MetaDateItem : Plugins.MetaConversationItem { this.time = date; } - public override Object? get_widget(Plugins.WidgetType widget_type) { + public override Object? get_widget(Plugins.ConversationItemWidgetInterface outer, Plugins.WidgetType widget_type) { return new DateSeparatorWidget(date); } @@ -86,9 +86,9 @@ public class DateSeparatorWidget : Box { label = new Label("") { use_markup=true, halign=Align.CENTER, hexpand=false, visible=true }; label.get_style_context().add_class("dim-label"); - this.add(new Separator(Orientation.HORIZONTAL) { valign=Align.CENTER, hexpand=true, visible=true }); - this.add(label); - this.add(new Separator(Orientation.HORIZONTAL) { valign=Align.CENTER, hexpand=true, visible=true }); + this.append(new Separator(Orientation.HORIZONTAL) { valign=Align.CENTER, hexpand=true, visible=true }); + this.append(label); + this.append(new Separator(Orientation.HORIZONTAL) { valign=Align.CENTER, hexpand=true, visible=true }); update_time(); } diff --git a/main/src/ui/conversation_content_view/file_default_widget.vala b/main/src/ui/conversation_content_view/file_default_widget.vala index 638dab15..3bd5842f 100644 --- a/main/src/ui/conversation_content_view/file_default_widget.vala +++ b/main/src/ui/conversation_content_view/file_default_widget.vala @@ -7,34 +7,49 @@ using Dino.Entities; namespace Dino.Ui { [GtkTemplate (ui = "/im/dino/Dino/file_default_widget.ui")] -public class FileDefaultWidget : EventBox { +public class FileDefaultWidget : Box { + + public signal void open_file(); + public signal void save_file_as(); + public signal void cancel_download(); [GtkChild] public unowned Stack image_stack; [GtkChild] public unowned Label name_label; [GtkChild] public unowned Label mime_label; [GtkChild] public unowned Image content_type_image; [GtkChild] public unowned Spinner spinner; - [GtkChild] public unowned EventBox stack_event_box; [GtkChild] public unowned MenuButton file_menu; - public ModelButton file_open_button; - public ModelButton file_save_button; - public ModelButton cancel_button; - private FileTransfer.State state; + class construct { + install_action("file.open", null, (widget, action_name) => { ((FileDefaultWidget) widget).open_file(); }); + install_action("file.save_as", null, (widget, action_name) => { ((FileDefaultWidget) widget).save_file_as(); }); + install_action("file.cancel", null, (widget, action_name) => { ((FileDefaultWidget) widget).cancel_download(); }); + } + public FileDefaultWidget() { - this.enter_notify_event.connect(on_pointer_entered_event); - this.leave_notify_event.connect(on_pointer_left_event); - file_open_button = new ModelButton() { text=_("Open"), visible=true }; - file_save_button = new ModelButton() { text=_("Save as…"), visible=true }; - cancel_button = new ModelButton() { text=_("Cancel"), visible=true }; + EventControllerMotion this_motion_events = new EventControllerMotion(); + this.add_controller(this_motion_events); + this_motion_events.enter.connect(on_pointer_entered_event); + this_motion_events.leave.connect(on_pointer_left_event); + + GestureClick gesture_click_controller = new GestureClick(); + this.add_controller(gesture_click_controller); + gesture_click_controller.pressed.connect((n_press, x, y) => { + // Check whether the click was inside the file menu. Otherwise, open the file. + double x_button, y_button; + this.translate_coordinates(file_menu, x, y, out x_button, out y_button); + if (file_menu.contains(x_button, y_button)) return; + + this.open_file(); + }); } public void update_file_info(string? mime_type, FileTransfer.State state, long size) { this.state = state; - spinner.active = false; // A hidden spinning spinner still uses CPU. Deactivate asap + spinner.stop(); // A hidden spinning spinner still uses CPU. Deactivate asap content_type_image.icon_name = get_file_icon_name(mime_type); string? mime_description = mime_type != null ? ContentType.get_description(mime_type) : null; @@ -45,33 +60,23 @@ public class FileDefaultWidget : EventBox { image_stack.set_visible_child_name("content_type_image"); // Create a menu - Gtk.PopoverMenu popover_menu = new Gtk.PopoverMenu(); - Box file_menu_box = new Box(Orientation.VERTICAL, 0) { margin=10, visible=true }; - file_menu_box.add(file_open_button); - file_menu_box.add(file_save_button); - popover_menu.add(file_menu_box); + Menu menu_model = new Menu(); + menu_model.append(_("Open"), "file.open"); + menu_model.append(_("Save as…"), "file.save_as"); + Gtk.PopoverMenu popover_menu = new Gtk.PopoverMenu.from_model(menu_model); file_menu.popover = popover_menu; - file_menu.button_release_event.connect(() => { - popover_menu.visible = true; - return true; - }); popover_menu.closed.connect(on_pointer_left); break; case FileTransfer.State.IN_PROGRESS: mime_label.label = _("Downloading %s…").printf(get_size_string(size)); - spinner.active = true; + spinner.start(); image_stack.set_visible_child_name("spinner"); // Create a menu - Gtk.PopoverMenu popover_menu = new Gtk.PopoverMenu(); - Box file_menu_box = new Box(Orientation.VERTICAL, 0) { margin=10, visible=true }; - file_menu_box.add(cancel_button); - popover_menu.add(file_menu_box); + Menu menu_model = new Menu(); + menu_model.append(_("Cancel"), "file.cancel_download"); + Gtk.PopoverMenu popover_menu = new Gtk.PopoverMenu.from_model(menu_model); file_menu.popover = popover_menu; - file_menu.button_release_event.connect(() => { - popover_menu.visible = true; - return true; - }); popover_menu.closed.connect(on_pointer_left); break; case FileTransfer.State.NOT_STARTED: @@ -92,8 +97,8 @@ public class FileDefaultWidget : EventBox { } } - private bool on_pointer_entered_event(Gdk.EventCrossing event) { - event.get_window().set_cursor(new Cursor.for_display(Gdk.Display.get_default(), CursorType.HAND2)); + private void on_pointer_entered_event() { + this.set_cursor_from_name("pointer"); content_type_image.opacity = 0.7; if (state == FileTransfer.State.NOT_STARTED) { image_stack.set_visible_child_name("download_image"); @@ -101,16 +106,13 @@ public class FileDefaultWidget : EventBox { if (state == FileTransfer.State.COMPLETE || state == FileTransfer.State.IN_PROGRESS) { file_menu.opacity = 1; } - return false; } - private bool on_pointer_left_event(Gdk.EventCrossing event) { - if (event.detail == Gdk.NotifyType.INFERIOR) return false; - if (file_menu.popover != null && file_menu.popover.visible) return false; + private void on_pointer_left_event() { + if (file_menu.popover != null && file_menu.popover.visible) return; - event.get_window().set_cursor(new Cursor.for_display(Gdk.Display.get_default(), CursorType.XTERM)); + this.set_cursor(null); on_pointer_left(); - return false; } private void on_pointer_left() { diff --git a/main/src/ui/conversation_content_view/file_image_widget.vala b/main/src/ui/conversation_content_view/file_image_widget.vala index 91eddd93..aad220e8 100644 --- a/main/src/ui/conversation_content_view/file_image_widget.vala +++ b/main/src/ui/conversation_content_view/file_image_widget.vala @@ -6,7 +6,7 @@ using Dino.Entities; namespace Dino.Ui { -public class FileImageWidget : EventBox { +public class FileImageWidget : Box { private ScalingImage image; FileDefaultWidget file_default_widget; @@ -14,7 +14,6 @@ public class FileImageWidget : EventBox { public FileImageWidget() { this.halign = Align.START; - this.events = EventMask.POINTER_MOTION_MASK; this.get_style_context().add_class("file-image-widget"); } @@ -36,6 +35,7 @@ public class FileImageWidget : EventBox { pixbuf = pixbuf.apply_embedded_orientation(); image.load(pixbuf); + Picture picture = new Picture.for_pixbuf(pixbuf) { can_shrink=true, keep_aspect_ratio=true, halign=Align.START }; Idle.add(load_from_file.callback); return image; @@ -47,28 +47,29 @@ public class FileImageWidget : EventBox { FileInfo file_info = file.query_info("*", FileQueryInfoFlags.NONE); string? mime_type = file_info.get_content_type(); - file_default_widget = new FileDefaultWidget() { valign=Align.END, vexpand=false }; - file_default_widget.stack_event_box.visible = false; + file_default_widget = new FileDefaultWidget() { valign=Align.END, vexpand=false, visible=false }; + file_default_widget.image_stack.visible = false; file_default_widget_controller = new FileDefaultWidgetController(file_default_widget); file_default_widget_controller.set_file(file, file_name, mime_type); Overlay overlay = new Overlay() { visible=true }; - overlay.add(image); + overlay.set_child(image); overlay.add_overlay(file_default_widget); + overlay.set_measure_overlay(image, true); + overlay.set_clip_overlay(file_default_widget, true); - this.enter_notify_event.connect((event) => { + EventControllerMotion this_motion_events = new EventControllerMotion(); + this.add_controller(this_motion_events); + this_motion_events.enter.connect(() => { file_default_widget.visible = true; - return false; }); - this.leave_notify_event.connect((event) => { - if (event.detail == Gdk.NotifyType.INFERIOR) return false; - if (file_default_widget.file_menu.popover != null && file_default_widget.file_menu.popover.visible) return false; + this_motion_events.leave.connect(() => { + if (file_default_widget.file_menu.popover != null && file_default_widget.file_menu.popover.visible) return; file_default_widget.visible = false; - return false; }); - this.add(overlay); + this.append(overlay); } } diff --git a/main/src/ui/conversation_content_view/file_widget.vala b/main/src/ui/conversation_content_view/file_widget.vala index b63195dc..6378c298 100644 --- a/main/src/ui/conversation_content_view/file_widget.vala +++ b/main/src/ui/conversation_content_view/file_widget.vala @@ -16,7 +16,7 @@ public class FileMetaItem : ConversationSummary.ContentMetaItem { this.stream_interactor = stream_interactor; } - public override Object? get_widget(Plugins.WidgetType type) { + public override Object? get_widget(Plugins.ConversationItemWidgetInterface outer, Plugins.WidgetType type) { FileItem file_item = content_item as FileItem; FileTransfer transfer = file_item.file_transfer; return new FileWidget(stream_interactor, transfer) { visible=true }; @@ -51,11 +51,11 @@ public class FileWidget : SizeRequestBox { this.file_transfer = file_transfer; update_widget.begin(); - size_allocate.connect((allocation) => { - if (allocation.height > parent.get_allocated_height()) { - Idle.add(() => { parent.queue_resize(); return false; }); - } - }); +// size_allocate.connect((allocation) => { +// if (allocation.height > parent.get_allocated_height()) { +// Idle.add(() => { parent.queue_resize(); return false; }); +// } +// }); file_transfer.bind_property("state", this, "file-transfer-state"); file_transfer.bind_property("mime-type", this, "file-transfer-mime-type"); @@ -79,7 +79,7 @@ public class FileWidget : SizeRequestBox { if (content != null) this.remove(content); content = file_image_widget; state = State.IMAGE; - this.add(content); + this.append(content); return; } catch (Error e) { } } @@ -91,7 +91,7 @@ public class FileWidget : SizeRequestBox { default_widget_controller.set_file_transfer(file_transfer, stream_interactor); content = default_file_widget; this.state = State.DEFAULT; - this.add(content); + this.append(content); } } @@ -128,10 +128,15 @@ public class FileDefaultWidgetController : Object { public FileDefaultWidgetController(FileDefaultWidget widget) { this.widget = widget; - widget.button_release_event.connect(on_clicked); - widget.file_open_button.clicked.connect(open_file); - widget.file_save_button.clicked.connect(save_file); - widget.cancel_button.clicked.connect(cancel_download); + + widget.open_file.connect(open_file); + widget.save_file_as.connect(save_file); + widget.cancel_download.connect(cancel_download); + + var gesture_controller = new GestureClick(); + gesture_controller.set_button(1); // listen for left clicks + gesture_controller.released.connect(on_clicked); + widget.add_controller(gesture_controller); } public void set_file_transfer(FileTransfer file_transfer, StreamInteractor stream_interactor) { @@ -173,30 +178,29 @@ public class FileDefaultWidgetController : Object { } private void save_file() { - var save_dialog = new FileChooserNative(_("Save as…"), widget.get_toplevel() as Gtk.Window, FileChooserAction.SAVE, null, null); - save_dialog.set_do_overwrite_confirmation(true); + var save_dialog = new FileChooserNative(_("Save as…"), widget.get_root() as Gtk.Window, FileChooserAction.SAVE, null, null); save_dialog.set_modal(true); save_dialog.set_current_name(file_name); - if (save_dialog.run() == Gtk.ResponseType.ACCEPT) { + save_dialog.response.connect(() => { try{ GLib.File.new_for_uri(file_uri).copy(save_dialog.get_file(), GLib.FileCopyFlags.OVERWRITE, null); } catch (Error err) { warning("Failed copy file %s - %s", file_uri, err.message); } - } + }); + + save_dialog.show(); } private void cancel_download() { file_transfer.cancellable.cancel(); } - private bool on_clicked(EventButton event_button) { + private void on_clicked() { switch (state) { case FileTransfer.State.COMPLETE: - if (event_button.button == 1) { - open_file(); - } + open_file(); break; case FileTransfer.State.NOT_STARTED: assert(stream_interactor != null && file_transfer != null); @@ -206,7 +210,6 @@ public class FileDefaultWidgetController : Object { // Clicking doesn't do anything in FAILED and IN_PROGRESS states break; } - return false; } } diff --git a/main/src/ui/conversation_content_view/message_item_widget.vala b/main/src/ui/conversation_content_view/message_item_widget.vala new file mode 100644 index 00000000..23a499d9 --- /dev/null +++ b/main/src/ui/conversation_content_view/message_item_widget.vala @@ -0,0 +1,229 @@ +using Dino.Entities; +using Gtk; + +namespace Dino.Ui { + public class MessageItemWidget : SizeRequestBin { + + public signal void edit_cancelled(); + public signal void edit_sent(string text); + + enum AdditionalInfo { + NONE, + PENDING, + DELIVERY_FAILED + } + + StreamInteractor stream_interactor; + public ContentItem content_item; + public Message.Marked marked { get; set; } + + Label label = new Label("") { use_markup=true, xalign=0, selectable=true, wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR, vexpand=true, visible=true }; + MessageItemEditMode? edit_mode = null; + ChatTextViewController? controller = null; + AdditionalInfo additional_info = AdditionalInfo.NONE; + + ulong realize_id = -1; + ulong style_updated_id = -1; + ulong marked_notify_handler_id = -1; + + construct { + this.append(label); + label.activate_link.connect(on_label_activate_link); + this.size_request_mode = SizeRequestMode.HEIGHT_FOR_WIDTH; + } + + public MessageItemWidget(StreamInteractor stream_interactor, ContentItem content_item) { + this.stream_interactor = stream_interactor; + this.content_item = content_item; + + Message message = ((MessageItem) content_item).message; + if (message.direction == Message.DIRECTION_SENT && !(message.marked in Message.MARKED_RECEIVED)) { + var binding = message.bind_property("marked", this, "marked"); + marked_notify_handler_id = this.notify["marked"].connect(() => { + // Currently "pending", but not anymore + if (additional_info == AdditionalInfo.PENDING && + message.marked != Message.Marked.SENDING && message.marked != Message.Marked.UNSENT) { + update_label(); + } + + // Currently "error", but not anymore + if (additional_info == AdditionalInfo.DELIVERY_FAILED && message.marked != Message.Marked.ERROR) { + update_label(); + } + + // Currently not error, but should be + if (additional_info != AdditionalInfo.DELIVERY_FAILED && message.marked == Message.Marked.ERROR) { + update_label(); + } + + // Nothing bad can happen anymore + if (message.marked in Message.MARKED_RECEIVED) { + binding.unbind(); + this.disconnect(marked_notify_handler_id); + } + }); + } + + update_label(); + } + + public void set_edit_mode() { + + MessageItem message_item = content_item as MessageItem; + Message message = message_item.message; + + if (edit_mode == null) { + edit_mode = new MessageItemEditMode(); + controller = new ChatTextViewController(edit_mode.chat_text_view, stream_interactor); + Conversation conversation = message_item.conversation; + controller.initialize_for_conversation(conversation); + + edit_mode.cancelled.connect(() => { + edit_cancelled(); + unset_edit_mode(); + }); + edit_mode.send.connect(() => { + if (((MessageItem) content_item).message.body != edit_mode.chat_text_view.text_view.buffer.text) { + edit_sent(edit_mode.chat_text_view.text_view.buffer.text); + } else { + edit_cancelled(); + } + unset_edit_mode(); + }); + } + + edit_mode.chat_text_view.text_view.buffer.text = message.body; + + this.remove(label); + this.append(edit_mode); + + edit_mode.chat_text_view.text_view.grab_focus(); + } + + public void unset_edit_mode() { + this.remove(edit_mode); + this.append(label); + label.grab_focus(); + label.selectable = false; + label.selectable = true; + } + + public void update_label() { + label.label = generate_markup_text(content_item); + } + + private string generate_markup_text(ContentItem item) { + MessageItem message_item = item as MessageItem; + Conversation conversation = message_item.conversation; + Message message = message_item.message; + + bool theme_dependent = false; + + string markup_text = message.body; + if (markup_text.length > 10000) { + markup_text = markup_text.substring(0, 10000) + " [" + _("Message too long") + "]"; + } + if (message.body.has_prefix("/me ")) { + markup_text = markup_text.substring(4); + } + + if (conversation.type_ == Conversation.Type.GROUPCHAT) { + markup_text = Util.parse_add_markup_theme(markup_text, conversation.nickname, true, true, true, Util.is_dark_theme(this), ref theme_dependent); + } else { + markup_text = Util.parse_add_markup_theme(markup_text, null, true, true, true, Util.is_dark_theme(this), ref theme_dependent); + } + + if (message.body.has_prefix("/me ")) { + string display_name = Util.get_participant_display_name(stream_interactor, conversation, message.from); + markup_text = @"<i><b>$(Markup.escape_text(display_name))</b> " + markup_text + "</i>"; + } + + int only_emoji_count = Util.get_only_emoji_count(markup_text); + if (only_emoji_count != -1) { + string size_str = only_emoji_count < 5 ? "xx-large" : "large"; + markup_text = @"<span size=\'$size_str\'>" + markup_text + "</span>"; + } + + string dim_color = Util.is_dark_theme(this) ? "#BDBDBD" : "#707070"; + + if (message.edit_to != null) { + markup_text += @" <span size='small' color='$dim_color'>(%s)</span>".printf(_("edited")); + theme_dependent = true; + } + + // Append message status info + additional_info = AdditionalInfo.NONE; + if (message.direction == Message.DIRECTION_SENT && (message.marked == Message.Marked.SENDING || message.marked == Message.Marked.UNSENT)) { + // Append "pending..." iff message has not been sent yet + if (message.time.compare(new DateTime.now_utc().add_seconds(-10)) < 0) { + markup_text += @" <span size='small' color='$dim_color'>%s</span>".printf(_("pending…")); + theme_dependent = true; + additional_info = AdditionalInfo.PENDING; + } else { + int time_diff = (- (int) message.time.difference(new DateTime.now_utc()) / 1000); + Timeout.add(10000 - time_diff, () => { + update_label(); + return false; + }); + } + } else if (message.direction == Message.DIRECTION_SENT && message.marked == Message.Marked.ERROR) { + // Append "delivery failed" if there was a server error + string error_color = Util.rgba_to_hex(Util.get_label_pango_color(label, "@error_color")); + markup_text += " <span size='small' color='%s'>%s</span>".printf(error_color, _("delivery failed")); + theme_dependent = true; + additional_info = AdditionalInfo.DELIVERY_FAILED; + } + + if (theme_dependent && realize_id == -1) { + realize_id = label.realize.connect(update_label); + // style_updated_id = label.style_updated.connect(update_label); + } else if (!theme_dependent && realize_id != -1) { + label.disconnect(realize_id); + label.disconnect(style_updated_id); + } + return markup_text; + } + + public static bool on_label_activate_link(string uri) { + // Always handle xmpp URIs with Dino + if (!uri.has_prefix("xmpp:")) return false; + File file = File.new_for_uri(uri); + Dino.Application.get_default().open(new File[]{file}, ""); + return true; + } + } + + [GtkTemplate (ui = "/im/dino/Dino/message_item_widget_edit_mode.ui")] + public class MessageItemEditMode : Box { + + public signal void cancelled(); + public signal void send(); + + [GtkChild] public unowned MenuButton emoji_button; + [GtkChild] public unowned ChatTextView chat_text_view; + [GtkChild] public unowned Button cancel_button; + [GtkChild] public unowned Button send_button; + [GtkChild] public unowned Frame frame; + + construct { + Util.force_css(frame, "* { border-radius: 3px; }"); + + 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); + + chat_text_view.text_view.buffer.changed.connect_after(on_text_view_changed); + + cancel_button.clicked.connect(() => cancelled()); + send_button.clicked.connect(() => send()); + chat_text_view.cancel_input.connect(() => cancelled()); + chat_text_view.send_text.connect(() => send()); + } + + private void on_text_view_changed() { + send_button.sensitive = chat_text_view.text_view.buffer.text != ""; + } + } +}
\ No newline at end of file diff --git a/main/src/ui/conversation_content_view/message_widget.vala b/main/src/ui/conversation_content_view/message_widget.vala index e7bd1282..3ebce0ee 100644 --- a/main/src/ui/conversation_content_view/message_widget.vala +++ b/main/src/ui/conversation_content_view/message_widget.vala @@ -10,84 +10,16 @@ namespace Dino.Ui.ConversationSummary { public class MessageMetaItem : ContentMetaItem { - private StreamInteractor stream_interactor; - private MessageItemWidget message_item_widget; - private MessageItem message_item; - - public MessageMetaItem(ContentItem content_item, StreamInteractor stream_interactor) { - base(content_item); - message_item = content_item as MessageItem; - this.stream_interactor = stream_interactor; - } - - public override Object? get_widget(Plugins.WidgetType type) { - message_item_widget = new MessageItemWidget(stream_interactor, content_item) { visible=true }; - - message_item_widget.edit_cancelled.connect(() => { this.in_edit_mode = false; }); - message_item_widget.edit_sent.connect(on_edit_send); - - stream_interactor.get_module(MessageCorrection.IDENTITY).received_correction.connect(on_received_correction); - - this.notify["in-edit-mode"].connect(() => { - if (in_edit_mode == false) return; - bool allowed = stream_interactor.get_module(MessageCorrection.IDENTITY).is_own_correction_allowed(message_item.conversation, message_item.message); - if (allowed) { - message_item_widget.set_edit_mode(); - } else { - this.in_edit_mode = false; - } - }); - - return message_item_widget; - } - - public override Gee.List<Plugins.MessageAction>? get_item_actions(Plugins.WidgetType type) { - if (content_item as FileItem != null) return null; - - bool allowed = stream_interactor.get_module(MessageCorrection.IDENTITY).is_own_correction_allowed(message_item.conversation, message_item.message); - Gee.List<Plugins.MessageAction> actions = new ArrayList<Plugins.MessageAction>(); - if (allowed && !in_edit_mode) { - Plugins.MessageAction action1 = new Plugins.MessageAction(); - action1.icon_name = "document-edit-symbolic"; - action1.callback = (button, content_meta_item_activated, widget) => { - this.in_edit_mode = true; - }; - actions.add(action1); - } - return actions; - } - - private void on_edit_send(string text) { - stream_interactor.get_module(MessageCorrection.IDENTITY).send_correction(message_item.conversation, message_item.message, text); - this.in_edit_mode = false; - } - - private void on_received_correction(ContentItem content_item) { - if (this.content_item.id == content_item.id) { - this.content_item = content_item; - message_item = content_item as MessageItem; - message_item_widget.content_item = content_item; - message_item_widget.update_label(); - } - } -} - -public class MessageItemWidget : SizeRequestBin { - - public signal void edit_cancelled(); - public signal void edit_sent(string text); - enum AdditionalInfo { NONE, PENDING, DELIVERY_FAILED } - StreamInteractor stream_interactor; - public ContentItem content_item; + private StreamInteractor stream_interactor; + private MessageItem message_item; public Message.Marked marked { get; set; } - Label label = new Label("") { use_markup=true, xalign=0, selectable=true, wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR, vexpand=true, visible=true }; MessageItemEditMode? edit_mode = null; ChatTextViewController? controller = null; AdditionalInfo additional_info = AdditionalInfo.NONE; @@ -96,15 +28,14 @@ public class MessageItemWidget : SizeRequestBin { ulong style_updated_id = -1; ulong marked_notify_handler_id = -1; - construct { - this.add(label); - label.activate_link.connect(on_label_activate_link); - this.size_request_mode = SizeRequestMode.HEIGHT_FOR_WIDTH; - } + public Label label = new Label("") { use_markup=true, xalign=0, selectable=true, wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR, hexpand=true, vexpand=true, can_focus=false }; - public MessageItemWidget(StreamInteractor stream_interactor, ContentItem content_item) { + public MessageMetaItem(ContentItem content_item, StreamInteractor stream_interactor) { + base(content_item); + message_item = content_item as MessageItem; this.stream_interactor = stream_interactor; - this.content_item = content_item; + + label.activate_link.connect(on_label_activate_link); Message message = ((MessageItem) content_item).message; if (message.direction == Message.DIRECTION_SENT && !(message.marked in Message.MARKED_RECEIVED)) { @@ -137,51 +68,6 @@ public class MessageItemWidget : SizeRequestBin { update_label(); } - public void set_edit_mode() { - - MessageItem message_item = content_item as MessageItem; - Message message = message_item.message; - - if (edit_mode == null) { - edit_mode = new MessageItemEditMode(); - controller = new ChatTextViewController(edit_mode.chat_text_view, stream_interactor); - Conversation conversation = message_item.conversation; - controller.initialize_for_conversation(conversation); - - edit_mode.cancelled.connect(() => { - edit_cancelled(); - unset_edit_mode(); - }); - edit_mode.send.connect(() => { - if (((MessageItem) content_item).message.body != edit_mode.chat_text_view.text_view.buffer.text) { - edit_sent(edit_mode.chat_text_view.text_view.buffer.text); - } else { - edit_cancelled(); - } - unset_edit_mode(); - }); - } - - edit_mode.chat_text_view.text_view.buffer.text = message.body; - - this.remove(label); - this.add(edit_mode); - - edit_mode.chat_text_view.text_view.grab_focus(); - } - - public void unset_edit_mode() { - this.remove(edit_mode); - this.add(label); - label.grab_focus(); - label.selectable = false; - label.selectable = true; - } - - public void update_label() { - label.label = generate_markup_text(content_item); - } - private string generate_markup_text(ContentItem item) { MessageItem message_item = item as MessageItem; Conversation conversation = message_item.conversation; @@ -198,9 +84,9 @@ public class MessageItemWidget : SizeRequestBin { } if (conversation.type_ == Conversation.Type.GROUPCHAT) { - markup_text = Util.parse_add_markup_theme(markup_text, conversation.nickname, true, true, true, Util.is_dark_theme(this), ref theme_dependent); + markup_text = Util.parse_add_markup_theme(markup_text, conversation.nickname, true, true, true, Util.is_dark_theme(this.label), ref theme_dependent); } else { - markup_text = Util.parse_add_markup_theme(markup_text, null, true, true, true, Util.is_dark_theme(this), ref theme_dependent); + markup_text = Util.parse_add_markup_theme(markup_text, null, true, true, true, Util.is_dark_theme(this.label), ref theme_dependent); } if (message.body.has_prefix("/me ")) { @@ -214,7 +100,7 @@ public class MessageItemWidget : SizeRequestBin { markup_text = @"<span size=\'$size_str\'>" + markup_text + "</span>"; } - string dim_color = Util.is_dark_theme(this) ? "#BDBDBD" : "#707070"; + string dim_color = Util.is_dark_theme(this.label) ? "#BDBDBD" : "#707070"; if (message.edit_to != null) { markup_text += @" <span size='small' color='$dim_color'>(%s)</span>".printf(_("edited")); @@ -246,7 +132,7 @@ public class MessageItemWidget : SizeRequestBin { if (theme_dependent && realize_id == -1) { realize_id = label.realize.connect(update_label); - style_updated_id = label.style_updated.connect(update_label); +// style_updated_id = label.style_updated.connect(update_label); } else if (!theme_dependent && realize_id != -1) { label.disconnect(realize_id); label.disconnect(style_updated_id); @@ -254,6 +140,83 @@ public class MessageItemWidget : SizeRequestBin { return markup_text; } + public void update_label() { + label.label = generate_markup_text(content_item); + } + + public override Object? get_widget(Plugins.ConversationItemWidgetInterface outer, Plugins.WidgetType type) { + + stream_interactor.get_module(MessageCorrection.IDENTITY).received_correction.connect(on_received_correction); + + this.notify["in-edit-mode"].connect(() => { + if (in_edit_mode == false) return; + bool allowed = stream_interactor.get_module(MessageCorrection.IDENTITY).is_own_correction_allowed(message_item.conversation, message_item.message); + if (allowed) { + MessageItem message_item = content_item as MessageItem; + Message message = message_item.message; + + edit_mode = new MessageItemEditMode(); + controller = new ChatTextViewController(edit_mode.chat_text_view, stream_interactor); + Conversation conversation = message_item.conversation; + controller.initialize_for_conversation(conversation); + + edit_mode.cancelled.connect(() => { + in_edit_mode = false; + outer.set_widget(label, Plugins.WidgetType.GTK4); + label.grab_focus(); + }); + edit_mode.send.connect(() => { + if (((MessageItem) content_item).message.body != edit_mode.chat_text_view.text_view.buffer.text) { + on_edit_send(edit_mode.chat_text_view.text_view.buffer.text); + } else { +// edit_cancelled(); + } + in_edit_mode = false; + outer.set_widget(label, Plugins.WidgetType.GTK4); + label.grab_focus(); + }); + + edit_mode.chat_text_view.text_view.buffer.text = message.body; + + outer.set_widget(edit_mode, Plugins.WidgetType.GTK4); + edit_mode.chat_text_view.text_view.grab_focus(); + } else { + this.in_edit_mode = false; + } + }); + + return label; + } + + public override Gee.List<Plugins.MessageAction>? get_item_actions(Plugins.WidgetType type) { + if (content_item as FileItem != null) return null; + + bool allowed = stream_interactor.get_module(MessageCorrection.IDENTITY).is_own_correction_allowed(message_item.conversation, message_item.message); + Gee.List<Plugins.MessageAction> actions = new ArrayList<Plugins.MessageAction>(); + if (allowed && !in_edit_mode) { + Plugins.MessageAction action1 = new Plugins.MessageAction(); + action1.icon_name = "document-edit-symbolic"; + action1.callback = (button, content_meta_item_activated, widget) => { + this.in_edit_mode = true; + }; + actions.add(action1); + } + return actions; + } + + private void on_edit_send(string text) { + stream_interactor.get_module(MessageCorrection.IDENTITY).send_correction(message_item.conversation, message_item.message, text); + this.in_edit_mode = false; + } + + private void on_received_correction(ContentItem content_item) { + if (this.content_item.id == content_item.id) { + this.content_item = content_item; + message_item = content_item as MessageItem; + update_label(); + } + } + public static bool on_label_activate_link(string uri) { // Always handle xmpp URIs with Dino if (!uri.has_prefix("xmpp:")) return false; @@ -276,7 +239,7 @@ public class MessageItemEditMode : Box { [GtkChild] public unowned Frame frame; construct { - Util.force_css(frame, "* { border-radius: 3px; }"); + Util.force_css(frame, "* { border-radius: 3px; padding: 5px 7px; }"); EmojiChooser chooser = new EmojiChooser(); chooser.emoji_picked.connect((emoji) => { diff --git a/main/src/ui/conversation_content_view/subscription_notification.vala b/main/src/ui/conversation_content_view/subscription_notification.vala index d493ff78..1f0f39d8 100644 --- a/main/src/ui/conversation_content_view/subscription_notification.vala +++ b/main/src/ui/conversation_content_view/subscription_notification.vala @@ -34,8 +34,8 @@ public class SubscriptionNotitication : Object { private void show_notification() { Box box = new Box(Orientation.HORIZONTAL, 5) { visible=true }; - Button accept_button = new Button() { label=_("Accept"), visible=true }; - Button deny_button = new Button() { label=_("Deny"), visible=true }; + Button accept_button = new Button.with_label(_("Accept")) { visible=true }; + Button deny_button = new Button.with_label(_("Deny")) { visible=true }; GLib.Application app = GLib.Application.get_default(); accept_button.clicked.connect(() => { app.activate_action("accept-subscription", conversation.id); @@ -45,9 +45,9 @@ public class SubscriptionNotitication : Object { app.activate_action("deny-subscription", conversation.id); conversation_view.remove_notification(box); }); - box.add(new Label(_("This contact would like to add you to their contact list")) { margin_end=10, visible=true }); - box.add(accept_button); - box.add(deny_button); + box.append(new Label(_("This contact would like to add you to their contact list")) { margin_end=10, visible=true }); + box.append(accept_button); + box.append(deny_button); conversation_view.add_notification(box); } } diff --git a/main/src/ui/conversation_list/conversation_list_item_factory.vala b/main/src/ui/conversation_list/conversation_list_item_factory.vala new file mode 100644 index 00000000..dc26d2f1 --- /dev/null +++ b/main/src/ui/conversation_list/conversation_list_item_factory.vala @@ -0,0 +1,245 @@ +using Gtk; +using Dino.Entities; +using Dino; +using Gee; +using Pango; +using Xmpp; + +namespace Dino.Ui.ConversationList { + + 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 ConversationListRow(); + } + + public static void on_bind(ListItem listitem) { + ConversationViewModel list_model = (ConversationViewModel) listitem.get_item(); + ConversationListRow view = (ConversationListRow) listitem.get_child(); + StreamInteractor stream_interactor = list_model.stream_interactor; + + list_model.bind_property("name", view.name_label, "label"); + list_model.notify["latest-content-item"].connect((obj, _) => { + update_content_item(view, list_model.conversation, stream_interactor, ((ConversationViewModel) obj).latest_content_item); + }); + list_model.notify["unread-count"].connect((obj, _) => { + update_read(view, list_model.conversation, stream_interactor, (int) obj); + }); + + view.x_button.clicked.connect(() => list_model.closed() ); + + ConversationViewModel view_model = (ConversationViewModel) listitem.get_item(); + view.name_label.label = view_model.name; + if (view_model.latest_content_item != null) { + update_content_item(view, view_model.conversation, stream_interactor, view_model.latest_content_item); + } + update_read(view, view_model.conversation, stream_interactor, view_model.unread_count); + } + + private static void update_content_item(ConversationListRow view, Conversation conversation, StreamInteractor stream_interactor, ContentItem last_content_item) { + view.time_label.label = get_relative_time(last_content_item.time.to_local()); + view.image.set_conversation(stream_interactor, conversation); + + Label nick_label = view.nick_label; + Label message_label = view.message_label; + + switch (last_content_item.type_) { + case MessageItem.TYPE: + MessageItem message_item = last_content_item as MessageItem; + Message last_message = message_item.message; + + string body = last_message.body; + bool me_command = body.has_prefix("/me "); + + /* If we have a /me command, we always show the display + * name, and we don't set me_is_me on + * get_participant_display_name, since that will return + * "Me" (internationalized), whereas /me commands expect to + * be in the third person. We also omit the colon in this + * case, and strip off the /me prefix itself. */ + + if (conversation.type_ == Conversation.Type.GROUPCHAT || me_command) { + nick_label.label = Util.get_participant_display_name(stream_interactor, conversation, last_message.from, !me_command); + } else if (last_message.direction == Message.DIRECTION_SENT) { + nick_label.label = _("Me"); + } else { + nick_label.label = ""; + } + + if (me_command) { + /* Don't slice off the space after /me */ + body = body.slice("/me".length, body.length); + } else if (nick_label.label.length > 0) { + /* TODO: Is this valid for RTL languages? */ + nick_label.label += ": "; + } + + message_label.attributes.filter((attr) => attr.equal(attr_style_new(Pango.Style.ITALIC))); + message_label.label = Util.summarize_whitespaces_to_space(body); + + break; + case FileItem.TYPE: + FileItem file_item = last_content_item as FileItem; + FileTransfer transfer = file_item.file_transfer; + + if (conversation.type_ == Conversation.Type.GROUPCHAT) { + // TODO properly display nick for oneself + nick_label.label = Util.get_participant_display_name(stream_interactor, conversation, file_item.file_transfer.from, true) + ": "; + } else { + nick_label.label = transfer.direction == Message.DIRECTION_SENT ? _("Me") + ": " : ""; + } + + bool file_is_image = transfer.mime_type != null && transfer.mime_type.has_prefix("image"); + message_label.attributes.insert(attr_style_new(Pango.Style.ITALIC)); + if (transfer.direction == Message.DIRECTION_SENT) { + message_label.label = (file_is_image ? _("Image sent") : _("File sent") ); + } else { + message_label.label = (file_is_image ? _("Image received") : _("File received") ); + } + break; + case CallItem.TYPE: + CallItem call_item = (CallItem) last_content_item; + Call call = call_item.call; + + nick_label.label = call.direction == Call.DIRECTION_OUTGOING ? _("Me") + ": " : ""; + message_label.attributes.insert(attr_style_new(Pango.Style.ITALIC)); + message_label.label = call.direction == Call.DIRECTION_OUTGOING ? _("Outgoing call") : _("Incoming call"); + break; + } + nick_label.visible = true; + message_label.visible = true; + } + + private void update_read(ConversationListRow view, Conversation conversation, StreamInteractor stream_interactor, int num_unread) { + Label unread_count_label = view.unread_count_label; + Label name_label = view.name_label; + Label time_label = view.time_label; + Label nick_label = view.nick_label; + Label message_label = view.message_label; + if (num_unread == 0) { + unread_count_label.visible = false; + + name_label.attributes.filter((attr) => attr.equal(attr_weight_new(Weight.BOLD))); + time_label.attributes.filter((attr) => attr.equal(attr_weight_new(Weight.BOLD))); + nick_label.attributes.filter((attr) => attr.equal(attr_weight_new(Weight.BOLD))); + message_label.attributes.filter((attr) => attr.equal(attr_weight_new(Weight.BOLD))); + } else { + unread_count_label.label = num_unread.to_string(); + unread_count_label.visible = true; + + if (conversation.get_notification_setting(stream_interactor) == Conversation.NotifySetting.ON) { + unread_count_label.get_style_context().add_class("unread-count-notify"); + unread_count_label.get_style_context().remove_class("unread-count"); + } else { + unread_count_label.get_style_context().add_class("unread-count"); + unread_count_label.get_style_context().remove_class("unread-count-notify"); + } + + name_label.attributes.insert(attr_weight_new(Weight.BOLD)); + time_label.attributes.insert(attr_weight_new(Weight.BOLD)); + nick_label.attributes.insert(attr_weight_new(Weight.BOLD)); + message_label.attributes.insert(attr_weight_new(Weight.BOLD)); + } + + name_label.label = name_label.label; // TODO initializes redrawing, which would otherwise not happen. nicer? + time_label.label = time_label.label; + nick_label.label = nick_label.label; + message_label.label = message_label.label; + } + + private Widget generate_tooltip(StreamInteractor stream_interactor, Conversation conversation) { + Grid grid = new Grid() { row_spacing=5, column_homogeneous=false, column_spacing=5, margin_start=7, margin_end=7, margin_top=7, margin_bottom=7 }; + + Label label = new Label(conversation.counterpart.to_string()) { valign=Align.START, xalign=0, visible=true }; + label.attributes = new AttrList(); + label.attributes.insert(attr_weight_new(Weight.BOLD)); + + grid.attach(label, 0, 0, 2, 1); + + Gee.List<Jid>? full_jids = stream_interactor.get_module(PresenceManager.IDENTITY).get_full_jids(conversation.counterpart, conversation.account); + if (full_jids == null) return grid; + + for (int i = 0; i < full_jids.size; i++) { + Jid full_jid = full_jids[i]; + string? show = stream_interactor.get_module(PresenceManager.IDENTITY).get_last_show(full_jid, conversation.account); + if (show == null) continue; + + int i_cache = i; + stream_interactor.get_module(EntityInfo.IDENTITY).get_identity.begin(conversation.account, full_jid, (_, res) => { + Xep.ServiceDiscovery.Identity? identity = stream_interactor.get_module(EntityInfo.IDENTITY).get_identity.end(res); + + Image image = new Image() { hexpand=false, valign=Align.CENTER, visible=true }; + if (identity != null && (identity.type_ == Xep.ServiceDiscovery.Identity.TYPE_PHONE || identity.type_ == Xep.ServiceDiscovery.Identity.TYPE_TABLET)) { + image.set_from_icon_name("dino-device-phone-symbolic"); + } else { + image.set_from_icon_name("dino-device-desktop-symbolic"); + } + + if (show == Presence.Stanza.SHOW_AWAY) { + Util.force_color(image, "#FF9800"); + } else if (show == Presence.Stanza.SHOW_XA || show == Presence.Stanza.SHOW_DND) { + Util.force_color(image, "#FF5722"); + } else { + Util.force_color(image, "#4CAF50"); + } + + string? status = null; + if (show == Presence.Stanza.SHOW_AWAY) { + status = "away"; + } else if (show == Presence.Stanza.SHOW_XA) { + status = "not available"; + } else if (show == Presence.Stanza.SHOW_DND) { + status = "do not disturb"; + } + + var sb = new StringBuilder(); + if (identity != null && identity.name != null) { + sb.append(identity.name); + } else if (full_jid.resourcepart != null) { + sb.append(full_jid.resourcepart); + } else { + return; + } + if (status != null) { + sb.append(" <i>(").append(status).append(")</i>"); + } + + Label resource = new Label(sb.str) { use_markup=true, hexpand=true, xalign=0, visible=true }; + + grid.attach(image, 0, i_cache + 1, 1, 1); + grid.attach(resource, 1, i_cache + 1, 1, 1); + }); + } + return grid; + } + + private static string get_relative_time(DateTime datetime) { + DateTime now = new DateTime.now_local(); + TimeSpan timespan = now.difference(datetime); + if (timespan > 365 * TimeSpan.DAY) { + return datetime.get_year().to_string(); + } else if (timespan > 7 * TimeSpan.DAY) { + // Day and month + // xgettext:no-c-format + return datetime.format(_("%b %d")); + } else if (timespan > 2 * TimeSpan.DAY) { + return datetime.format("%a"); + } else if (datetime.get_day_of_month() != now.get_day_of_month()) { + return _("Yesterday"); + } else if (timespan > 9 * TimeSpan.MINUTE) { + return datetime.format(Util.is_24h_format() ? + /* xgettext:no-c-format */ /* Time in 24h format (w/o seconds) */ _("%H∶%M") : + /* xgettext:no-c-format */ /* Time in 12h format (w/o seconds) */ _("%l∶%M %p")); + } else if (timespan > 1 * TimeSpan.MINUTE) { + ulong mins = (ulong) (timespan.abs() / TimeSpan.MINUTE); + return n("%i min ago", "%i mins ago", mins).printf(mins); + } else { + return _("Just now"); + } + } +}
\ No newline at end of file diff --git a/main/src/ui/conversation_list/conversation_list_model.vala b/main/src/ui/conversation_list/conversation_list_model.vala new file mode 100644 index 00000000..9412e64a --- /dev/null +++ b/main/src/ui/conversation_list/conversation_list_model.vala @@ -0,0 +1,141 @@ +using Gtk; +using Dino.Entities; +using Dino; +using Gee; +using Xmpp; + +public class Dino.Ui.ConversationViewModel : Object { + public signal void closed(); + + public StreamInteractor stream_interactor { get; set; } + public Conversation conversation { get; set; } + public string name { get; set; } + public ContentItem? latest_content_item { get; set; } + public int unread_count { get; set; } +} + +public class Dino.Ui.ConversationListModel : Object, ListModel { + + public signal void closed_conversation(Conversation conversation); + + private HashMap<Conversation, ConversationViewModel> conversation_view_model_hm = new HashMap<Conversation, ConversationViewModel>(Conversation.hash_func, Conversation.equals_func); + private ArrayList<ConversationViewModel> view_models = new ArrayList<ConversationViewModel>(); + private StreamInteractor stream_interactor; + + public ConversationListModel(StreamInteractor stream_interactor) { + this.stream_interactor = stream_interactor; + + stream_interactor.get_module(ConversationManager.IDENTITY).conversation_activated.connect(add_conversation); + stream_interactor.get_module(ConversationManager.IDENTITY).conversation_deactivated.connect(remove_conversation); + stream_interactor.get_module(ContentItemStore.IDENTITY).new_item.connect(on_content_item_received); + + foreach (Conversation conversation in stream_interactor.get_module(ConversationManager.IDENTITY).get_active_conversations()) { + var view_model = create_view_model(conversation); + view_models.add(view_model); + conversation_view_model_hm[conversation] = view_model; + } + view_models.sort(sort); + items_changed(0, 0, get_n_items()); + + stream_interactor.get_module(RosterManager.IDENTITY).updated_roster_item.connect((account, jid, roster_item) => { + ConversationViewModel? view_model = get_view_model(account, jid, Conversation.Type.CHAT); + if (view_model == null) return; + view_model.name = Util.get_conversation_display_name(stream_interactor, view_model.conversation); + }); + stream_interactor.get_module(MucManager.IDENTITY).room_info_updated.connect((account, jid) => { + ConversationViewModel? view_model = get_view_model(account, jid, Conversation.Type.GROUPCHAT); + if (view_model == null) return; + view_model.name = Util.get_conversation_display_name(stream_interactor, view_model.conversation); + // bubble color might have changed + view_model.unread_count = stream_interactor.get_module(ChatInteraction.IDENTITY).get_num_unread(view_model.conversation); + }); + stream_interactor.get_module(MucManager.IDENTITY).private_room_occupant_updated.connect((account, room, occupant) => { + ConversationViewModel? view_model = get_view_model(account, room.bare_jid, Conversation.Type.GROUPCHAT); + if (view_model == null) return; + view_model.name = Util.get_conversation_display_name(stream_interactor, view_model.conversation); + }); + } + + public GLib.Object? get_item (uint position) { + if (position >= view_models.size) return null; + return view_models[(int)position]; + } + + public GLib.Type get_item_type () { + return GLib.Type.OBJECT; + } + + public uint get_n_items () { + return view_models.size; + } + + private ConversationViewModel create_view_model(Conversation conversation) { + var view_model = new ConversationViewModel(); + view_model.stream_interactor = stream_interactor; + view_model.conversation = conversation; + view_model.name = Util.get_conversation_display_name(stream_interactor, conversation); + view_model.latest_content_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_latest(conversation); + view_model.unread_count = stream_interactor.get_module(ChatInteraction.IDENTITY).get_num_unread(conversation); + view_model.closed.connect(() => closed_conversation(conversation)); + + return view_model; + } + + private void add_conversation(Conversation conversation) { + var view_model = create_view_model(conversation); + + view_models.add(view_model); + conversation_view_model_hm[conversation] = view_model; + view_models.sort(sort); + + int idx = view_models.index_of(view_model); + items_changed(idx, 0, 1); + } + + private async void remove_conversation(Conversation conversation) { + ConversationViewModel? view_model = conversation_view_model_hm[conversation]; + if (view_model == null) return; + + int idx = view_models.index_of(view_model); + view_models.remove(view_model); + conversation_view_model_hm.unset(conversation); + items_changed(idx, 1, 0); + } + + private void on_content_item_received(ContentItem item, Conversation conversation) { + ConversationViewModel? view_model = conversation_view_model_hm[conversation]; + if (view_model == null) return; + + view_model.latest_content_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_latest(conversation); + view_model.unread_count = stream_interactor.get_module(ChatInteraction.IDENTITY).get_num_unread(conversation); + + view_models.sort(sort); + items_changed(0, view_models.size, view_models.size); // TODO better + } + + private ConversationViewModel? get_view_model(Account account, Jid jid, Conversation.Type? conversation_ty) { + foreach (ConversationViewModel view_model in view_models) { + Conversation conversation = view_model.conversation; + if (conversation.account.equals(account) && conversation.counterpart.equals(jid)) { + if (conversation_ty != null && conversation.type_ != conversation_ty) continue; + return view_model; + } + } + return null; + } + + private int sort(ConversationViewModel vm1, ConversationViewModel vm2) { + Conversation c1 = vm1.conversation; + Conversation c2 = vm2.conversation; + + if (c1 == null || c2 == null) return 0; + if (c1.last_active == null) return -1; + if (c2.last_active == null) return 1; + + int comp = c2.last_active.compare(c1.last_active); + if (comp != 0) return comp; + + return Util.get_conversation_display_name(stream_interactor, c1) + .collate(Util.get_conversation_display_name(stream_interactor, c2)); + } +}
\ No newline at end of file diff --git a/main/src/ui/conversation_list/conversation_list_row.vala b/main/src/ui/conversation_list/conversation_list_row.vala new file mode 100644 index 00000000..ab4e8cee --- /dev/null +++ b/main/src/ui/conversation_list/conversation_list_row.vala @@ -0,0 +1,41 @@ +using Gee; +using Gdk; +using Gtk; +using Pango; + +using Dino; +using Dino.Entities; +using Xmpp; + +[GtkTemplate (ui = "/im/dino/Dino/conversation_row.ui")] +public class Dino.Ui.ConversationListRow : ListBoxRow { + + [GtkChild] public unowned AvatarImage image; + [GtkChild] public unowned Label name_label; + [GtkChild] public unowned Label time_label; + [GtkChild] public unowned Label nick_label; + [GtkChild] public unowned Label message_label; + [GtkChild] public unowned Label unread_count_label; + [GtkChild] public unowned Button x_button; + [GtkChild] public unowned Revealer time_revealer; + [GtkChild] public unowned Revealer xbutton_revealer; + [GtkChild] public unowned Revealer unread_count_revealer; + [GtkChild] public unowned Revealer main_revealer; + + construct { + name_label.attributes = new AttrList(); + } + + public override void state_flags_changed(StateFlags flags) { + StateFlags curr_flags = get_state_flags(); + if ((curr_flags & StateFlags.PRELIGHT) != 0) { + time_revealer.set_reveal_child(false); + unread_count_revealer.set_reveal_child(false); + xbutton_revealer.set_reveal_child(true); + } else { + time_revealer.set_reveal_child(true); + unread_count_revealer.set_reveal_child(true); + xbutton_revealer.set_reveal_child(false); + } + } +}
\ No newline at end of file diff --git a/main/src/ui/conversation_list_titlebar.vala b/main/src/ui/conversation_list_titlebar.vala index d97bec6e..692ac680 100644 --- a/main/src/ui/conversation_list_titlebar.vala +++ b/main/src/ui/conversation_list_titlebar.vala @@ -15,18 +15,12 @@ public class ConversationListTitlebar : Gtk.Box { } } -[GtkTemplate (ui = "/im/dino/Dino/conversation_list_titlebar_csd.ui")] -public class ConversationListTitlebarCsd : Gtk.HeaderBar { - - [GtkChild] private unowned MenuButton add_button; - [GtkChild] private unowned MenuButton menu_button; - - public ConversationListTitlebarCsd() { - custom_title = new Label("Dino") { visible = true, hexpand = true, xalign = 0 }; - custom_title.get_style_context().add_class("title"); - - create_add_menu(add_button, menu_button); - } +public static HeaderBar get_conversation_list_titlebar_csd() { + Builder builder = new Builder.from_resource("/im/dino/Dino/conversation_list_titlebar_csd.ui"); + MenuButton add_button = (MenuButton) builder.get_object("add_button"); + MenuButton menu_button = (MenuButton) builder.get_object("menu_button"); + create_add_menu(add_button, menu_button); + return (HeaderBar) builder.get_object("header_bar"); } private static void create_add_menu(MenuButton add_button, MenuButton menu_button) { diff --git a/main/src/ui/conversation_selector/conversation_selector.vala b/main/src/ui/conversation_selector/conversation_selector.vala index 4c7e6b8d..2e262bf1 100644 --- a/main/src/ui/conversation_selector/conversation_selector.vala +++ b/main/src/ui/conversation_selector/conversation_selector.vala @@ -6,16 +6,20 @@ using Dino.Entities; namespace Dino.Ui { -public class ConversationSelector : ListBox { +public class ConversationSelector : Widget { public signal void conversation_selected(Conversation conversation); + ListBox list_box = new ListBox() { hexpand=true }; + private StreamInteractor stream_interactor; private uint? drag_timeout; private HashMap<Conversation, ConversationSelectorRow> rows = new HashMap<Conversation, ConversationSelectorRow>(Conversation.hash_func, Conversation.equals_func); public ConversationSelector init(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; + list_box.insert_after(this, null); + this.layout_manager = new BinLayout(); stream_interactor.get_module(ConversationManager.IDENTITY).conversation_activated.connect(add_conversation); stream_interactor.get_module(ConversationManager.IDENTITY).conversation_deactivated.connect(remove_conversation); @@ -33,19 +37,21 @@ public class ConversationSelector : ListBox { construct { get_style_context().add_class("sidebar"); - set_header_func(header); - set_sort_func(sort); + list_box.set_header_func(header); + list_box.set_sort_func(sort); realize.connect(() => { - ListBoxRow? first_row = get_row_at_index(0); + ListBoxRow? first_row = list_box.get_row_at_index(0); if (first_row != null) { - select_row(first_row); + list_box.select_row(first_row); row_activated(first_row); } }); + + list_box.row_activated.connect(row_activated); } - public override void row_activated(ListBoxRow r) { + public void row_activated(ListBoxRow r) { ConversationSelectorRow? row = r as ConversationSelectorRow; if (row != null) { conversation_selected(row.conversation); @@ -56,12 +62,12 @@ public class ConversationSelector : ListBox { if (!rows.has_key(conversation)) { add_conversation(conversation); } - this.select_row(rows[conversation]); + list_box.select_row(rows[conversation]); } private void on_content_item_received(ContentItem item, Conversation conversation) { if (rows.has_key(conversation)) { - invalidate_sort(); + list_box.invalidate_sort(); } } @@ -70,17 +76,17 @@ public class ConversationSelector : ListBox { if (!rows.has_key(conversation)) { row = new ConversationSelectorRow(stream_interactor, conversation); rows[conversation] = row; - add(row); + list_box.append(row); row.main_revealer.set_reveal_child(true); - drag_dest_set(row, DestDefaults.MOTION, null, Gdk.DragAction.COPY); - drag_dest_set_track_motion(row, true); - row.drag_motion.connect(this.on_drag_motion); - row.drag_leave.connect(this.on_drag_leave); +// drag_dest_set(row, DestDefaults.MOTION, null, Gdk.DragAction.COPY); +// drag_dest_set_track_motion(row, true); +// row.drag_motion.connect(this.on_drag_motion); +// row.drag_leave.connect(this.on_drag_leave); } - invalidate_sort(); + list_box.invalidate_sort(); } - public bool on_drag_motion(Widget widget, Gdk.DragContext context, + /*public bool on_drag_motion(Widget widget, Gdk.DragContext context, int x, int y, uint time) { if (this.drag_timeout != null) return false; @@ -100,17 +106,17 @@ public class ConversationSelector : ListBox { Source.remove(this.drag_timeout); this.drag_timeout = null; } - } + }*/ private void select_fallback_conversation(Conversation conversation) { - if (get_selected_row() == rows[conversation]) { + if (list_box.get_selected_row() == rows[conversation]) { int index = rows[conversation].get_index(); - ListBoxRow? next_select_row = get_row_at_index(index + 1); + ListBoxRow? next_select_row = list_box.get_row_at_index(index + 1); if (next_select_row == null) { - next_select_row = get_row_at_index(index - 1); + next_select_row = list_box.get_row_at_index(index - 1); } if (next_select_row != null) { - select_row(next_select_row); + list_box.select_row(next_select_row); row_activated(next_select_row); } } @@ -120,17 +126,17 @@ public class ConversationSelector : ListBox { select_fallback_conversation(conversation); if (rows.has_key(conversation)) { yield rows[conversation].colapse(); - remove(rows[conversation]); + list_box.remove(rows[conversation]); rows.unset(conversation); } } public void loop_conversations(bool backwards) { - int index = get_selected_row().get_index(); + int index = list_box.get_selected_row().get_index(); int new_index = ((index + (backwards ? -1 : 1)) + rows.size) % rows.size; - ListBoxRow? next_select_row = get_row_at_index(new_index); + ListBoxRow? next_select_row = list_box.get_row_at_index(new_index); if (next_select_row != null) { - select_row(next_select_row); + list_box.select_row(next_select_row); row_activated(next_select_row); } } diff --git a/main/src/ui/conversation_selector/conversation_selector_row.vala b/main/src/ui/conversation_selector/conversation_selector_row.vala index 067f257c..f813ddfc 100644 --- a/main/src/ui/conversation_selector/conversation_selector_row.vala +++ b/main/src/ui/conversation_selector/conversation_selector_row.vala @@ -264,7 +264,7 @@ public class ConversationSelectorRow : ListBoxRow { private static Regex dino_resource_regex = /^dino\.[a-f0-9]{8}$/; private Widget generate_tooltip() { - Grid grid = new Grid() { row_spacing=5, column_homogeneous=false, column_spacing=2, margin_start=5, margin_end=5, margin_top=2, margin_bottom=2, visible=true }; + Grid grid = new Grid() { row_spacing=5, column_homogeneous=false, column_spacing=5, margin_start=7, margin_end=7, margin_top=7, margin_bottom=7 }; Label label = new Label(conversation.counterpart.to_string()) { valign=Align.START, xalign=0, visible=true }; label.attributes = new AttrList(); @@ -286,9 +286,9 @@ public class ConversationSelectorRow : ListBoxRow { Image image = new Image() { hexpand=false, valign=Align.CENTER, visible=true }; if (identity != null && (identity.type_ == Xep.ServiceDiscovery.Identity.TYPE_PHONE || identity.type_ == Xep.ServiceDiscovery.Identity.TYPE_TABLET)) { - image.set_from_icon_name("dino-device-phone-symbolic", IconSize.SMALL_TOOLBAR); + image.set_from_icon_name("dino-device-phone-symbolic"); } else { - image.set_from_icon_name("dino-device-desktop-symbolic", IconSize.SMALL_TOOLBAR); + image.set_from_icon_name("dino-device-desktop-symbolic"); } if (show == Presence.Stanza.SHOW_AWAY) { diff --git a/main/src/ui/conversation_titlebar/call_entry.vala b/main/src/ui/conversation_titlebar/call_entry.vala index 1b8b2a05..72376126 100644 --- a/main/src/ui/conversation_titlebar/call_entry.vala +++ b/main/src/ui/conversation_titlebar/call_entry.vala @@ -8,65 +8,42 @@ namespace Dino.Ui { public class CallTitlebarEntry : Plugins.ConversationTitlebarEntry, Object { public string id { get { return "call"; } } - - public CallButton call_button; - - private StreamInteractor stream_interactor; - - public CallTitlebarEntry(StreamInteractor stream_interactor) { - this.stream_interactor = stream_interactor; - - call_button = new CallButton(stream_interactor) { tooltip_text=_("Start call") }; - call_button.set_image(new Gtk.Image.from_icon_name("dino-phone-symbolic", Gtk.IconSize.MENU) { visible=true }); - } - public double order { get { return 4; } } - public Plugins.ConversationTitlebarWidget? get_widget(Plugins.WidgetType type) { - if (type == Plugins.WidgetType.GTK) { - return call_button; - } - return null; - } - } - public class CallButton : Plugins.ConversationTitlebarWidget, Gtk.MenuButton { + private MenuButton button = new MenuButton() { tooltip_text=_("Start call") }; private StreamInteractor stream_interactor; private Conversation conversation; - private ModelButton audio_button = new ModelButton() { text=_("Audio call"), visible=true }; - private ModelButton video_button = new ModelButton() { text=_("Video call"), visible=true }; - - public CallButton(StreamInteractor stream_interactor) { + public CallTitlebarEntry(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; - use_popover = true; - image = new Gtk.Image.from_icon_name("dino-phone-symbolic", Gtk.IconSize.MENU) { visible=true }; + button.set_icon_name("dino-phone-symbolic"); - Gtk.PopoverMenu popover_menu = new Gtk.PopoverMenu(); - Box box = new Box(Orientation.VERTICAL, 0) { margin=10, visible=true }; - audio_button.clicked.connect(() => { + Menu menu_model = new Menu(); + menu_model.append(_("Audio call"), "call.audio"); + menu_model.append(_("Video call"), "call.video"); + Gtk.PopoverMenu popover_menu = new Gtk.PopoverMenu.from_model(menu_model); + button.popover = popover_menu; + + SimpleActionGroup action_group = new SimpleActionGroup(); + SimpleAction audio_call_action = new SimpleAction("audio", null); + audio_call_action.activate.connect((parameter) => { stream_interactor.get_module(Calls.IDENTITY).initiate_call.begin(conversation, false, (_, res) => { CallState call_state = stream_interactor.get_module(Calls.IDENTITY).initiate_call.end(res); open_call_window(call_state); }); }); - box.add(audio_button); - - video_button.clicked.connect(() => { + action_group.insert(audio_call_action); + SimpleAction video_call_action = new SimpleAction("video", null); + video_call_action.activate.connect((parameter) => { stream_interactor.get_module(Calls.IDENTITY).initiate_call.begin(conversation, true, (_, res) => { CallState call_state = stream_interactor.get_module(Calls.IDENTITY).initiate_call.end(res); open_call_window(call_state); }); }); - box.add(video_button); - popover_menu.add(box); - - popover = popover_menu; - - clicked.connect(() => { - popover_menu.visible = true; - }); + action_group.insert(video_call_action); + button.insert_action_group("call", action_group); stream_interactor.get_module(Calls.IDENTITY).call_incoming.connect((call, state,conversation) => { update_button_state(); @@ -96,6 +73,7 @@ namespace Dino.Ui { } public new void set_conversation(Conversation conversation) { + print(@"set_conversation $(conversation.counterpart)\n"); this.conversation = conversation; update_visibility.begin(); @@ -103,12 +81,12 @@ namespace Dino.Ui { } private void update_button_state() { - this.sensitive = !stream_interactor.get_module(Calls.IDENTITY).is_call_in_progress(); + button.sensitive = !stream_interactor.get_module(Calls.IDENTITY).is_call_in_progress(); } private async void update_visibility() { if (conversation == null) { - visible = false; + button.visible = false; return; } @@ -116,10 +94,15 @@ namespace Dino.Ui { bool can_do_calls = yield stream_interactor.get_module(Calls.IDENTITY).can_conversation_do_calls(conversation); if (conv_bak != conversation) return; - visible = video_button.visible = can_do_calls; + button.visible = can_do_calls; } public new void unset_conversation() { } + + public Object? get_widget(Plugins.WidgetType type) { + if (type != Plugins.WidgetType.GTK4) return null; + return button; + } } } diff --git a/main/src/ui/conversation_titlebar/conversation_titlebar.vala b/main/src/ui/conversation_titlebar/conversation_titlebar.vala index 60d8286b..0d13e48b 100644 --- a/main/src/ui/conversation_titlebar/conversation_titlebar.vala +++ b/main/src/ui/conversation_titlebar/conversation_titlebar.vala @@ -6,14 +6,17 @@ using Dino.Entities; namespace Dino.Ui { -public interface ConversationTitlebar : Widget { +public interface ConversationTitlebar : Object { public abstract string? subtitle { get; set; } public abstract string? title { get; set; } - public abstract void insert_entry(Plugins.ConversationTitlebarEntry entry); + public abstract void insert_button(Widget button); + public abstract Widget get_widget(); } -public class ConversationTitlebarNoCsd : ConversationTitlebar, Gtk.Box { +public class ConversationTitlebarNoCsd : ConversationTitlebar, Object { + + public Box main = new Box(Orientation.HORIZONTAL, 0); public string? title { get { return title_label.label; } @@ -33,49 +36,61 @@ public class ConversationTitlebarNoCsd : ConversationTitlebar, Gtk.Box { private Label subtitle_label = new Label("") { use_markup=true, ellipsize=EllipsizeMode.END, visible=false }; construct { - Box content_box = new Box(Orientation.HORIZONTAL, 0) { margin=5, margin_start=15, margin_end=10, hexpand=true, visible=true }; - this.add(content_box); + Box content_box = new Box(Orientation.HORIZONTAL, 0) { margin_start=15, margin_end=10, hexpand=true, visible=true }; + main.append(content_box); Box titles_box = new Box(Orientation.VERTICAL, 0) { valign=Align.CENTER, hexpand=true, visible=true }; - content_box.add(titles_box); + content_box.append(titles_box); - titles_box.add(title_label); + titles_box.append(title_label); subtitle_label.attributes = new AttrList(); subtitle_label.get_style_context().add_class("dim-label"); - titles_box.add(subtitle_label); + titles_box.append(subtitle_label); - content_box.add(widgets_box); + content_box.append(widgets_box); } public ConversationTitlebarNoCsd() { - this.get_style_context().add_class("dino-header-right"); + main.get_style_context().add_class("dino-header-right"); } - public void insert_entry(Plugins.ConversationTitlebarEntry entry) { - Plugins.ConversationTitlebarWidget widget = entry.get_widget(Plugins.WidgetType.GTK); - if (widget != null) { - Button gtk_widget = (Gtk.Button) widget; - gtk_widget.relief = ReliefStyle.NONE; - widgets_box.pack_end(gtk_widget); - } + public void insert_button(Widget button) { + widgets_box.prepend(button); + } + + public Widget get_widget() { + return main; } } -public class ConversationTitlebarCsd : ConversationTitlebar, Gtk.HeaderBar { +public class ConversationTitlebarCsd : ConversationTitlebar, Object { + + public new string? title { get { return title_label.label; } set { title_label.label = value; } } + public new string? subtitle { get { return subtitle_label.label; } set { subtitle_label.label = value; subtitle_label.visible = (value != null); } } - public new string? title { get { return this.get_title(); } set { base.set_title(value); } } - public new string? subtitle { get { return this.get_subtitle(); } set { base.set_subtitle(value); } } + public HeaderBar header_bar = new HeaderBar(); + private Label title_label = new Label("") { ellipsize=EllipsizeMode.END }; + private Label subtitle_label = new Label("") { ellipsize=EllipsizeMode.END, visible=false }; public ConversationTitlebarCsd() { - this.get_style_context().add_class("dino-right"); - show_close_button = true; - hexpand = true; + 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); + } + + public void insert_button(Widget button) { + header_bar.pack_end(button); } - public void insert_entry(Plugins.ConversationTitlebarEntry entry) { - Plugins.ConversationTitlebarWidget widget = entry.get_widget(Plugins.WidgetType.GTK); - Button gtk_widget = (Gtk.Button)widget; - this.pack_end(gtk_widget); + public Widget get_widget() { + return header_bar; } } diff --git a/main/src/ui/conversation_titlebar/menu_entry.vala b/main/src/ui/conversation_titlebar/menu_entry.vala index 9b3b6ee2..28a06c24 100644 --- a/main/src/ui/conversation_titlebar/menu_entry.vala +++ b/main/src/ui/conversation_titlebar/menu_entry.vala @@ -6,58 +6,42 @@ namespace Dino.Ui { class MenuEntry : Plugins.ConversationTitlebarEntry, Object { public string id { get { return "menu"; } } - - StreamInteractor stream_interactor; - MenuWidget widget; - - public MenuEntry(StreamInteractor stream_interactor) { - this.stream_interactor = stream_interactor; - } - public double order { get { return 0; } } - public Plugins.ConversationTitlebarWidget? get_widget(Plugins.WidgetType type) { - if (type == Plugins.WidgetType.GTK) { - if (widget == null) { - widget = new MenuWidget(stream_interactor) { visible=true, sensitive=false }; - } - return widget; - } - return null; - } -} -class MenuWidget : Button, Plugins.ConversationTitlebarWidget { - - private StreamInteractor stream_interactor; + StreamInteractor stream_interactor; private Conversation? conversation; - public MenuWidget(StreamInteractor stream_interactor) { + Button button = new Button() { icon_name="open-menu-symbolic" }; + + public MenuEntry(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; - image = new Image.from_icon_name("open-menu-symbolic", IconSize.MENU); - clicked.connect(on_clicked); + button.clicked.connect(on_clicked); } public new void set_conversation(Conversation conversation) { - this.sensitive = true; + button.sensitive = true; this.conversation = conversation; if (conversation.type_ == Conversation.Type.GROUPCHAT) { - tooltip_text = "Channel details"; + button.tooltip_text = "Channel details"; } else { - tooltip_text = "Conversation details"; + button.tooltip_text = "Conversation details"; } } public new void unset_conversation() { - this.sensitive = false; + button.sensitive = false; } private void on_clicked() { ContactDetails.Dialog contact_details_dialog = new ContactDetails.Dialog(stream_interactor, conversation); - contact_details_dialog.set_transient_for((Window) get_toplevel()); + contact_details_dialog.set_transient_for((Window) button.get_root()); contact_details_dialog.present(); } + public Object? get_widget(Plugins.WidgetType type) { + if (type != Plugins.WidgetType.GTK4) return null; + return button; + } } - } diff --git a/main/src/ui/conversation_titlebar/occupants_entry.vala b/main/src/ui/conversation_titlebar/occupants_entry.vala index a316be20..5c0da99b 100644 --- a/main/src/ui/conversation_titlebar/occupants_entry.vala +++ b/main/src/ui/conversation_titlebar/occupants_entry.vala @@ -6,53 +6,39 @@ namespace Dino.Ui { class OccupantsEntry : Plugins.ConversationTitlebarEntry, Object { public string id { get { return "occupants"; } } + public double order { get { return 3; } } StreamInteractor stream_interactor; - OccupantsWidget widget; - - public OccupantsEntry(StreamInteractor stream_interactor) { - this.stream_interactor = stream_interactor; - } - - public double order { get { return 3; } } - public Plugins.ConversationTitlebarWidget? get_widget(Plugins.WidgetType type) { - if (type == Plugins.WidgetType.GTK) { - if (widget == null) { - widget = new OccupantsWidget(stream_interactor); - } - return widget; - } - return null; - } -} + private Conversation? conversation; -class OccupantsWidget : MenuButton, Plugins.ConversationTitlebarWidget { + private MenuButton button = new MenuButton() { icon_name="system-users-symbolic", tooltip_text=_("Members") }; - private Conversation? conversation; - private StreamInteractor stream_interactor; private OccupantMenu.View menu = null; - public OccupantsWidget(StreamInteractor stream_interactor) { - image = new Image.from_icon_name("system-users-symbolic", IconSize.MENU); - tooltip_text = _("Members"); - + public OccupantsEntry(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; - set_use_popover(true); } public new void set_conversation(Conversation conversation) { this.conversation = conversation; - visible = conversation.type_ == Conversation.Type.GROUPCHAT; if (conversation.type_ == Conversation.Type.GROUPCHAT) { + button.visible = true; OccupantMenu.View new_menu = new OccupantMenu.View(stream_interactor, conversation); - set_popover(new_menu); + button.set_popover(new_menu); menu = new_menu; + } else { + button.visible = false; } } public new void unset_conversation() { - visible = false; + button.visible = false; + } + + public Object? get_widget(Plugins.WidgetType type) { + if (type != Plugins.WidgetType.GTK4) return null; + return button; } } diff --git a/main/src/ui/conversation_titlebar/search_entry.vala b/main/src/ui/conversation_titlebar/search_entry.vala index 109c54d7..a51d7d43 100644 --- a/main/src/ui/conversation_titlebar/search_entry.vala +++ b/main/src/ui/conversation_titlebar/search_entry.vala @@ -7,25 +7,21 @@ namespace Dino.Ui { public class SearchMenuEntry : Plugins.ConversationTitlebarEntry, Object { public string id { get { return "search"; } } + public double order { get { return 1; } } - public GlobalSearchButton search_button = new GlobalSearchButton() { tooltip_text=_("Search messages"), visible = true }; + public ToggleButton button = new ToggleButton() { tooltip_text=_("Search messages") }; public SearchMenuEntry() { - search_button.set_image(new Gtk.Image.from_icon_name("system-search-symbolic", Gtk.IconSize.MENU) { visible = true }); + button.set_icon_name("system-search-symbolic"); } - public double order { get { return 1; } } - public Plugins.ConversationTitlebarWidget? get_widget(Plugins.WidgetType type) { - if (type == Plugins.WidgetType.GTK) { - return search_button; - } - return null; - } -} - -public class GlobalSearchButton : Plugins.ConversationTitlebarWidget, Gtk.ToggleButton { public new void set_conversation(Conversation conversation) { } public new void unset_conversation() { } + + public Object? get_widget(Plugins.WidgetType type) { + if (type != Plugins.WidgetType.GTK4) return null; + return button; + } } } diff --git a/main/src/ui/conversation_view.vala b/main/src/ui/conversation_view.vala index af7e32c1..a51b2381 100644 --- a/main/src/ui/conversation_view.vala +++ b/main/src/ui/conversation_view.vala @@ -7,23 +7,37 @@ using Dino.Entities; namespace Dino.Ui { [GtkTemplate (ui = "/im/dino/Dino/conversation_view.ui")] -public class ConversationView : Gtk.Overlay { +public class ConversationView : Widget { +// [GtkChild] public unowned ScrolledWindow conversation_scrolled; + [GtkChild] public unowned Overlay overlay; [GtkChild] public unowned Revealer goto_end_revealer; [GtkChild] public unowned Button goto_end_button; [GtkChild] public unowned ChatInput.View chat_input; [GtkChild] public unowned ConversationSummary.ConversationView conversation_frame; [GtkChild] public unowned Revealer white_revealer; + public ListView list_view = new ListView(null, null); + + public bool at_current_content = true; + construct { + this.layout_manager = new BinLayout(); white_revealer.notify["child-revealed"].connect_after(on_child_revealed_changed); + +// conversation_scrolled.set_child(list_view); +// list_view.set_factory(get_item_factory()); + +// conversation_scrolled.vadjustment.notify["upper"].connect_after(on_upper_notify); +// conversation_scrolled.vadjustment.notify["value"].connect(on_value_notify); + } public void add_overlay_dialog(Widget widget) { Revealer revealer = new Revealer() { transition_type=RevealerTransitionType.CROSSFADE , transition_duration= 100, visible=true }; - revealer.add(widget); + revealer.set_child(widget); - this.add_overlay(revealer); + overlay.add_overlay(revealer); revealer.reveal_child = true; white_revealer.visible = true; @@ -44,7 +58,22 @@ public class ConversationView : Gtk.Overlay { public override void dispose() { // To prevent a warning when closing Dino // "Can't set a target list on a widget until you've called gtk_drag_dest_set() to make the widget into a drag destination" - Gtk.drag_dest_unset(this); +// Gtk.drag_dest_unset(this); + } + + private void on_upper_notify() { + print("on_upper_notify\n"); + if (at_current_content) { + print("on_upper_notify2\n"); + // scroll down +// conversation_scrolled.vadjustment.value = conversation_scrolled.vadjustment.upper - conversation_scrolled.vadjustment.page_size; +// conversation_scrolled.scroll_child(ScrollType.END, false); + } + } + + private void on_value_notify() { + print("on_value_notify\n"); +// at_current_content = false; } } diff --git a/main/src/ui/conversation_view_controller.vala b/main/src/ui/conversation_view_controller.vala index 32b69835..759984c8 100644 --- a/main/src/ui/conversation_view_controller.vala +++ b/main/src/ui/conversation_view_controller.vala @@ -11,9 +11,9 @@ enum Target { STRING } -const TargetEntry[] target_list = { - { "text/uri-list", 0, Target.URI_LIST } -}; +//const TargetEntry[] target_list = { +// { "text/uri-list", 0, Target.URI_LIST } +//}; public class ConversationViewController : Object { @@ -25,6 +25,7 @@ public class ConversationViewController : Object { private Widget? overlay_dialog; private ConversationTitlebar titlebar; public SearchMenuEntry search_menu_entry = new SearchMenuEntry(); + public ListView list_view = new ListView(null, null); private ChatInputController chat_input_controller; private StreamInteractor stream_interactor; @@ -37,21 +38,21 @@ public class ConversationViewController : Object { this.app = GLib.Application.get_default() as Application; this.chat_input_controller = new ChatInputController(view.chat_input, stream_interactor); - chat_input_controller.activate_last_message_correction.connect(view.conversation_frame.activate_last_message_correction); +// chat_input_controller.activate_last_message_correction.connect(view.conversation_frame.activate_last_message_correction); chat_input_controller.file_picker_selected.connect(open_file_picker); chat_input_controller.clipboard_pasted.connect(on_clipboard_paste); view.conversation_frame.init(stream_interactor); // drag 'n drop file upload - view.drag_data_received.connect(this.on_drag_data_received); +// view.drag_data_received.connect(this.on_drag_data_received); // forward key presses - view.chat_input.key_press_event.connect(forward_key_press_to_chat_input); - view.conversation_frame.key_press_event.connect(forward_key_press_to_chat_input); - titlebar.key_press_event.connect(forward_key_press_to_chat_input); +// view.chat_input.key_press_event.connect(forward_key_press_to_chat_input); +// view.conversation_frame.key_press_event.connect(forward_key_press_to_chat_input); +// titlebar.key_press_event.connect(forward_key_press_to_chat_input); - // goto-end floating button +// goto-end floating button var vadjustment = view.conversation_frame.scrolled.vadjustment; vadjustment.notify["value"].connect(() => { bool button_active = vadjustment.value < vadjustment.upper - vadjustment.page_size; @@ -94,20 +95,24 @@ public class ConversationViewController : Object { app.plugin_registry.register_contact_titlebar_entry(new OccupantsEntry(stream_interactor)); app.plugin_registry.register_contact_titlebar_entry(new CallTitlebarEntry(stream_interactor)); foreach(var entry in app.plugin_registry.conversation_titlebar_entries) { - titlebar.insert_entry(entry); + Widget? button = entry.get_widget(Plugins.WidgetType.GTK4) as Widget; + if (button == null) { + continue; + } + titlebar.insert_button(button); } - AccelGroup accel_group = new AccelGroup(); - accel_group.connect(Gdk.Key.U, ModifierType.CONTROL_MASK, AccelFlags.VISIBLE, () => { - if (conversation == null) return false; - stream_interactor.get_module(FileManager.IDENTITY).is_upload_available.begin(conversation, (_, res) => { - if (stream_interactor.get_module(FileManager.IDENTITY).is_upload_available.end(res)) { - open_file_picker(); - } - }); - return false; - }); - ((Gtk.Window)view.get_toplevel()).add_accel_group(accel_group); +// AccelGroup accel_group = new AccelGroup(); +// accel_group.connect(Gdk.Key.U, ModifierType.CONTROL_MASK, AccelFlags.VISIBLE, () => { +// if (conversation == null) return false; +// stream_interactor.get_module(FileManager.IDENTITY).is_upload_available.begin(conversation, (_, res) => { +// if (stream_interactor.get_module(FileManager.IDENTITY).is_upload_available.end(res)) { +// open_file_picker(); +// } +// }); +// return false; +// }); +// ((Gtk.Window)view.get_toplevel()).add_accel_group(accel_group); } public void select_conversation(Conversation? conversation, bool default_initialize_conversation) { @@ -120,6 +125,13 @@ public class ConversationViewController : Object { this.conversation = conversation; + // Set list model onto list view +// Dino.Application app = GLib.Application.get_default() as Dino.Application; +// var map_list_model = get_conversation_content_model(new ContentItemMetaModel(app.db, conversation, stream_interactor), stream_interactor); +// NoSelection selection_model = new NoSelection(map_list_model); +// view.list_view.set_model(selection_model); +// view.at_current_content = true; + conversation.notify["encryption"].connect(update_file_upload_status); chat_input_controller.set_conversation(conversation); @@ -127,11 +139,8 @@ public class ConversationViewController : Object { update_conversation_display_name(); update_conversation_topic(); - foreach(var e in this.app.plugin_registry.conversation_titlebar_entries) { - Plugins.ConversationTitlebarWidget view = e.get_widget(Plugins.WidgetType.GTK); - if (view != null) { - view.set_conversation(conversation); - } + foreach(Plugins.ConversationTitlebarEntry e in this.app.plugin_registry.conversation_titlebar_entries) { + e.set_conversation(conversation); } if (default_initialize_conversation) { @@ -151,9 +160,9 @@ public class ConversationViewController : Object { bool upload_available = stream_interactor.get_module(FileManager.IDENTITY).is_upload_available.end(res); chat_input_controller.set_file_upload_active(upload_available); if (upload_available && overlay_dialog == null) { - Gtk.drag_dest_set(view, DestDefaults.ALL, target_list, Gdk.DragAction.COPY); +// Gtk.drag_dest_set(view, DestDefaults.ALL, target_list, Gdk.DragAction.COPY); } else { - Gtk.drag_dest_unset(view); +// Gtk.drag_dest_unset(view); } }); } @@ -178,48 +187,49 @@ public class ConversationViewController : Object { } private void on_clipboard_paste() { - Clipboard clipboard = Clipboard.get(Gdk.SELECTION_CLIPBOARD); - if (clipboard.wait_is_image_available()) { - clipboard.request_image((_, pixbuf) => { - File file = File.new_for_path(Path.build_filename(FileManager.get_storage_dir(), Xmpp.random_uuid() + ".png")); - try { - FileOutputStream fos = file.create(FileCreateFlags.REPLACE_DESTINATION); - pixbuf.save_to_stream_async.begin(fos, "png", null, () => { - open_send_file_overlay(file); - }); - } catch (Error e) { - warning("Could not create file to store pasted image in %s, %s", file.get_path(), e.message); - } - }); - } + Clipboard clipboard = view.get_clipboard(); +// if (clipboard.wait_is_image_available()) { +// clipboard.request_image((_, pixbuf) => { +// File file = File.new_for_path(Path.build_filename(FileManager.get_storage_dir(), Xmpp.random_uuid() + ".png")); +// try { +// FileOutputStream fos = file.create(FileCreateFlags.REPLACE_DESTINATION); +// pixbuf.save_to_stream_async.begin(fos, "png", null, () => { +// open_send_file_overlay(file); +// }); +// } catch (Error e) { +// warning("Could not create file to store pasted image in %s, %s", file.get_path(), e.message); +// } +// }); +// } } - private void on_drag_data_received(Widget widget, Gdk.DragContext context, int x, int y, SelectionData selection_data, uint target_type, uint time) { - if ((selection_data != null) && (selection_data.get_length() >= 0)) { - switch (target_type) { - case Target.URI_LIST: - string[] uris = selection_data.get_uris(); - // For now we only process the first dragged file - if (uris.length >= 1) { - try { - string file_path = Filename.from_uri(uris[0]); - open_send_file_overlay(File.new_for_path(file_path)); - } catch (ConvertError e) { - warning("Could not handle dragged file %s, %s", uris[0], e.message); - } - } - break; - default: - break; - } - } - } +// private void on_drag_data_received(Widget widget, Gdk.DragContext context, int x, int y, SelectionData selection_data, uint target_type, uint time) { +// if ((selection_data != null) && (selection_data.get_length() >= 0)) { +// switch (target_type) { +// case Target.URI_LIST: +// string[] uris = selection_data.get_uris(); +// // For now we only process the first dragged file +// if (uris.length >= 1) { +// try { +// string file_path = Filename.from_uri(uris[0]); +// open_send_file_overlay(File.new_for_path(file_path)); +// } catch (ConvertError e) { +// warning("Could not handle dragged file %s, %s", uris[0], e.message); +// } +// } +// break; +// default: +// break; +// } +// } +// } private void open_file_picker() { - PreviewFileChooserNative chooser = new PreviewFileChooserNative(_("Select file"), view.get_toplevel() as Gtk.Window, FileChooserAction.OPEN, _("Select"), _("Cancel")); - if (chooser.run() == Gtk.ResponseType.ACCEPT) { - open_send_file_overlay(File.new_for_path(chooser.get_filename())); - } + FileChooserNative chooser = new FileChooserNative(_("Select file"), view.get_root() as Gtk.Window, FileChooserAction.OPEN, _("Select"), _("Cancel")); + chooser.response.connect(() => { + open_send_file_overlay(File.new_for_path(chooser.get_file().get_path())); + }); + chooser.show(); } private void open_send_file_overlay(File file) { @@ -252,8 +262,8 @@ public class ConversationViewController : Object { update_file_upload_status(); }); - view.add_overlay_dialog(overlay); - overlay_dialog = overlay; + view.add_overlay_dialog(overlay.get_widget()); + overlay_dialog = overlay.get_widget(); update_file_upload_status(); } @@ -262,24 +272,24 @@ public class ConversationViewController : Object { stream_interactor.get_module(FileManager.IDENTITY).send_file.begin(file, conversation); } - private bool forward_key_press_to_chat_input(EventKey event) { - if (((Gtk.Window)view.get_toplevel()).get_focus() is TextView) { - return false; - } - - // Don't forward / change focus on Control / Alt - if (event.keyval == Gdk.Key.Control_L || event.keyval == Gdk.Key.Control_R || - event.keyval == Gdk.Key.Alt_L || event.keyval == Gdk.Key.Alt_R) { - return false; - } - // Don't forward / change focus on Control + ... - if ((event.state & ModifierType.CONTROL_MASK) > 0) { - return false; - } - if (view.chat_input.chat_text_view.text_view.key_press_event(event)) { - return true; - } - return false; - } +// private bool forward_key_press_to_chat_input(EventKey event) { +// if (((Gtk.Window)view.get_toplevel()).get_focus() is TextView) { +// return false; +// } +// +// // Don't forward / change focus on Control / Alt +// if (event.keyval == Gdk.Key.Control_L || event.keyval == Gdk.Key.Control_R || +// event.keyval == Gdk.Key.Alt_L || event.keyval == Gdk.Key.Alt_R) { +// return false; +// } +// // Don't forward / change focus on Control + ... +// if ((event.state & ModifierType.CONTROL_MASK) > 0) { +// return false; +// } +// if (view.chat_input.chat_text_view.text_view.key_press_event(event)) { +// return true; +// } +// return false; +// } } } diff --git a/main/src/ui/file_send_overlay.vala b/main/src/ui/file_send_overlay.vala index 369d291a..ceb78818 100644 --- a/main/src/ui/file_send_overlay.vala +++ b/main/src/ui/file_send_overlay.vala @@ -6,33 +6,41 @@ using Dino.Entities; namespace Dino.Ui { -[GtkTemplate (ui = "/im/dino/Dino/file_send_overlay.ui")] -public class FileSendOverlay : Gtk.EventBox { +public class FileSendOverlay { public signal void close(); public signal void send_file(); - [GtkChild] public unowned Button close_button; - [GtkChild] public unowned Button send_button; - [GtkChild] public unowned SizingBin file_widget_insert; - [GtkChild] public unowned Label info_label; + public Box main_box; + public Button close_button; + public Button send_button; + public SizingBin file_widget_insert; + public Label info_label; private bool can_send = true; public FileSendOverlay(File file, FileInfo file_info) { + Builder builder = new Builder.from_resource("/im/dino/Dino/file_send_overlay.ui"); + main_box = (Box) builder.get_object("main_box"); + close_button = (Button) builder.get_object("close_button"); + send_button = (Button) builder.get_object("send_button"); + file_widget_insert = (SizingBin) builder.get_object("file_widget_insert"); + info_label = (Label) builder.get_object("info_label"); + close_button.clicked.connect(() => { - this.close(); - this.destroy(); + main_box.unparent(); + main_box.destroy(); }); send_button.clicked.connect(() => { send_file(); this.close(); - this.destroy(); + main_box.unparent(); + main_box.destroy(); }); load_file_widget.begin(file, file_info); - this.realize.connect(() => { + main_box.realize.connect(() => { if (can_send) { send_button.grab_focus(); } else { @@ -40,12 +48,12 @@ public class FileSendOverlay : Gtk.EventBox { } }); - this.key_release_event.connect((event) => { - if (event.keyval == Gdk.Key.Escape) { - this.destroy(); - } - return false; - }); +// this.key_release_event.connect((event) => { +// if (event.keyval == Gdk.Key.Escape) { +// this.destroy(); +// } +// return false; +// }); } private async void load_file_widget(File file, FileInfo file_info) { @@ -78,7 +86,7 @@ public class FileSendOverlay : Gtk.EventBox { widget = default_widget; } - file_widget_insert.add(widget); + widget.set_parent(file_widget_insert); } public void set_file_too_large() { @@ -87,6 +95,10 @@ public class FileSendOverlay : Gtk.EventBox { send_button.sensitive = false; can_send = false; } + + public Widget get_widget() { + return main_box; + } } } diff --git a/main/src/ui/global_search.vala b/main/src/ui/global_search.vala index 30f8f65a..bee409fe 100644 --- a/main/src/ui/global_search.vala +++ b/main/src/ui/global_search.vala @@ -6,25 +6,38 @@ using Dino.Entities; namespace Dino.Ui { -[GtkTemplate (ui = "/im/dino/Dino/global_search.ui")] -public class GlobalSearch : Overlay { +public class GlobalSearch { public signal void selected_item(MessageItem item); private StreamInteractor stream_interactor; private string search = ""; private int loaded_results = -1; private Mutex reloading_mutex = Mutex(); - [GtkChild] public unowned SearchEntry search_entry; - [GtkChild] public unowned Label entry_number_label; - [GtkChild] public unowned ScrolledWindow results_scrolled; - [GtkChild] public unowned Box results_box; - [GtkChild] public unowned Stack results_empty_stack; - [GtkChild] public unowned Frame auto_complete_overlay; - [GtkChild] public unowned ListBox auto_complete_list; + public Overlay overlay; + public SearchEntry search_entry; + public Label entry_number_label; + public ScrolledWindow results_scrolled; + public Box results_box; + public Stack results_empty_stack; + public Frame auto_complete_overlay; + public ListBox auto_complete_list; - public GlobalSearch init(StreamInteractor stream_interactor) { + private ArrayList<Widget> auto_complete_children = new ArrayList<Widget>(); + private ArrayList<Widget> results_box_children = new ArrayList<Widget>(); + + public GlobalSearch(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; + Builder builder = new Builder.from_resource("/im/dino/Dino/global_search.ui"); + overlay = (Overlay) builder.get_object("overlay"); + search_entry = (SearchEntry) builder.get_object("search_entry"); + entry_number_label = (Label) builder.get_object("entry_number_label"); + results_scrolled = (ScrolledWindow) builder.get_object("results_scrolled"); + results_box = (Box) builder.get_object("results_box"); + results_empty_stack = (Stack) builder.get_object("results_empty_stack"); + auto_complete_overlay = (Frame) builder.get_object("auto_complete_overlay"); + auto_complete_list = (ListBox) builder.get_object("auto_complete_list"); + search_entry.search_changed.connect(() => { set_search(search_entry.text); }); @@ -34,9 +47,10 @@ public class GlobalSearch : Overlay { results_scrolled.vadjustment.notify["value"].connect(on_scrolled_window_vadjustment_value); results_scrolled.vadjustment.notify["upper"].connect_after(on_scrolled_window_vadjustment_upper); - event.connect(on_event); - - return this; + var overlay_key_events = new EventControllerKey(); + overlay_key_events.key_pressed.connect(on_key_pressed); + overlay_key_events.key_released.connect(on_key_released); + overlay.add_controller(overlay_key_events); } private void on_scrolled_window_vadjustment_value() { @@ -57,38 +71,49 @@ public class GlobalSearch : Overlay { reloading_mutex.unlock(); } - private bool on_event(Gdk.Event event) { - if (auto_complete_overlay.visible) { - if (event.type == Gdk.EventType.KEY_PRESS && event.key.keyval == Gdk.Key.Up) { - var row = auto_complete_list.get_selected_row(); - var index = row == null ? -1 : row.get_index() - 1; - if (index == -1) index = (int)auto_complete_list.get_children().length() - 1; - auto_complete_list.select_row(auto_complete_list.get_row_at_index(index)); - return true; - } - if (event.type == Gdk.EventType.KEY_PRESS && event.key.keyval == Gdk.Key.Down) { - var row = auto_complete_list.get_selected_row(); - var index = row == null ? 0 : row.get_index() + 1; - if (index == auto_complete_list.get_children().length()) index = 0; - auto_complete_list.select_row(auto_complete_list.get_row_at_index(index)); - return true; - } - if (event.type == Gdk.EventType.KEY_PRESS && event.key.keyval == Gdk.Key.Tab || - event.type == Gdk.EventType.KEY_RELEASE && event.key.keyval == Gdk.Key.Return) { - auto_complete_list.get_selected_row().activate(); - return true; - } + private bool on_key_pressed(uint keyval, uint keycode, Gdk.ModifierType state) { + if (!auto_complete_overlay.visible) return false; + + if (keyval == Gdk.Key.Up) { + var row = auto_complete_list.get_selected_row(); + var index = row == null ? -1 : row.get_index() - 1; + if (index == -1) index = (int)auto_complete_children.size - 1; + auto_complete_list.select_row(auto_complete_list.get_row_at_index(index)); + return true; + } + if (keyval == Gdk.Key.Down) { + var row = auto_complete_list.get_selected_row(); + var index = row == null ? 0 : row.get_index() + 1; + if (index == auto_complete_children.size) index = 0; + auto_complete_list.select_row(auto_complete_list.get_row_at_index(index)); + return true; + } + if (keyval == Gdk.Key.Tab) { + auto_complete_list.get_selected_row().activate(); + return true; } // TODO: Handle cursor movement in results // TODO: Direct all keystrokes to text input return false; } + private void on_key_released(uint keyval, uint keycode, Gdk.ModifierType state) { + if (keyval == Gdk.Key.Return) { + auto_complete_list.get_selected_row().activate(); + } + } + private void update_auto_complete() { Gee.List<SearchSuggestion> suggestions = stream_interactor.get_module(SearchProcessor.IDENTITY).suggest_auto_complete(search_entry.text, search_entry.cursor_position); auto_complete_overlay.visible = suggestions.size > 0; if (suggestions.size > 0) { - auto_complete_list.@foreach((widget) => auto_complete_list.remove(widget)); + // Remove current suggestions + foreach (Widget widget in auto_complete_children) { + auto_complete_list.remove(widget); + } + auto_complete_children.clear(); + + // Populate new suggestions foreach(SearchSuggestion suggestion in suggestions) { Builder builder = new Builder.from_resource("/im/dino/Dino/search_autocomplete.ui"); AvatarImage avatar = (AvatarImage)builder.get_object("image"); @@ -107,24 +132,29 @@ public class GlobalSearch : Overlay { label.label = display_name; } ListBoxRow row = new ListBoxRow() { visible = true, can_focus = false }; - row.add((Widget)builder.get_object("root")); + row.set_child((Widget)builder.get_object("root")); row.activate.connect(() => { handle_suggestion(suggestion); }); - auto_complete_list.add(row); + auto_complete_list.append(row); + auto_complete_children.add(row); } auto_complete_list.select_row(auto_complete_list.get_row_at_index(0)); } } private void handle_suggestion(SearchSuggestion suggestion) { - search_entry.move_cursor(MovementStep.LOGICAL_POSITIONS, suggestion.start_index - search_entry.cursor_position, false); - search_entry.delete_from_cursor(DeleteType.CHARS, suggestion.end_index - suggestion.start_index); - search_entry.insert_at_cursor(suggestion.completion + " "); + search_entry.delete_text(suggestion.start_index, suggestion.end_index); + int position = search_entry.cursor_position; + search_entry.insert_text(suggestion.completion + " ", suggestion.completion.length + 1, ref position); + search_entry.set_position(-1); } private void clear_search() { - results_box.@foreach((widget) => { results_box.remove(widget); }); + foreach (Widget widget in results_box_children) { + results_box.remove(widget); + } + results_box_children.clear(); loaded_results = 0; } @@ -157,30 +187,32 @@ public class GlobalSearch : Overlay { Box context_box = new Box(Orientation.VERTICAL, 5) { visible=true }; if (before_message != null && before_message.size > 0) { - context_box.add(get_context_message_widget(before_message.first())); + context_box.append(get_context_message_widget(before_message.first())); } Widget match_widget = get_match_message_widget(item); - context_box.add(match_widget); + context_box.append(match_widget); if (after_message != null && after_message.size > 0) { - context_box.add(get_context_message_widget(after_message.first())); + context_box.append(get_context_message_widget(after_message.first())); } - Label date_label = new Label(ConversationSummary.ItemMetaDataHeader.get_relative_time(item.time.to_local())) { xalign=0, visible=true }; + Label date_label = new Label(ConversationSummary.ConversationItemSkeleton.get_relative_time(item.time.to_local())) { xalign=0, visible=true }; date_label.get_style_context().add_class("dim-label"); string display_name = Util.get_conversation_display_name(stream_interactor, item.conversation); string title = item.message.type_ == Message.Type.GROUPCHAT ? _("In %s").printf(display_name) : _("With %s").printf(display_name); Box header_box = new Box(Orientation.HORIZONTAL, 10) { margin_start=7, visible=true }; - header_box.add(new Label(@"<b>$(Markup.escape_text(title))</b>") { ellipsize=EllipsizeMode.END, xalign=0, use_markup=true, visible=true }); - header_box.add(date_label); + header_box.append(new Label(@"<b>$(Markup.escape_text(title))</b>") { ellipsize=EllipsizeMode.END, xalign=0, use_markup=true, visible=true }); + header_box.append(date_label); Box result_box = new Box(Orientation.VERTICAL, 7) { visible=true }; - result_box.add(header_box); - result_box.add(context_box); + result_box.append(header_box); + result_box.append(context_box); + + results_box.append(result_box); + results_box_children.add(result_box); - results_box.add(result_box); } } @@ -237,11 +269,11 @@ public class GlobalSearch : Overlay { label.label = markup_text; grid.attach(label, 1, 1, 1, 1); - Button button = new Button() { relief=ReliefStyle.NONE, visible=true }; + Button button = new Button() { has_frame=false, visible=true }; button.clicked.connect(() => { selected_item(item); }); - button.add(grid); + button.child = grid; return button; } @@ -278,6 +310,10 @@ public class GlobalSearch : Overlay { } return ret; } + + public Widget get_widget() { + return overlay; + } } } diff --git a/main/src/ui/main_window.vala b/main/src/ui/main_window.vala index 04c01b26..a7e78b5d 100644 --- a/main/src/ui/main_window.vala +++ b/main/src/ui/main_window.vala @@ -13,21 +13,19 @@ public class MainWindow : Gtk.Window { public new string? title { get; set; } public string? subtitle { get; set; } - public WelcomePlaceholder welcome_placeholder = new WelcomePlaceholder() { visible=true }; - public NoAccountsPlaceholder accounts_placeholder = new NoAccountsPlaceholder() { visible=true }; + public WelcomePlaceholder welcome_placeholder = new WelcomePlaceholder(); + public NoAccountsPlaceholder accounts_placeholder = new NoAccountsPlaceholder(); public ConversationView conversation_view; public ConversationSelector conversation_selector; public ConversationTitlebar conversation_titlebar; - public ConversationTitlebarCsd conversation_titlebar_csd; - public ConversationListTitlebarCsd conversation_list_titlebar_csd; - public HeaderBar placeholder_headerbar = new HeaderBar() { title="Dino", show_close_button=true, visible=true }; - public Box box = new Box(Orientation.VERTICAL, 0) { orientation=Orientation.VERTICAL, visible=true }; - public Paned headerbar_paned = new Paned(Orientation.HORIZONTAL) { visible=true }; + public Widget conversation_list_titlebar; + public HeaderBar placeholder_headerbar = new HeaderBar() { show_title_buttons=true }; + public Box box = new Box(Orientation.VERTICAL, 0) { orientation=Orientation.VERTICAL }; + public Paned headerbar_paned = new Paned(Orientation.HORIZONTAL) { resize_start_child=false, shrink_start_child=false, shrink_end_child=false }; public Paned paned; public Revealer search_revealer; - public SearchEntry search_entry; - public GlobalSearch search_box; - private Stack stack = new Stack() { visible=true }; + public GlobalSearch global_search; + private Stack stack = new Stack(); private Stack left_stack; private Stack right_stack; @@ -35,18 +33,29 @@ public class MainWindow : Gtk.Window { private Database db; private Config config; + class construct { + var shortcut = new Shortcut(new KeyvalTrigger(Key.F, ModifierType.CONTROL_MASK), new CallbackAction((widget, args) => { + ((MainWindow) widget).search_revealer.reveal_child = true; + return false; + })); + add_shortcut(shortcut); + } + public MainWindow(Application application, StreamInteractor stream_interactor, Database db, Config config) { Object(application : application); this.stream_interactor = stream_interactor; this.db = db; this.config = config; - restore_window_size(); + this.title = "Dino"; this.get_style_context().add_class("dino-main"); - setup_headerbar(); + Gtk.Settings.get_default().notify["gtk-decoration-layout"].connect(set_window_buttons); - this.realize.connect(set_window_buttons); + ((Widget)this).realize.connect(set_window_buttons); + ((Widget)this).realize.connect(restore_window_size); + + setup_headerbar(); setup_unified(); setup_stack(); @@ -56,35 +65,32 @@ public class MainWindow : Gtk.Window { private void setup_unified() { Builder builder = new Builder.from_resource("/im/dino/Dino/unified_main_content.ui"); paned = (Paned) builder.get_object("paned"); - box.add(paned); + box.append(paned); left_stack = (Stack) builder.get_object("left_stack"); right_stack = (Stack) builder.get_object("right_stack"); conversation_view = (ConversationView) builder.get_object("conversation_view"); - conversation_selector = ((ConversationSelector) builder.get_object("conversation_list")).init(stream_interactor); - search_box = ((GlobalSearch) builder.get_object("search_box")).init(stream_interactor); search_revealer = (Revealer) builder.get_object("search_revealer"); - search_entry = (SearchEntry) builder.get_object("search_entry"); + conversation_selector = ((ConversationSelector) builder.get_object("conversation_list")).init(stream_interactor); + + Frame search_frame = (Frame) builder.get_object("search_frame"); + global_search = new GlobalSearch(stream_interactor); + search_frame.set_child(global_search.get_widget()); + Image conversation_list_placeholder_image = (Image) builder.get_object("conversation_list_placeholder_image"); conversation_list_placeholder_image.set_from_pixbuf(new Pixbuf.from_resource("/im/dino/Dino/icons/dino-conversation-list-placeholder-arrow.svg")); } private void setup_headerbar() { if (Util.use_csd()) { - conversation_list_titlebar_csd = new ConversationListTitlebarCsd() { visible=true }; - headerbar_paned.pack1(conversation_list_titlebar_csd, false, false); - - conversation_titlebar_csd = new ConversationTitlebarCsd() { visible=true }; - conversation_titlebar = conversation_titlebar_csd; - headerbar_paned.pack2(conversation_titlebar_csd, true, false); + conversation_list_titlebar = get_conversation_list_titlebar_csd(); + conversation_titlebar = new ConversationTitlebarCsd(); } else { - ConversationListTitlebar conversation_list_titlebar = new ConversationListTitlebar() { visible=true }; - headerbar_paned.pack1(conversation_list_titlebar, false, false); - - conversation_titlebar = new ConversationTitlebarNoCsd() { visible=true }; - headerbar_paned.pack2(conversation_titlebar, true, false); - - box.add(headerbar_paned); + conversation_list_titlebar = new ConversationListTitlebar(); + conversation_titlebar = new ConversationTitlebarNoCsd(); + box.append(headerbar_paned); } + headerbar_paned.set_start_child(conversation_list_titlebar); + headerbar_paned.set_end_child(conversation_titlebar.get_widget()); } private void set_window_buttons() { @@ -93,15 +99,17 @@ public class MainWindow : Gtk.Window { if (gtk_settings == null) return; string[] buttons = gtk_settings.gtk_decoration_layout.split(":"); - this.conversation_list_titlebar_csd.decoration_layout = buttons[0] + ":"; - this.conversation_titlebar_csd.decoration_layout = ((buttons.length == 2) ? ":" + buttons[1] : ""); + HeaderBar conversation_headerbar = this.conversation_titlebar.get_widget() as HeaderBar; + conversation_headerbar.decoration_layout = ((buttons.length == 2) ? ":" + buttons[1] : ""); + HeaderBar conversation_list_headerbar = this.conversation_list_titlebar as HeaderBar; + conversation_list_headerbar.decoration_layout = buttons[0] + ":"; } private void setup_stack() { stack.add_named(box, "main"); stack.add_named(welcome_placeholder, "welcome_placeholder"); stack.add_named(accounts_placeholder, "accounts_placeholder"); - add(stack); + set_child(stack); } public enum StackState { @@ -146,10 +154,8 @@ public class MainWindow : Gtk.Window { public void restore_window_size() { Gdk.Display? display = Gdk.Display.get_default(); if (display != null) { - Gdk.Monitor? monitor = display.get_primary_monitor(); - if (monitor == null) { - monitor = display.get_monitor_at_point(1, 1); - } + Gdk.Surface? surface = get_surface(); + Gdk.Monitor? monitor = display.get_monitor_at_surface(surface); if (monitor != null && config.window_width <= monitor.geometry.width && @@ -157,37 +163,30 @@ public class MainWindow : Gtk.Window { set_default_size(config.window_width, config.window_height); } } - this.window_position = Gtk.WindowPosition.CENTER; if (config.window_maximize) { maximize(); } - this.delete_event.connect(() => { + ((Widget)this).unrealize.connect(() => { save_window_size(); - config.window_maximize = this.is_maximized; - return false; + config.window_maximize = this.maximized; }); } public void save_window_size() { - if (this.is_maximized) return; + if (this.maximized) return; Gdk.Display? display = get_display(); - Gdk.Window? window = get_window(); - if (display != null && window != null) { - Gdk.Monitor monitor = display.get_monitor_at_window(window); - - int width = 0; - int height = 0; - get_size(out width, out height); - + Gdk.Surface? surface = get_surface(); + if (display != null && surface != null) { + Gdk.Monitor monitor = display.get_monitor_at_surface(surface); // Only store if the values have changed and are reasonable-looking. - if (config.window_width != width && width > 0 && width <= monitor.geometry.width) { - config.window_width = width; + if (config.window_width != default_width && default_width > 0 && default_width <= monitor.geometry.width) { + config.window_width = default_width; } - if (config.window_height != height && height > 0 && height <= monitor.geometry.height) { - config.window_height = height; + if (config.window_height != default_height && default_height > 0 && default_height <= monitor.geometry.height) { + config.window_height = default_height; } } } diff --git a/main/src/ui/main_window_controller.vala b/main/src/ui/main_window_controller.vala index dceb4094..7049aa40 100644 --- a/main/src/ui/main_window_controller.vala +++ b/main/src/ui/main_window_controller.vala @@ -30,18 +30,18 @@ public class MainWindowController : Object { this.conversation_view_controller = new ConversationViewController(window.conversation_view, window.conversation_titlebar, stream_interactor); - conversation_view_controller.search_menu_entry.search_button.bind_property("active", window.search_revealer, "reveal_child"); + conversation_view_controller.search_menu_entry.button.bind_property("active", window.search_revealer, "reveal_child", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL); window.search_revealer.notify["child-revealed"].connect(() => { if (window.search_revealer.child_revealed) { - if (window.conversation_view.conversation_frame.conversation != null && window.search_box.search_entry.text == "") { + if (window.conversation_view.conversation_frame.conversation != null && window.global_search.search_entry.text == "") { reset_search_entry(); } - window.search_box.search_entry.grab_focus_without_selecting(); - window.search_box.search_entry.set_position((int)window.search_box.search_entry.text_length); + window.global_search.search_entry.grab_focus(); + window.global_search.search_entry.set_position((int)window.global_search.search_entry.text.length); } }); - window.search_box.selected_item.connect((item) => { + window.global_search.selected_item.connect((item) => { select_conversation(item.conversation, false, false); window.conversation_view.conversation_frame.initialize_around_message(item.conversation, item); close_search(); @@ -55,30 +55,53 @@ public class MainWindowController : Object { window.accounts_placeholder.primary_button.clicked.connect(() => { app.activate_action("accounts", null); }); window.conversation_selector.conversation_selected.connect((conversation) => select_conversation(conversation)); - window.event.connect((event) => { - if (event.type == EventType.BUTTON_PRESS) { - int dest_x, dest_y; - bool ret = window.search_box.translate_coordinates(window, 0, 0, out dest_x, out dest_y); - int geometry_x, geometry_y, geometry_width, geometry_height; - window.get_window().get_geometry(out geometry_x, out geometry_y, out geometry_width, out geometry_height); - if (ret && event.button.x_root - geometry_x < dest_x || event.button.y_root - geometry_y < dest_y) { - close_search(); - } - } else if (event.type == EventType.KEY_RELEASE) { - if (event.key.keyval == Gdk.Key.Escape) { - close_search(); - } +// ConversationListModel list_model = new ConversationListModel(stream_interactor); +// list_model.closed_conversation.connect((conversation) => { +// print(@"closed $(conversation.counterpart.bare_jid)\n"); +// stream_interactor.get_module(ConversationManager.IDENTITY).close_conversation(conversation); +// }); +// SingleSelection selection_model = new SingleSelection(list_model) { autoselect=false }; +// selection_model.notify["selected-item"].connect(() => { +// ConversationViewModel view_model = (ConversationViewModel) selection_model.selected_item; +// if (view_model.conversation.equals(conversation)) return; +// print(@"selected conversation $(view_model.conversation.counterpart)\n"); +// select_conversation(view_model.conversation); +// }); +// window.conversation_list_view.set_model(selection_model); +// print(list_model.get_n_items().to_string() + " " + selection_model.get_n_items().to_string() + "<<"); +// print(selection_model.get_selected().to_string() + "<<"); +// window.conversation_list_view.realize.connect(() => { +// selection_model.set_selected(0); +// }); + + Widget window_widget = ((Widget) window); + + GestureClick gesture_click_controller = new GestureClick(); + window_widget.add_controller(gesture_click_controller); + gesture_click_controller.pressed.connect((n_press, click_x, click_y) => { + double search_x, search_y; + bool ret = window.search_revealer.translate_coordinates(window, 0, 0, out search_x, out search_y); + if (ret && click_x < search_x) { + close_search(); + } + }); + + EventControllerKey key_event_controller = new EventControllerKey(); + window_widget.add_controller(key_event_controller); + // TODO GTK4: Why doesn't this work with key_pressed signal + key_event_controller.key_released.connect((keyval) => { + if (keyval == Gdk.Key.Escape) { + close_search(); } - return false; }); - window.focus_in_event.connect(() => { + + EventControllerFocus focus_event_controller = new EventControllerFocus(); + window_widget.add_controller(focus_event_controller); + focus_event_controller.enter.connect(() => { stream_interactor.get_module(ChatInteraction.IDENTITY).on_window_focus_in(conversation); - window.urgency_hint = false; - return false; }); - window.focus_out_event.connect(() => { + focus_event_controller.leave.connect(() => { stream_interactor.get_module(ChatInteraction.IDENTITY).on_window_focus_out(conversation); - return false; }); window.conversation_selected.connect(conversation => select_conversation(conversation)); @@ -89,12 +112,12 @@ public class MainWindowController : Object { stream_interactor.get_module(ConversationManager.IDENTITY).conversation_deactivated.connect(() => update_stack_state()); update_stack_state(); - AccelGroup accel_group = new AccelGroup(); - accel_group.connect(Gdk.Key.F, ModifierType.CONTROL_MASK, AccelFlags.VISIBLE, () => { - window.search_revealer.reveal_child = true; - return false; - }); - window.add_accel_group(accel_group); +// AccelGroup accel_group = new AccelGroup(); +// accel_group.connect(Gdk.Key.F, ModifierType.CONTROL_MASK, AccelFlags.VISIBLE, () => { +// window.search_revealer.reveal_child = true; +// return false; +// }); +// window.add_accel_group(accel_group); } public void select_conversation(Conversation? conversation, bool do_reset_search = true, bool default_initialize_conversation = true) { @@ -122,11 +145,8 @@ public class MainWindowController : Object { conversation_view_controller.unset_conversation(); - foreach(var e in this.app.plugin_registry.conversation_titlebar_entries) { - Plugins.ConversationTitlebarWidget widget = e.get_widget(Plugins.WidgetType.GTK); - if (widget != null) { - widget.unset_conversation(); - } + foreach(Plugins.ConversationTitlebarEntry e in this.app.plugin_registry.conversation_titlebar_entries) { + e.unset_conversation(); } } @@ -150,18 +170,17 @@ public class MainWindowController : Object { switch (conversation.type_) { case Conversation.Type.CHAT: case Conversation.Type.GROUPCHAT_PM: - window.search_box.search_entry.text = @"with:$(conversation.counterpart) "; + window.global_search.search_entry.text = @"with:$(conversation.counterpart) "; break; case Conversation.Type.GROUPCHAT: - window.search_box.search_entry.text = @"in:$(conversation.counterpart) "; + window.global_search.search_entry.text = @"in:$(conversation.counterpart) "; break; } } } private void close_search() { - conversation_view_controller.search_menu_entry.search_button.active = false; - window.search_revealer.reveal_child = false; + conversation_view_controller.search_menu_entry.button.active = false; } } diff --git a/main/src/ui/manage_accounts/add_account_dialog.vala b/main/src/ui/manage_accounts/add_account_dialog.vala index 9cf60e3f..d3dbf390 100644 --- a/main/src/ui/manage_accounts/add_account_dialog.vala +++ b/main/src/ui/manage_accounts/add_account_dialog.vala @@ -110,9 +110,9 @@ public class AddAccountDialog : Gtk.Dialog { foreach (string server in server_list) { ListBoxRow list_box_row = new ListBoxRow() { visible=true }; - list_box_row.add(new Label(server) { xalign=0, margin=3, margin_start=7, margin_end=7, visible=true }); + list_box_row.set_child(new Label(server) { xalign=0, margin_start=7, margin_end=7, visible=true }); list_box_jids[list_box_row] = server; - server_list_box.add(list_box_row); + server_list_box.append(list_box_row); } // Register Form @@ -134,7 +134,7 @@ public class AddAccountDialog : Gtk.Dialog { create_account_box.visible = false; register_box.visible = false; success_box.visible = false; - set_default(sign_in_jid_continue_button); +// set_default(sign_in_jid_continue_button); sign_in_jid_error_label.label = ""; jid_entry.sensitive = true; @@ -174,7 +174,7 @@ public class AddAccountDialog : Gtk.Dialog { create_account_box.visible = false; register_box.visible = false; success_box.visible = false; - set_default(sign_in_password_continue_button); +// set_default(sign_in_password_continue_button); sign_in_password_error_label.label = ""; sign_in_password_title.label = _("Sign in to %s").printf(login_jid.to_string()); @@ -184,7 +184,7 @@ public class AddAccountDialog : Gtk.Dialog { private void show_select_server() { server_entry.text = ""; server_entry.grab_focus(); - set_default(select_server_continue); +// set_default(select_server_continue); server_list_box.row_activated.disconnect(on_server_list_row_activated); server_list_box.unselect_all(); @@ -209,7 +209,7 @@ public class AddAccountDialog : Gtk.Dialog { create_account_box.visible = false; success_box.visible = false; - set_default(register_form_continue); +// set_default(register_form_continue); animate_window_resize(register_box); } @@ -223,7 +223,7 @@ public class AddAccountDialog : Gtk.Dialog { register_box.visible = false; success_description.label = _("You can now use the account %s.").printf("<b>" + Markup.escape_text(account.bare_jid.to_string()) + "</b>"); - set_default(success_continue_button); +// set_default(success_continue_button); } private void on_jid_entry_changed() { @@ -329,36 +329,41 @@ public class AddAccountDialog : Gtk.Dialog { } private void set_register_form(Jid server, Xep.InBandRegistration.Form form) { - form_box.foreach((widget) => { form_box.remove(widget); }); + Widget widget = form_box.get_first_child(); + while (widget != null) { + form_box.remove(widget); + widget = form_box.get_first_child(); + } +// form_box.foreach((widget) => { form_box.remove(widget); }); register_title.label = _("Register on %s").printf(server.to_string()); if (form.oob != null) { - form_box.add(new Label(_("The server requires to sign up through a website")){ visible=true } ); - form_box.add(new Label(@"<a href=\"$(form.oob)\">$(form.oob)</a>") { use_markup=true, visible=true }); + form_box.append(new Label(_("The server requires to sign up through a website")){ visible=true } ); + form_box.append(new Label(@"<a href=\"$(form.oob)\">$(form.oob)</a>") { use_markup=true, visible=true }); register_form_continue_label.label = _("Open website"); register_form_continue.visible = true; register_form_continue.grab_focus(); } else if (form.fields.size > 0) { if (form.instructions != null && form.instructions != "") { string markup_instructions = Util.parse_add_markup(form.instructions, null, true, false); - form_box.add(new Label(markup_instructions) { use_markup=true, halign=Align.CENTER, xalign=0, margin_top=7, + form_box.append(new Label(markup_instructions) { use_markup=true, halign=Align.CENTER, xalign=0, margin_top=7, wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR, visible=true }); } foreach (Xep.DataForms.DataForm.Field field in form.fields) { Widget? field_widget = Util.get_data_form_field_widget(field); if (field.label != null && field.label != "" && field_widget != null) { - form_box.add(new Label(field.label) { xalign=0, margin_top=7, visible=true }); - form_box.add(field_widget); + form_box.append(new Label(field.label) { xalign=0, margin_top=7, visible=true }); + form_box.append(field_widget); } else if (field.type_ == Xep.DataForms.DataForm.Type.FIXED && field.get_value_string() != "") { string markup_fixed_field = Util.parse_add_markup(field.get_value_string(), null, true, false); - form_box.add(new Label(markup_fixed_field) { use_markup=true, xalign=0, margin_top=7, + form_box.append(new Label(markup_fixed_field) { use_markup=true, xalign=0, margin_top=7, wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR, visible=true }); } } register_form_continue.visible = true; register_form_continue_label.label = _("Register"); } else { - form_box.add(new Label(_("Check %s for information on how to sign up").printf(@"<a href=\"http://$(server)\">$(server)</a>")) { use_markup=true, visible=true }); + form_box.append(new Label(_("Check %s for information on how to sign up").printf(@"<a href=\"http://$(server)\">$(server)</a>")) { use_markup=true, visible=true }); register_form_continue.visible = false; } } @@ -418,21 +423,21 @@ public class AddAccountDialog : Gtk.Dialog { } private void animate_window_resize(Widget widget) { // TODO code duplication - int def_height, curr_width, curr_height; - get_size(out curr_width, out curr_height); - widget.get_preferred_height(null, out def_height); - def_height += 5; - int difference = def_height - curr_height; - Timer timer = new Timer(); - Timeout.add((int) (stack.transition_duration / 30), - () => { - ulong microsec; - timer.elapsed(out microsec); - ulong millisec = microsec / 1000; - double partial = double.min(1, (double) millisec / stack.transition_duration); - resize(curr_width, (int) (curr_height + difference * partial)); - return millisec < stack.transition_duration; - }); +// int def_height, curr_width, curr_height; +// get_size(out curr_width, out curr_height); +// widget.get_preferred_height(null, out def_height); +// def_height += 5; +// int difference = def_height - curr_height; +// Timer timer = new Timer(); +// Timeout.add((int) (stack.transition_duration / 30), +// () => { +// ulong microsec; +// timer.elapsed(out microsec); +// ulong millisec = microsec / 1000; +// double partial = double.min(1, (double) millisec / stack.transition_duration); +// resize(curr_width, (int) (curr_height + difference * partial)); +// return millisec < stack.transition_duration; +// }); } } diff --git a/main/src/ui/manage_accounts/dialog.vala b/main/src/ui/manage_accounts/dialog.vala index 5d596bc2..88dc7485 100644 --- a/main/src/ui/manage_accounts/dialog.vala +++ b/main/src/ui/manage_accounts/dialog.vala @@ -17,8 +17,8 @@ public class Dialog : Gtk.Dialog { [GtkChild] public unowned Stack main_stack; [GtkChild] public unowned ListBox account_list; [GtkChild] public unowned Button no_accounts_add; - [GtkChild] public unowned ToolButton add_account_button; - [GtkChild] public unowned ToolButton remove_account_button; + [GtkChild] public unowned Button add_account_button; + [GtkChild] public unowned Button remove_account_button; [GtkChild] public unowned AvatarImage image; [GtkChild] public unowned Button image_button; [GtkChild] public unowned Label jid_label; @@ -28,8 +28,6 @@ public class Dialog : Gtk.Dialog { [GtkChild] public unowned Util.EntryLabelHybrid alias_hybrid; [GtkChild] public unowned Grid settings_list; - private ArrayList<Plugins.AccountSettingsWidget> plugin_widgets = new ArrayList<Plugins.AccountSettingsWidget>(); - private Database db; private StreamInteractor stream_interactor; private Account? selected_account; @@ -44,8 +42,8 @@ public class Dialog : Gtk.Dialog { if (selected_account != null) remove_account(account_row); }); image_button.clicked.connect(show_select_avatar); - alias_hybrid.entry.key_release_event.connect(() => { selected_account.alias = alias_hybrid.text; return false; }); - password_hybrid.entry.key_release_event.connect(() => { selected_account.password = password_hybrid.text; return false; }); +// alias_hybrid.entry.key_release_event.connect(() => { selected_account.alias = alias_hybrid.text; return false; }); +// password_hybrid.entry.key_release_event.connect(() => { selected_account.password = password_hybrid.text; return false; }); Util.LabelHybridGroup label_hybrid_group = new Util.LabelHybridGroup(); label_hybrid_group.add(alias_hybrid); @@ -54,25 +52,18 @@ public class Dialog : Gtk.Dialog { main_stack.set_visible_child_name("no_accounts"); int row_index = 4; - int16 default_top_padding = new Gtk.Button().get_style_context().get_padding(Gtk.StateFlags.NORMAL).top + 1; + int16 default_top_padding = new Gtk.Button().get_style_context().get_padding().top + 1; Application app = GLib.Application.get_default() as Application; - foreach (var e in app.plugin_registry.account_settings_entries) { - Plugins.AccountSettingsWidget widget = e.get_widget(Plugins.WidgetType.GTK); - plugin_widgets.add(widget); + foreach (Plugins.AccountSettingsEntry e in app.plugin_registry.account_settings_entries) { + Widget? widget = e.get_widget(Plugins.WidgetType.GTK4) as Widget; + if (widget == null) continue; Label label = new Label(e.name) { xalign=1, yalign=0, visible=true }; label.get_style_context().add_class("dim-label"); label.margin_top = e.label_top_padding == -1 ? default_top_padding : e.label_top_padding; - settings_list.attach(label, 0, row_index); - if (widget is Widget) { - Widget gtkw = (Widget) widget; - plugin_widgets.add(widget); - gtkw.visible = true; - settings_list.attach(gtkw, 1, row_index, 2); - } else { - // TODO - } + + settings_list.attach(widget, 1, row_index, 2); row_index++; } } @@ -102,7 +93,7 @@ public class Dialog : Gtk.Dialog { public AccountRow add_account(Account account) { AccountRow account_item = new AccountRow (stream_interactor, account); - account_list.add(account_item); + account_list.append(account_item); main_stack.set_visible_child_name("accounts_exist"); return account_item; } @@ -127,7 +118,7 @@ public class Dialog : Gtk.Dialog { Button ok_button = msg.get_widget_for_response(ResponseType.OK) as Button; ok_button.label = _("Remove"); ok_button.get_style_context().add_class("destructive-action"); - if (msg.run() == Gtk.ResponseType.OK) { + if (/*msg.run() == Gtk.ResponseType.OK*/ true) { account_list.remove(account_item); if (account_item.account.enabled) account_disabled(account_item.account); account_item.account.remove(); @@ -149,7 +140,7 @@ public class Dialog : Gtk.Dialog { } private void show_select_avatar() { - PreviewFileChooserNative chooser = new PreviewFileChooserNative(_("Select avatar"), this, FileChooserAction.OPEN, _("Select"), _("Cancel")); + FileChooserNative chooser = new FileChooserNative(_("Select avatar"), this, FileChooserAction.OPEN, _("Select"), _("Cancel")); FileFilter filter = new FileFilter(); foreach (PixbufFormat pixbuf_format in Pixbuf.get_formats()) { foreach (string mime_type in pixbuf_format.get_mime_types()) { @@ -164,10 +155,12 @@ public class Dialog : Gtk.Dialog { filter.add_pattern("*"); chooser.add_filter(filter); - if (chooser.run() == Gtk.ResponseType.ACCEPT) { - string uri = chooser.get_filename(); + chooser.response.connect(() => { + string uri = chooser.get_file().get_path(); stream_interactor.get_module(AvatarManager.IDENTITY).publish(selected_account, uri); - } + }); + + chooser.show(); } private bool change_account_state(bool state) { @@ -201,8 +194,9 @@ public class Dialog : Gtk.Dialog { active_switch.state_set.connect(change_account_state); - foreach(Plugins.AccountSettingsWidget widget in plugin_widgets) { - widget.set_account(account); + Application app = GLib.Application.get_default() as Application; + foreach (Plugins.AccountSettingsEntry e in app.plugin_registry.account_settings_entries) { + e.set_account(account); } } diff --git a/main/src/ui/occupant_menu/list.vala b/main/src/ui/occupant_menu/list.vala index fcb039d7..20a98cf6 100644 --- a/main/src/ui/occupant_menu/list.vala +++ b/main/src/ui/occupant_menu/list.vala @@ -4,7 +4,7 @@ using Gtk; using Dino.Entities; using Xmpp; -namespace Dino.Ui.OccupantMenu{ +namespace Dino.Ui.OccupantMenu { [GtkTemplate (ui = "/im/dino/Dino/occupant_list.ui")] public class List : Box { @@ -17,7 +17,8 @@ public class List : Box { private Conversation conversation; private string[]? filter_values; - private HashMap<Jid, ListRow> rows = new HashMap<Jid, ListRow>(Jid.hash_func, Jid.equals_func); + private HashMap<Jid, Widget> rows = new HashMap<Jid, Widget>(Jid.hash_func, Jid.equals_func); + public HashMap<Widget, ListRow> row_wrappers = new HashMap<Widget, ListRow>(); public List(StreamInteractor stream_interactor, Conversation conversation) { this.stream_interactor = stream_interactor; @@ -53,8 +54,12 @@ public class List : Box { } public void add_occupant(Jid jid) { - rows[jid] = new ListRow(stream_interactor, conversation, jid); - list_box.add(rows[jid]); + var row_wrapper = new ListRow(stream_interactor, conversation, jid); + var widget = row_wrapper.get_widget(); + + row_wrappers[widget] = row_wrapper; + rows[jid] = widget; + list_box.append(widget); } public void remove_occupant(Jid jid) { @@ -81,13 +86,13 @@ public class List : Box { } private void header(ListBoxRow row, ListBoxRow? before_row) { - ListRow c1 = row as ListRow; - Xmpp.Xep.Muc.Affiliation? a1 = stream_interactor.get_module(MucManager.IDENTITY).get_affiliation(conversation.counterpart, c1.jid, c1.conversation.account); + ListRow row_wrapper1 = row_wrappers[row.get_child()]; + Xmpp.Xep.Muc.Affiliation? a1 = stream_interactor.get_module(MucManager.IDENTITY).get_affiliation(conversation.counterpart, row_wrapper1.jid, row_wrapper1.conversation.account); if (a1 == null) return; if (before_row != null) { - ListRow c2 = (ListRow) before_row; - Xmpp.Xep.Muc.Affiliation? a2 = stream_interactor.get_module(MucManager.IDENTITY).get_affiliation(conversation.counterpart, c2.jid, c2.conversation.account); + ListRow row_wrapper2 = row_wrappers[before_row.get_child()]; + Xmpp.Xep.Muc.Affiliation? a2 = stream_interactor.get_module(MucManager.IDENTITY).get_affiliation(conversation.counterpart, row_wrapper2.jid, row_wrapper2.conversation.account); if (a1 != a2) { row.set_header(generate_header_widget(a1, false)); } else if (row.get_header() != null){ @@ -112,7 +117,7 @@ public class List : Box { } int count = 0; - foreach (ListRow row in rows.values) { + foreach (ListRow row in row_wrappers.values) { Xmpp.Xep.Muc.Affiliation aff = stream_interactor.get_module(MucManager.IDENTITY).get_affiliation(conversation.counterpart, row.jid, conversation.account); if (aff == affiliation) count++; } @@ -120,36 +125,34 @@ public class List : Box { Label title_label = new Label("") { margin_start=10, xalign=0, visible=true }; title_label.set_markup(@"<b>$(Markup.escape_text(aff_str))</b>"); - Label count_label = new Label(@"$count") { xalign=0, margin_end=7, expand=true, visible=true }; + Label count_label = new Label(@"$count") { xalign=0, margin_end=7, hexpand=true, visible=true }; count_label.get_style_context().add_class("dim-label"); Grid grid = new Grid() { margin_top=top?5:15, column_spacing=5, hexpand=true, visible=true }; grid.attach(title_label, 0, 0, 1, 1); grid.attach(count_label, 1, 0, 1, 1); - grid.attach(new Separator(Orientation.HORIZONTAL) { expand=true, visible=true }, 0, 1, 2, 1); + grid.attach(new Separator(Orientation.HORIZONTAL) { hexpand=true, vexpand=true, visible=true }, 0, 1, 2, 1); return grid; } private bool filter(ListBoxRow r) { - if (r.get_type().is_a(typeof(ListRow))) { - ListRow row = r as ListRow; - foreach (string filter in filter_values) { - return row.name_label.label.down().contains(filter.down()); - } + ListRow row_wrapper = row_wrappers[r.get_child()]; + foreach (string filter in filter_values) { + return row_wrapper.name_label.label.down().contains(filter.down()); } return true; } private int sort(ListBoxRow row1, ListBoxRow row2) { - if (row1.get_type().is_a(typeof(ListRow)) && row2.get_type().is_a(typeof(ListRow))) { - ListRow c1 = row1 as ListRow; - ListRow c2 = row2 as ListRow; - int affiliation1 = get_affiliation_ranking(stream_interactor.get_module(MucManager.IDENTITY).get_affiliation(conversation.counterpart, c1.jid, c1.conversation.account) ?? Xmpp.Xep.Muc.Affiliation.NONE); - int affiliation2 = get_affiliation_ranking(stream_interactor.get_module(MucManager.IDENTITY).get_affiliation(conversation.counterpart, c2.jid, c2.conversation.account) ?? Xmpp.Xep.Muc.Affiliation.NONE); - if (affiliation1 < affiliation2) return -1; - else if (affiliation1 > affiliation2) return 1; - else return c1.name_label.label.collate(c2.name_label.label); - } + ListRow row_wrapper1 = row_wrappers[row1.get_child()]; + ListRow row_wrapper2 = row_wrappers[row2.get_child()]; + + int affiliation1 = get_affiliation_ranking(stream_interactor.get_module(MucManager.IDENTITY).get_affiliation(conversation.counterpart, row_wrapper1.jid, row_wrapper1.conversation.account) ?? Xmpp.Xep.Muc.Affiliation.NONE); + int affiliation2 = get_affiliation_ranking(stream_interactor.get_module(MucManager.IDENTITY).get_affiliation(conversation.counterpart, row_wrapper2.jid, row_wrapper2.conversation.account) ?? Xmpp.Xep.Muc.Affiliation.NONE); + + if (affiliation1 < affiliation2) return -1; + else if (affiliation1 > affiliation2) return 1; + else return row_wrapper1.name_label.label.collate(row_wrapper2.name_label.label); return 0; } diff --git a/main/src/ui/occupant_menu/list_row.vala b/main/src/ui/occupant_menu/list_row.vala index e0fb3f14..6b43fe7f 100644 --- a/main/src/ui/occupant_menu/list_row.vala +++ b/main/src/ui/occupant_menu/list_row.vala @@ -5,15 +5,22 @@ using Xmpp; namespace Dino.Ui.OccupantMenu { -[GtkTemplate (ui = "/im/dino/Dino/occupant_list_item.ui")] -public class ListRow : ListBoxRow { +public class ListRow : Object { - [GtkChild] private unowned AvatarImage image; - [GtkChild] public unowned Label name_label; + private Grid main_grid; + private AvatarImage image; + public Label name_label; public Conversation? conversation; public Jid? jid; + construct { + Builder builder = new Builder.from_resource("/im/dino/Dino/occupant_list_item.ui"); + main_grid = (Grid) builder.get_object("main_grid"); + image = (AvatarImage) builder.get_object("image"); + name_label = (Label) builder.get_object("name_label"); + } + public ListRow(StreamInteractor stream_interactor, Conversation conversation, Jid jid) { this.conversation = conversation; this.jid = jid; @@ -26,6 +33,10 @@ public class ListRow : ListBoxRow { name_label.label = text; image.set_text(c); } + + public Widget get_widget() { + return main_grid; + } } } diff --git a/main/src/ui/occupant_menu/view.vala b/main/src/ui/occupant_menu/view.vala index 428d2c99..35aa95f4 100644 --- a/main/src/ui/occupant_menu/view.vala +++ b/main/src/ui/occupant_menu/view.vala @@ -22,15 +22,14 @@ public class View : Popover { this.stream_interactor = stream_interactor; this.conversation = conversation; - this.show.connect(initialize_list); - invite_list.add(new ListRow.label("+", _("Invite")) {visible=true}); - list_box.add(invite_list); + invite_list.append(new ListRow.label("+", _("Invite")).get_widget()); + list_box.append(invite_list); invite_list.row_activated.connect(on_invite_clicked); stack.add_named(list_box, "list"); - add(stack); + set_child(stack); stack.visible_child_name = "list"; hide.connect(reset); @@ -46,12 +45,11 @@ public class View : Popover { private void initialize_list() { if (list == null) { list = new List(stream_interactor, conversation) { visible=true }; - list_box.add(list); - list_box.reorder_child(list, 0); + list_box.prepend(list); list.list_box.row_activated.connect((row) => { - ListRow list_row = row as ListRow; - show_menu(list_row.jid, list_row.name_label.label); + ListRow row_wrapper = list.row_wrappers[row.get_child()]; + show_menu(row_wrapper.jid, row_wrapper.name_label.label); }); } } @@ -71,37 +69,37 @@ public class View : Popover { if (real_jid != null) name += "\n<span font=\'8\'>%s</span>".printf(Markup.escape_text(real_jid.bare_jid.to_string())); Box header_box = new Box(Orientation.HORIZONTAL, 5) { visible=true }; - header_box.add(new Image.from_icon_name("pan-start-symbolic", IconSize.SMALL_TOOLBAR) { visible=true }); - header_box.add(new Label(name) { xalign=0, use_markup=true, hexpand=true, visible=true }); - Button header_button = new Button() { relief=ReliefStyle.NONE, visible=true }; - header_button.add(header_box); + header_box.append(new Image.from_icon_name("pan-start-symbolic") { visible=true }); + header_box.append(new Label(name) { xalign=0, use_markup=true, hexpand=true, visible=true }); + Button header_button = new Button() { has_frame=false, visible=true }; + header_button.child = header_box; - Box outer_box = new Box(Orientation.VERTICAL, 5) { margin=10, visible=true }; - outer_box.add(header_button); + Box outer_box = new Box(Orientation.VERTICAL, 5) { visible=true }; + outer_box.append(header_button); header_button.clicked.connect(show_list); - ModelButton private_button = new ModelButton() { active=true, text=_("Start private conversation"), visible=true }; - outer_box.add(private_button); + Button private_button = new Button.with_label(_("Start private conversation")) { visible=true }; + outer_box.append(private_button); private_button.clicked.connect(private_conversation_button_clicked); Jid? own_jid = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(conversation.counterpart, conversation.account); Xmpp.Xep.Muc.Role? role = stream_interactor.get_module(MucManager.IDENTITY).get_role(own_jid, conversation.account); if (role == Xmpp.Xep.Muc.Role.MODERATOR && stream_interactor.get_module(MucManager.IDENTITY).kick_possible(conversation.account, jid)) { - ModelButton kick_button = new ModelButton() { active=true, text=_("Kick"), visible=true }; - outer_box.add(kick_button); + Button kick_button = new Button.with_label(_("Kick")) { visible=true }; + outer_box.append(kick_button); kick_button.clicked.connect(kick_button_clicked); } if (stream_interactor.get_module(MucManager.IDENTITY).is_moderated_room(conversation.account, conversation.counterpart) && role == Xmpp.Xep.Muc.Role.MODERATOR){ if (stream_interactor.get_module(MucManager.IDENTITY).get_role(selected_jid, conversation.account) == Xmpp.Xep.Muc.Role.VISITOR) { - ModelButton voice_button = new ModelButton() { active=true, text=_("Grant write permission"), visible=true }; - outer_box.add(voice_button); + Button voice_button = new Button.with_label(_("Grant write permission")) { visible=true }; + outer_box.append(voice_button); voice_button.clicked.connect(() => voice_button_clicked("participant")); } else if (stream_interactor.get_module(MucManager.IDENTITY).get_role(selected_jid, conversation.account) == Xmpp.Xep.Muc.Role.PARTICIPANT){ - ModelButton voice_button = new ModelButton() { active=true, text=_("Revoke write permission"), visible=true }; - outer_box.add(voice_button); + Button voice_button = new Button.with_label(_("Revoke write permission")) { visible=true }; + outer_box.append(voice_button); voice_button.clicked.connect(() => voice_button_clicked("visitor")); } @@ -141,7 +139,7 @@ public class View : Popover { Gee.List<Account> acc_list = new ArrayList<Account>(Account.equals_func); acc_list.add(conversation.account); SelectContactDialog add_chat_dialog = new SelectContactDialog(stream_interactor, acc_list); - add_chat_dialog.set_transient_for((Window) get_toplevel()); + add_chat_dialog.set_transient_for((Window) get_root()); add_chat_dialog.title = _("Invite to Conference"); add_chat_dialog.ok_button.label = _("Invite"); add_chat_dialog.selected.connect((account, jid) => { diff --git a/main/src/ui/util/data_forms.vala b/main/src/ui/util/data_forms.vala index b36012de..53439149 100644 --- a/main/src/ui/util/data_forms.vala +++ b/main/src/ui/util/data_forms.vala @@ -36,18 +36,20 @@ public static Widget? get_data_form_field_widget(DataForms.DataForm.Field field) case DataForms.DataForm.Type.TEXT_PRIVATE: DataForms.DataForm.TextPrivateField text_private_field = field as DataForms.DataForm.TextPrivateField; Entry entry = new Entry() { text=text_private_field.value ?? "", valign=Align.CENTER, visible=true, visibility=false }; - entry.key_release_event.connect(() => { + var entry_key_events = new EventControllerKey(); + entry_key_events.key_released.connect(() => { text_private_field.value = entry.text; - return false; }); + entry.add_controller(entry_key_events); return entry; case DataForms.DataForm.Type.TEXT_SINGLE: DataForms.DataForm.TextSingleField text_single_field = field as DataForms.DataForm.TextSingleField; Entry entry = new Entry() { text=text_single_field.value ?? "", valign=Align.CENTER, visible=true }; - entry.key_release_event.connect(() => { + var entry_key_events = new EventControllerKey(); + entry_key_events.key_released.connect(() => { text_single_field.value = entry.text; - return false; }); + entry.add_controller(entry_key_events); return entry; default: return null; diff --git a/main/src/ui/util/helper.vala b/main/src/ui/util/helper.vala index 427c2d3a..98abb48e 100644 --- a/main/src/ui/util/helper.vala +++ b/main/src/ui/util/helper.vala @@ -134,29 +134,30 @@ public static string get_occupant_display_name(StreamInteractor stream_interacto return Dino.get_occupant_display_name(stream_interactor, conversation, jid, me_is_me ? _("Me") : null); } -public static void image_set_from_scaled_pixbuf(Image image, Gdk.Pixbuf pixbuf, int scale = 0, int width = 0, int height = 0) { - if (scale == 0) scale = image.scale_factor; - Cairo.Surface surface = Gdk.cairo_surface_create_from_pixbuf(pixbuf, scale, image.get_window()); - if (height == 0 && width != 0) { - height = (int) ((double) width / pixbuf.width * pixbuf.height); - } else if (height != 0 && width == 0) { - width = (int) ((double) height / pixbuf.height * pixbuf.width); - } - if (width != 0) { - Cairo.Surface surface_new = new Cairo.Surface.similar_image(surface, Cairo.Format.ARGB32, width, height); - Cairo.Context context = new Cairo.Context(surface_new); - context.scale((double) width * scale / pixbuf.width, (double) height * scale / pixbuf.height); - context.set_source_surface(surface, 0, 0); - context.get_source().set_filter(Cairo.Filter.BEST); - context.paint(); - surface = surface_new; - } - image.set_from_surface(surface); -} +// TODO this has no usages? +//public static void image_set_from_scaled_pixbuf(Image image, Gdk.Pixbuf pixbuf, int scale = 0, int width = 0, int height = 0) { +// if (scale == 0) scale = image.scale_factor; +// Cairo.Surface surface = Gdk.cairo_surface_create_from_pixbuf(pixbuf, scale, image.get_window()); +// if (height == 0 && width != 0) { +// height = (int) ((double) width / pixbuf.width * pixbuf.height); +// } else if (height != 0 && width == 0) { +// width = (int) ((double) height / pixbuf.height * pixbuf.width); +// } +// if (width != 0) { +// Cairo.Surface surface_new = new Cairo.Surface.similar_image(surface, Cairo.Format.ARGB32, width, height); +// Cairo.Context context = new Cairo.Context(surface_new); +// context.scale((double) width * scale / pixbuf.width, (double) height * scale / pixbuf.height); +// context.set_source_surface(surface, 0, 0); +// context.get_source().set_filter(Cairo.Filter.BEST); +// context.paint(); +// surface = surface_new; +// } +// image.set_from_surface(surface); +//} public static Gdk.RGBA get_label_pango_color(Label label, string css_color) { Gtk.CssProvider provider = force_color(label, css_color); - Gdk.RGBA color_rgba = label.get_style_context().get_color(StateFlags.NORMAL); + Gdk.RGBA color_rgba = label.get_style_context().get_color(); label.get_style_context().remove_provider(provider); return color_rgba; } @@ -176,7 +177,7 @@ private const string force_color_css = "%s { color: %s; }"; public static Gtk.CssProvider force_css(Gtk.Widget widget, string css) { var p = new Gtk.CssProvider(); try { - p.load_from_data(css); + p.load_from_data(css.data); widget.get_style_context().add_provider(p, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); } catch (GLib.Error err) { // handle err @@ -197,7 +198,7 @@ public static void force_error_color(Gtk.Widget widget, string selector = "*") { } public static bool is_dark_theme(Gtk.Widget widget) { - Gdk.RGBA bg = widget.get_style_context().get_color(StateFlags.NORMAL); + Gdk.RGBA bg = widget.get_style_context().get_color(); return (bg.red > 0.5 && bg.green > 0.5 && bg.blue > 0.5); } diff --git a/main/src/ui/util/label_hybrid.vala b/main/src/ui/util/label_hybrid.vala index b992f169..d880ba6f 100644 --- a/main/src/ui/util/label_hybrid.vala +++ b/main/src/ui/util/label_hybrid.vala @@ -3,15 +3,18 @@ using Gtk; namespace Dino.Ui.Util { -public class LabelHybrid : Stack { +public class LabelHybrid : Widget { + public Stack stack = new Stack(); public Label label = new Label("") { visible=true, max_width_chars=1, ellipsize=Pango.EllipsizeMode.END }; - protected Button button = new Button() { relief=ReliefStyle.NONE, visible=true }; + protected Button button = new Button() { has_frame=false, visible=true }; internal virtual void init(Widget widget) { - button.add(label); - add_named(button, "label"); - add_named(widget, "widget"); + this.layout_manager = new BinLayout(); + stack.insert_after(this, null); + button.child = label; + stack.add_named(button, "label"); + stack.add_named(widget, "widget"); button.clicked.connect(() => { show_widget(); @@ -19,12 +22,12 @@ public class LabelHybrid : Stack { } public void show_widget() { - visible_child_name = "widget"; - get_child_by_name("widget").grab_focus(); + stack.visible_child_name = "widget"; + stack.get_child_by_name("widget").grab_focus(); } public void show_label() { - visible_child_name = "label"; + stack.visible_child_name = "label"; } } @@ -73,18 +76,25 @@ public class EntryLabelHybrid : LabelHybrid { base.init(entry); update_label(); - entry.key_release_event.connect((event) => { - if (event.keyval == Gdk.Key.Return) { - show_label(); - } else { - set_label_label(entry.text); - } - return false; - }); - entry.focus_out_event.connect(() => { + var key_events = new EventControllerKey(); + key_events.key_released.connect(on_key_released); + entry.add_controller(key_events); + + var focus_events = new EventControllerFocus(); + focus_events.leave.connect(on_focus_leave); + entry.add_controller(focus_events); + } + + private void on_key_released(uint keyval) { + if (keyval == Gdk.Key.Return) { show_label(); - return false; - }); + } else { + set_label_label(entry.text); + } + } + + private void on_focus_leave() { + show_label(); } private void set_label_label(string value) { @@ -143,14 +153,18 @@ public class ComboBoxTextLabelHybrid : LabelHybrid { update_label(); show_label(); }); - combobox.focus_out_event.connect(() => { - update_label(); - show_label(); - return false; - }); button.clicked.connect(() => { combobox.popup(); }); + + var focus_events = new EventControllerFocus(); + focus_events.leave.connect(on_focus_leave); + combobox.add_controller(focus_events); + } + + private void on_focus_leave() { + update_label(); + show_label(); } private void update_label() { @@ -166,10 +180,10 @@ public class LabelHybridGroup { hybrids.add(hybrid); hybrid.notify["visible-child-name"].connect(() => { - if (hybrid.visible_child_name == "label") return; + if (hybrid.stack.visible_child_name == "label") return; foreach (LabelHybrid h in hybrids) { if (h != hybrid) { - h.set_visible_child_name("label"); + h.stack.set_visible_child_name("label"); } } }); diff --git a/main/src/ui/util/scaling_image.vala b/main/src/ui/util/scaling_image.vala index 477432c5..3dd3221f 100644 --- a/main/src/ui/util/scaling_image.vala +++ b/main/src/ui/util/scaling_image.vala @@ -2,7 +2,7 @@ using Gdk; using Gtk; namespace Dino.Ui { -class ScalingImage : Misc { +class ScalingImage : Widget { public int min_width { get; set; default = -1; } public int target_width { get; set; default = -1; } public int max_width { get; set; default = -1; } @@ -65,23 +65,28 @@ class ScalingImage : Misc { if (exact_height < min_height) exact_height = min_height; } - public override void size_allocate(Allocation allocation) { - if (max_width != -1) allocation.width = int.min(allocation.width, max_width); - if (max_height != -1) allocation.height = int.min(allocation.height, max_height); - allocation.height = int.max(allocation.height, min_height); - allocation.width = int.max(allocation.width, min_width); - double exact_width = allocation.width, exact_height = allocation.height; + public override void size_allocate(int width, int height, int baseline) { + if (max_width != -1) width = int.min(width, max_width); + if (max_height != -1) height = int.min(height, max_height); + height = int.max(height, min_height); + width = int.max(width, min_width); + double exact_width = width, exact_height = height; calculate_size(ref exact_width, ref exact_height); - base.size_allocate(allocation); - if (last_allocation_height != allocation.height || last_allocation_width != allocation.width || last_scale_factor != scale_factor) { - last_allocation_height = allocation.height; - last_allocation_width = allocation.width; + base.size_allocate(width, height, baseline); + if (last_allocation_height != height || last_allocation_width != width || last_scale_factor != scale_factor) { + last_allocation_height = height; + last_allocation_width = width; last_scale_factor = scale_factor; cached_surface = null; } } - public override bool draw(Cairo.Context ctx_in) { + public override void snapshot(Gtk.Snapshot snapshot) { + Cairo.Context context = snapshot.append_cairo(Graphene.Rect.alloc().init(0, 0, get_allocated_width(), get_allocated_height())); + draw(context); + } + + public bool draw(Cairo.Context ctx_in) { if (image == null) return false; Cairo.Context ctx = ctx_in; int width = this.get_allocated_width(), height = this.get_allocated_height(), base_factor = 1; @@ -148,33 +153,43 @@ class ScalingImage : Misc { return buffer; } - public override void get_preferred_width(out int minimum_width, out int natural_width) { - minimum_width = int.max(0, min_width); - double exact_width = -1, exact_height = -1; - calculate_size(ref exact_width, ref exact_height); - natural_width = (int) Math.ceil(exact_width); - } - - public override void get_preferred_height(out int minimum_height, out int natural_height) { - minimum_height = int.max(0, min_height); - double exact_width = -1, exact_height = -1; - calculate_size(ref exact_width, ref exact_height); - natural_height = (int) Math.ceil(exact_height); - } - - public override void get_preferred_height_for_width(int width, out int minimum_height, out int natural_height) { - double exact_width = width, exact_height = -1; - calculate_size(ref exact_width, ref exact_height); - natural_height = (int) Math.ceil(exact_height); - minimum_height = natural_height; + public override void measure(Orientation orientation, int for_size, out int minimum, out int natural, out int minimum_baseline, out int natural_baseline) { + double natural_width = -1, natural_height = -1; + calculate_size(ref natural_width, ref natural_height); + if (orientation == Orientation.HORIZONTAL) { + natural = (int) Math.ceil(natural_width); + } else { + natural = (int) Math.ceil(natural_height); + } + if (for_size == -1) { + minimum = 0; + } else { + if (orientation == Orientation.HORIZONTAL) { + double exact_width = -1, exact_height = for_size; + calculate_size(ref exact_width, ref exact_height); + minimum = int.max((int)Math.floor(exact_width), min_width); + } else { + double exact_width = for_size, exact_height = -1; + calculate_size(ref exact_width, ref exact_height); + minimum = int.max((int)Math.floor(exact_height), min_height); + } + } + minimum_baseline = natural_baseline = -1; } - public override void get_preferred_width_for_height(int height, out int minimum_width, out int natural_width) { - double exact_width = -1, exact_height = height; - calculate_size(ref exact_width, ref exact_height); - natural_width = (int) Math.ceil(exact_width); - minimum_width = natural_width; - } +// public override void get_preferred_height_for_width(int width, out int minimum_height, out int natural_height) { +// double exact_width = width, exact_height = -1; +// calculate_size(ref exact_width, ref exact_height); +// natural_height = (int) Math.ceil(exact_height); +// minimum_height = natural_height; +// } +// +// public override void get_preferred_width_for_height(int height, out int minimum_width, out int natural_width) { +// double exact_width = -1, exact_height = height; +// calculate_size(ref exact_width, ref exact_height); +// natural_width = (int) Math.ceil(exact_width); +// minimum_width = natural_width; +// } public override SizeRequestMode get_request_mode() { return SizeRequestMode.HEIGHT_FOR_WIDTH; diff --git a/main/src/ui/util/size_request_box.vala b/main/src/ui/util/size_request_box.vala index a2828262..7d7b6185 100644 --- a/main/src/ui/util/size_request_box.vala +++ b/main/src/ui/util/size_request_box.vala @@ -9,9 +9,13 @@ public class SizeRequestBox : Box { } } -public class SizeRequestBin : Bin { +public class SizeRequestBin : Widget { public SizeRequestMode size_request_mode { get; set; default = SizeRequestMode.CONSTANT_SIZE; } + construct { + this.layout_manager = new BinLayout(); + } + public override Gtk.SizeRequestMode get_request_mode() { return size_request_mode; } diff --git a/main/src/ui/util/sizing_bin.vala b/main/src/ui/util/sizing_bin.vala index 9c5ff4c7..939022a1 100644 --- a/main/src/ui/util/sizing_bin.vala +++ b/main/src/ui/util/sizing_bin.vala @@ -1,7 +1,7 @@ using Gtk; namespace Dino.Ui { -public class SizingBin : Bin { +public class SizingBin : Widget { public int min_width { get; set; default = -1; } public int target_width { get; set; default = -1; } public int max_width { get; set; default = -1; } @@ -9,27 +9,29 @@ public class SizingBin : Bin { public int target_height { get; set; default = -1; } public int max_height { get; set; default = -1; } - public override void size_allocate(Allocation allocation) { - if (max_height != -1) allocation.height = int.min(allocation.height, max_height); - if (max_width != -1) allocation.width = int.min(allocation.width, max_width); - base.size_allocate(allocation); + construct { + layout_manager = new BinLayout(); } - public override void get_preferred_width(out int minimum_width, out int natural_width) { - base.get_preferred_width(out minimum_width, out natural_width); - if (min_width != -1) minimum_width = int.max(minimum_width, min_width); - if (max_width != -1) natural_width = int.min(natural_width, max_width); - if (target_width != -1) natural_width = int.max(natural_width, target_width); - natural_width = int.max(natural_width, minimum_width); + public override void size_allocate(int width, int height, int baseline) { + if (max_height != -1) height = int.min(height, max_height); + if (max_width != -1) width = int.min(width, max_width); + base.size_allocate(width, height, baseline); } - public override void get_preferred_height_for_width(int width, out int minimum_height, out int natural_height) { - base.get_preferred_height_for_width(width, out minimum_height, out natural_height); - if (min_height != -1) minimum_height = int.max(minimum_height, min_height); - if (max_height != -1) natural_height = int.min(natural_height, max_height); - if (target_height != -1) natural_height = int.max(natural_height, target_height); - natural_height = int.max(natural_height, minimum_height); + public override void measure(Orientation orientation, int for_size, out int minimum, out int natural, out int minimum_baseline, out int natural_baseline) { + base.measure(orientation, for_size, out minimum, out natural, out minimum_baseline, out natural_baseline); + if (orientation == Orientation.HORIZONTAL) { + if (min_width != -1) minimum = int.max(minimum, min_width); + if (max_width != -1) natural = int.min(natural, max_width); + if (target_width != -1) natural = int.max(natural, target_width); + natural = int.max(natural, minimum); + } else { + if (min_height != -1) minimum = int.max(minimum, min_height); + if (max_height != -1) natural = int.min(natural, max_height); + if (target_height != -1) natural = int.max(natural, target_height); + natural = int.max(natural, minimum); + } } - } } diff --git a/main/vapi/emojichooser.vapi b/main/vapi/emojichooser.vapi deleted file mode 100644 index f29e52d3..00000000 --- a/main/vapi/emojichooser.vapi +++ /dev/null @@ -1,7 +0,0 @@ -namespace Dino { - [CCode (cheader_filename = "emojichooser.h")] - class EmojiChooser : Gtk.Popover { - public signal void emoji_picked(string text); - public EmojiChooser(); - } -} diff --git a/plugins/http-files/CMakeLists.txt b/plugins/http-files/CMakeLists.txt index 2da46731..b29cfda3 100644 --- a/plugins/http-files/CMakeLists.txt +++ b/plugins/http-files/CMakeLists.txt @@ -11,7 +11,7 @@ find_packages(HTTP_FILES_PACKAGES REQUIRED GLib GModule GObject - GTK3 + GTK4 ${Soup} ) diff --git a/plugins/ice/CMakeLists.txt b/plugins/ice/CMakeLists.txt index 4783cea6..57ca69e3 100644 --- a/plugins/ice/CMakeLists.txt +++ b/plugins/ice/CMakeLists.txt @@ -5,7 +5,8 @@ find_packages(ICE_PACKAGES REQUIRED GLib GModule GObject - GTK3 + GIO + GDKPixbuf2 ) vala_precompile(ICE_VALA_C diff --git a/plugins/omemo/CMakeLists.txt b/plugins/omemo/CMakeLists.txt index 195001cb..dc9a93b0 100644 --- a/plugins/omemo/CMakeLists.txt +++ b/plugins/omemo/CMakeLists.txt @@ -9,7 +9,7 @@ find_packages(OMEMO_PACKAGES REQUIRED GLib GModule GObject - GTK3 + GTK4 ) set(RESOURCE_LIST @@ -53,7 +53,6 @@ SOURCES src/protocol/stream_module.vala src/ui/account_settings_entry.vala - src/ui/account_settings_widget.vala src/ui/bad_messages_populator.vala src/ui/call_encryption_entry.vala src/ui/contact_details_provider.vala diff --git a/plugins/omemo/data/contact_details_dialog.ui b/plugins/omemo/data/contact_details_dialog.ui index 62aded6b..fdb9f1cf 100644 --- a/plugins/omemo/data/contact_details_dialog.ui +++ b/plugins/omemo/data/contact_details_dialog.ui @@ -1,28 +1,26 @@ <?xml version="1.0" encoding="UTF-8"?> <interface> + <requires lib="gtk" version="4.0"/> <template class="DinoPluginsOmemoContactDetailsDialog"> <property name="modal">True</property> <property name="resizable">False</property> - <child internal-child="vbox"> + <child internal-child="content_area"> <object class="GtkBox"> - <property name="visible">True</property> - <property name="margin">12</property> + <property name="margin-start">12</property> + <property name="margin-end">12</property> + <property name="margin-top">12</property> + <property name="margin-bottom">12</property> <property name="spacing">12</property> <child> <object class="GtkFrame"> - <property name="visible">True</property> - <child> + <property name="child"> <object class="GtkListBox"> - <property name="visible">True</property> <property name="selection-mode">none</property> <child> <object class="GtkListBoxRow"> - <property name="visible">True</property> - <property name="activatable">False</property> - <child> + <property name="activatable">0</property> + <property name="child"> <object class="GtkBox"> - <property name="visible">True</property> - <property name="orientation">horizontal</property> <property name="margin-start">20</property> <property name="margin-end">20</property> <property name="margin-top">14</property> @@ -30,27 +28,25 @@ <property name="spacing">40</property> <child> <object class="GtkBox"> - <property name="visible">True</property> <property name="orientation">vertical</property> - <property name="hexpand">True</property> + <property name="hexpand">1</property> <child> <object class="GtkLabel" id="automatically_accept_new_label"> - <property name="visible">True</property> <property name="halign">start</property> <attributes> - <attribute name="scale" value="1.1"/> + <attribute name="scale" value="1.1"></attribute> </attributes> </object> </child> <child> <object class="GtkLabel" id="automatically_accept_new_descr"> - <property name="visible">True</property> <property name="max_width_chars">1</property> - <property name="expand">True</property> + <property name="hexpand">1</property> + <property name="vexpand">1</property> <property name="xalign">0</property> - <property name="wrap">True</property> + <property name="wrap">1</property> <attributes> - <attribute name="scale" value="0.8"/> + <attribute name="scale" value="0.8"></attribute> </attributes> <style> <class name="dim-label"/> @@ -61,92 +57,73 @@ </child> <child> <object class="GtkSwitch" id="auto_accept_switch"> - <property name="visible">True</property> <property name="halign">end</property> <property name="valign">center</property> </object> </child> </object> - </child> + </property> </object> </child> </object> - </child> + </property> </object> </child> <child> <object class="GtkBox" id="own_fingerprint_container"> - <property name="visible">False</property> + <property name="visible">0</property> <property name="orientation">vertical</property> <property name="spacing">2</property> <child> <object class="GtkLabel" id="own_key_label"> - <property name="visible">True</property> <property name="halign">start</property> <attributes> - <attribute name="weight" value="PANGO_WEIGHT_BOLD"/> + <attribute name="weight" value="PANGO_WEIGHT_BOLD"></attribute> </attributes> </object> </child> <child> <object class="GtkFrame"> - <property name="visible">True</property> - <child> + <property name="child"> <object class="GtkListBox"> - <property name="visible">True</property> <property name="selection-mode">none</property> <child> <object class="GtkListBoxRow"> - <property name="visible">True</property> - <property name="activatable">False</property> - <property name="hexpand">True</property> - <child> + <property name="activatable">0</property> + <property name="hexpand">1</property> + <property name="child"> <object class="GtkBox"> - <property name="visible">True</property> <property name="margin-start">20</property> <property name="margin-end">20</property> <property name="margin-top">14</property> <property name="spacing">40</property> <property name="margin-bottom">14</property> - <property name="orientation">horizontal</property> - <property name="hexpand">True</property> + <property name="hexpand">1</property> <child> <object class="GtkLabel" id="own_fingerprint_label"> - <property name="visible">True</property> <property name="halign">start</property> <property name="justify">right</property> - <property name="hexpand">True</property> + <property name="hexpand">1</property> </object> </child> <child> <object class="GtkBox"> - <property name="visible">True</property> - <property name="orientation">horizontal</property> - <property name="hexpand">True</property> + <property name="hexpand">1</property> <property name="spacing">5</property> <child> - <object class="GtkButton" id="show_qrcode_button"> - <property name="visible">True</property> + <object class="GtkMenuButton" id="show_qrcode_button"> + <property name="icon-name">dino-qr-code-symbolic</property> <property name="halign">start</property> - <property name="hexpand">True</property> - <child> - <object class="GtkImage"> - <property name="visible">True</property> - <property name="icon-name">dino-qr-code-symbolic</property> - <property name="icon-size">1</property> - </object> - </child> + <property name="hexpand">1</property> </object> </child> <child> <object class="GtkButton" id="copy_button"> - <property name="visible">True</property> <property name="halign">end</property> - <property name="hexpand">True</property> + <property name="hexpand">1</property> <child> <object class="GtkImage"> - <property name="visible">True</property> - <property name="icon-size">1</property> + <property name="icon-size">normal</property> <property name="icon-name">edit-copy-symbolic</property> </object> </child> @@ -155,114 +132,99 @@ </object> </child> </object> - </child> + </property> </object> </child> </object> - </child> + </property> </object> </child> </object> </child> <child> <object class="GtkBox" id="new_keys_container"> - <property name="visible">False</property> + <property name="visible">0</property> <property name="orientation">vertical</property> <property name="spacing">2</property> <child> <object class="GtkLabel" id="new_keys_label"> - <property name="visible">True</property> <property name="halign">start</property> <attributes> - <attribute name="weight" value="PANGO_WEIGHT_BOLD"/> + <attribute name="weight" value="PANGO_WEIGHT_BOLD"></attribute> </attributes> </object> </child> <child> <object class="GtkFrame"> - <property name="visible">True</property> - <child> + <property name="child"> <object class="GtkScrolledWindow"> <property name="hscrollbar_policy">never</property> - <property name="vscrollbar_policy">automatic</property> - <property name="visible">True</property> - <property name="propagate_natural_height">True</property> - <child> + <property name="propagate_natural_height">1</property> + <property name="child"> <object class="GtkListBox" id="new_keys_listbox"> - <property name="visible">True</property> <property name="selection-mode">none</property> </object> - </child> + </property> </object> - </child> + </property> </object> </child> </object> </child> <child> <object class="GtkBox" id="keys_container"> - <property name="visible">False</property> + <property name="visible">0</property> <property name="orientation">vertical</property> <property name="spacing">2</property> <child> <object class="GtkLabel" id="associated_keys_label"> - <property name="visible">True</property> <property name="halign">start</property> <attributes> - <attribute name="weight" value="PANGO_WEIGHT_BOLD"/> + <attribute name="weight" value="PANGO_WEIGHT_BOLD"></attribute> </attributes> </object> </child> <child> <object class="GtkFrame"> - <property name="visible">True</property> - <child> + <property name="child"> <object class="GtkScrolledWindow"> <property name="hscrollbar_policy">never</property> - <property name="vscrollbar_policy">automatic</property> - <property name="visible">True</property> - <property name="propagate_natural_height">True</property> - <child> + <property name="propagate_natural_height">1</property> + <property name="child"> <object class="GtkListBox" id="keys_listbox"> - <property name="visible">True</property> <property name="selection-mode">none</property> </object> - </child> + </property> </object> - </child> + </property> </object> </child> </object> </child> <child> <object class="GtkExpander" id="inactive_keys_expander"> - <property name="visible">False</property> + <property name="visible">0</property> <child type="label"> <object class="GtkLabel" id="inactive_expander_label"> - <property name="visible">True</property> <attributes> - <attribute name="weight" value="PANGO_WEIGHT_BOLD"/> + <attribute name="weight" value="PANGO_WEIGHT_BOLD"></attribute> </attributes> </object> </child> <child> <object class="GtkFrame"> - <property name="visible">True</property> <property name="margin-top">2</property> - <child> + <property name="child"> <object class="GtkScrolledWindow"> <property name="hscrollbar_policy">never</property> - <property name="vscrollbar_policy">automatic</property> - <property name="visible">True</property> - <property name="propagate_natural_height">True</property> - <child> + <property name="propagate_natural_height">1</property> + <property name="child"> <object class="GtkListBox" id="inactive_keys_listbox"> - <property name="visible">True</property> <property name="selection-mode">none</property> </object> - </child> + </property> </object> - </child> + </property> </object> </child> </object> @@ -271,19 +233,18 @@ </child> </template> <object class="GtkPopover" id="qrcode_popover"> - <property name="visible">False</property> - <property name="relative-to">show_qrcode_button</property> <property name="position">left</property> - <property name="modal">True</property> - <child> + <property name="child"> <object class="GtkBox"> <property name="visible">True</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> - <object class="GtkImage" id="qrcode_image"> - <property name="visible">True</property> - </object> + <object class="GtkImage" id="qrcode_image"/> </child> </object> - </child> + </property> </object> </interface> diff --git a/plugins/omemo/data/manage_key_dialog.ui b/plugins/omemo/data/manage_key_dialog.ui index dcec4c90..37f158da 100644 --- a/plugins/omemo/data/manage_key_dialog.ui +++ b/plugins/omemo/data/manage_key_dialog.ui @@ -1,164 +1,149 @@ <?xml version="1.0" encoding="UTF-8"?> <interface> + <requires lib="gtk" version="4.0"/> <template class="DinoPluginsOmemoManageKeyDialog"> <property name="modal">True</property> <property name="resizable">False</property> <child type="titlebar"> <object class="GtkHeaderBar" id="headerbar"> - <property name="visible">True</property> - <property name="show_close_button">False</property> + <property name="show_title_buttons">0</property> <child> - <object class="GtkButton" id="cancel_button"> - <property name="sensitive">True</property> - <property name="visible">True</property> - </object> - <packing> - <property name="pack_type">start</property> - </packing> + <object class="GtkButton" id="cancel_button"/> </child> - <child> + <child type="end"> <object class="GtkButton" id="ok_button"> - <property name="has_default">True</property> - <property name="can_default">True</property> - <property name="sensitive">False</property> - <property name="visible">True</property> + <property name="sensitive">0</property> <style> <class name="suggested-action"/> </style> </object> - <packing> - <property name="pack_type">end</property> - </packing> </child> </object> </child> - <child internal-child="vbox"> + <child internal-child="content_area"> <object class="GtkBox"> - <property name="visible">True</property> <child> <object class="GtkStack" id="manage_stack"> - <property name="visible">True</property> <property name="transition-type">slide-left-right</property> <child> - <object class="GtkBox" id="main_screen"> - <property name="visible">True</property> - <property name="margin">12</property> - <property name="spacing">12</property> - <property name="orientation">vertical</property> - <property name="valign">center</property> - <child> - <object class="GtkLabel" id="main_desc_label"> - <property name="visible">True</property> - <property name="wrap">True</property> - <property name="xalign">0</property> - <property name="max-width-chars">1</property> - </object> - </child> - <child> - <object class="GtkFrame"> - <property name="visible">True</property> + <object class="GtkStackPage"> + <property name="name">main</property> + <property name="child"> + <object class="GtkBox" id="main_screen"> + <property name="margin-start">12</property> + <property name="margin-end">12</property> + <property name="margin-top">12</property> + <property name="margin-bottom">12</property> + <property name="spacing">12</property> + <property name="orientation">vertical</property> + <property name="valign">center</property> + <child> + <object class="GtkLabel" id="main_desc_label"> + <property name="wrap">1</property> + <property name="xalign">0</property> + <property name="max-width-chars">1</property> + </object> + </child> <child> - <object class="GtkListBox" id="main_action_list"> - <property name="visible">True</property> - <property name="selection-mode">none</property> + <object class="GtkFrame"> + <property name="child"> + <object class="GtkListBox" id="main_action_list"> + <property name="selection-mode">none</property> + </object> + </property> </object> </child> </object> - </child> + </property> </object> - <packing> - <property name="name">main</property> - </packing> </child> <child> - <object class="GtkBox" id="verify_screen"> - <property name="visible">True</property> - <property name="margin">12</property> - <property name="spacing">12</property> - <property name="orientation">vertical</property> - <property name="valign">center</property> - <child> - <object class="GtkLabel" id="compare_fingerprint_label"> - <property name="visible">True</property> - <property name="wrap">True</property> - <property name="xalign">0.5</property> - <property name="max-width-chars">45</property> - </object> - </child> - <child> - <object class="GtkBox"> - <property name="visible">True</property> - <property name="orientation">vertical</property> - <property name="margin-left">12</property> - <property name="margin-right">12</property> + <object class="GtkStackPage"> + <property name="name">verify</property> + <property name="child"> + <object class="GtkBox" id="verify_screen"> + <property name="margin-start">12</property> + <property name="margin-end">12</property> + <property name="margin-top">12</property> + <property name="margin-bottom">12</property> <property name="spacing">12</property> - <property name="hexpand">False</property> - <property name="halign">center</property> - <child> - <object class="GtkLabel" id="verify_label"> - <property name="visible">True</property> - <property name="margin-top">12</property> - <property name="margin-bottom">12</property> - <property name="justify">right</property> - </object> - </child> + <property name="orientation">vertical</property> + <property name="valign">center</property> <child> - <object class="GtkButton" id="verify_no_button"> - <property name="visible">True</property> - <property name="hexpand">True</property> + <object class="GtkLabel" id="compare_fingerprint_label"> + <property name="wrap">1</property> + <property name="max-width-chars">45</property> </object> </child> <child> - <object class="GtkButton" id="verify_yes_button"> - <property name="visible">True</property> - <property name="hexpand">True</property> + <object class="GtkBox"> + <property name="orientation">vertical</property> + <property name="margin-start">12</property> + <property name="margin-end">12</property> + <property name="spacing">12</property> + <property name="hexpand">0</property> + <property name="halign">center</property> + <child> + <object class="GtkLabel" id="verify_label"> + <property name="margin-top">12</property> + <property name="margin-bottom">12</property> + <property name="justify">right</property> + </object> + </child> + <child> + <object class="GtkButton" id="verify_no_button"> + <property name="hexpand">1</property> + </object> + </child> + <child> + <object class="GtkButton" id="verify_yes_button"> + <property name="hexpand">1</property> + </object> + </child> </object> </child> </object> - </child> + </property> </object> - <packing> - <property name="name">verify</property> - </packing> </child> <child> - <object class="GtkBox" id="confirm_screen"> - <property name="visible">True</property> - <property name="margin">12</property> - <property name="spacing">12</property> - <property name="orientation">vertical</property> - <property name="valign">center</property> - <child> - <object class="GtkImage" id="confirm_image"> - <property name="visible">True</property> - </object> - </child> - <child> - <object class="GtkLabel" id="confirm_title_label"> - <property name="visible">True</property> - <attributes> - <attribute name="scale" value="1.1"/> - </attributes> - </object> - </child> - <child> - <object class="GtkLabel" id="confirm_desc_label"> - <property name="visible">True</property> - <property name="justify">center</property> - <property name="wrap">True</property> - <property name="max-width-chars">40</property> - <attributes> - <attribute name="scale" value="0.8"/> - </attributes> - <style> - <class name="dim-label"/> - </style> + <object class="GtkStackPage"> + <property name="name">confirm</property> + <property name="child"> + <object class="GtkBox" id="confirm_screen"> + <property name="margin-start">12</property> + <property name="margin-end">12</property> + <property name="margin-top">12</property> + <property name="margin-bottom">12</property> + <property name="spacing">12</property> + <property name="orientation">vertical</property> + <property name="valign">center</property> + <child> + <object class="GtkImage" id="confirm_image"/> + </child> + <child> + <object class="GtkLabel" id="confirm_title_label"> + <attributes> + <attribute name="scale" value="1.1"></attribute> + </attributes> + </object> + </child> + <child> + <object class="GtkLabel" id="confirm_desc_label"> + <property name="justify">center</property> + <property name="wrap">1</property> + <property name="max-width-chars">40</property> + <attributes> + <attribute name="scale" value="0.8"></attribute> + </attributes> + <style> + <class name="dim-label"/> + </style> + </object> + </child> </object> - </child> + </property> </object> - <packing> - <property name="name">confirm</property> - </packing> </child> </object> </child> diff --git a/plugins/omemo/src/logic/trust_manager.vala b/plugins/omemo/src/logic/trust_manager.vala index 20076a43..cae88eef 100644 --- a/plugins/omemo/src/logic/trust_manager.vala +++ b/plugins/omemo/src/logic/trust_manager.vala @@ -109,7 +109,7 @@ public class TrustManager { // TODO: Handling of files - 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 && device_id != 0) { Jid jid = content_item.jid; diff --git a/plugins/omemo/src/ui/account_settings_entry.vala b/plugins/omemo/src/ui/account_settings_entry.vala index 3866febe..8736260b 100644 --- a/plugins/omemo/src/ui/account_settings_entry.vala +++ b/plugins/omemo/src/ui/account_settings_entry.vala @@ -1,26 +1,58 @@ +using Dino.Entities; +using Gtk; + namespace Dino.Plugins.Omemo { public class AccountSettingsEntry : Plugins.AccountSettingsEntry { private Plugin plugin; + private Account account; + + private Box box = new Box(Orientation.HORIZONTAL, 0); + private Label fingerprint = new Label("...") { xalign=0 }; + private Button btn = new Button.from_icon_name("view-list-symbolic") { has_frame=false, valign=Align.CENTER, visible=false }; + + public override string id { get { return "omemo_identity_key"; }} + + public override string name { get { return "OMEMO"; }} public AccountSettingsEntry(Plugin plugin) { this.plugin = plugin; - } - public override string id { get { - return "omemo_identity_key"; - }} + Border border = new Button().get_style_context().get_padding(); + fingerprint.margin_top = border.top + 1; + fingerprint.margin_start = border.left + 1; + fingerprint.visible = true; + box.append(fingerprint); - public override string name { get { - return "OMEMO"; - }} + btn.clicked.connect(() => { + activated(); + ContactDetailsDialog dialog = new ContactDetailsDialog(plugin, account, account.bare_jid); + dialog.set_transient_for((Window) box.get_root()); + dialog.present(); + }); + // TODO expand=false? + box.append(btn); + } + + public override Object? get_widget(WidgetType type) { + if (type != WidgetType.GTK4) return null; + return box; + } - public override Plugins.AccountSettingsWidget? get_widget(WidgetType type) { - if (type == WidgetType.GTK) { - return new AccountSettingWidget(plugin); + public override void set_account(Account account) { + this.account = account; + btn.visible = false; + Qlite.Row? row = plugin.db.identity.row_with(plugin.db.identity.account_id, account.id).inner; + if (row == null) { + fingerprint.set_markup("%s\n<span font='8'>%s</span>".printf(_("Own fingerprint"), _("Will be generated on first connection"))); + } else { + string res = fingerprint_markup(fingerprint_from_base64(((!)row)[plugin.db.identity.identity_key_public_base64])); + fingerprint.set_markup("%s\n<span font_family='monospace' font='8'>%s</span>".printf(_("Own fingerprint"), res)); + btn.visible = true; } - return null; } + + public override void deactivate() { } } }
\ No newline at end of file diff --git a/plugins/omemo/src/ui/account_settings_widget.vala b/plugins/omemo/src/ui/account_settings_widget.vala deleted file mode 100644 index cc562221..00000000 --- a/plugins/omemo/src/ui/account_settings_widget.vala +++ /dev/null @@ -1,54 +0,0 @@ -using Gtk; -using Dino.Entities; - -namespace Dino.Plugins.Omemo { - -public class AccountSettingWidget : Plugins.AccountSettingsWidget, Box { - private Plugin plugin; - private Label fingerprint; - private Account account; - private Button btn; - - public AccountSettingWidget(Plugin plugin) { - this.plugin = plugin; - - fingerprint = new Label("..."); - fingerprint.xalign = 0; - Border border = new Button().get_style_context().get_padding(StateFlags.NORMAL); - fingerprint.margin_top = border.top + 1; - fingerprint.margin_start = border.left + 1; - fingerprint.visible = true; - pack_start(fingerprint); - - btn = new Button(); - btn.image = new Image.from_icon_name("view-list-symbolic", IconSize.BUTTON); - btn.relief = ReliefStyle.NONE; - btn.visible = false; - btn.valign = Align.CENTER; - btn.clicked.connect(() => { - activated(); - ContactDetailsDialog dialog = new ContactDetailsDialog(plugin, account, account.bare_jid); - dialog.set_transient_for((Window) get_toplevel()); - dialog.present(); - }); - pack_start(btn, false); - } - - public void set_account(Account account) { - this.account = account; - btn.visible = false; - Qlite.Row? row = plugin.db.identity.row_with(plugin.db.identity.account_id, account.id).inner; - if (row == null) { - fingerprint.set_markup("%s\n<span font='8'>%s</span>".printf(_("Own fingerprint"), _("Will be generated on first connection"))); - } else { - string res = fingerprint_markup(fingerprint_from_base64(((!)row)[plugin.db.identity.identity_key_public_base64])); - fingerprint.set_markup("%s\n<span font_family='monospace' font='8'>%s</span>".printf(_("Own fingerprint"), res)); - btn.visible = true; - } - } - - public void deactivate() { - } -} - -} diff --git a/plugins/omemo/src/ui/bad_messages_populator.vala b/plugins/omemo/src/ui/bad_messages_populator.vala index bd2474f2..ca0bd35d 100644 --- a/plugins/omemo/src/ui/bad_messages_populator.vala +++ b/plugins/omemo/src/ui/bad_messages_populator.vala @@ -123,7 +123,7 @@ public class BadMessageItem : Plugins.MetaConversationItem { this.badness_type = badness_type; } - public override Object? get_widget(Plugins.WidgetType widget_type) { + public override Object? get_widget(Plugins.ConversationItemWidgetInterface outer, Plugins.WidgetType widget_type) { return new BadMessagesWidget(plugin, conversation, problem_jid, badness_type); } @@ -161,12 +161,12 @@ public class BadMessagesWidget : Box { } Label label = new Label(warning_text) { margin_start=70, margin_end=70, justify=Justification.CENTER, use_markup=true, selectable=true, wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR, hexpand=true, visible=true }; label.get_style_context().add_class("dim-label"); - this.add(label); + this.append(label); label.activate_link.connect(() => { if (badness_type == BadnessType.UNTRUSTED) { ContactDetailsDialog dialog = new ContactDetailsDialog(plugin, conversation.account, jid); - dialog.set_transient_for((Window) get_toplevel()); + dialog.set_transient_for((Window) get_root()); dialog.present(); } diff --git a/plugins/omemo/src/ui/contact_details_dialog.vala b/plugins/omemo/src/ui/contact_details_dialog.vala index b4d6d8f0..80943824 100644 --- a/plugins/omemo/src/ui/contact_details_dialog.vala +++ b/plugins/omemo/src/ui/contact_details_dialog.vala @@ -37,10 +37,12 @@ public class ContactDetailsDialog : Gtk.Dialog { [GtkChild] private unowned ListBox inactive_keys_listbox; [GtkChild] private unowned Switch auto_accept_switch; [GtkChild] private unowned Button copy_button; - [GtkChild] private unowned Button show_qrcode_button; + [GtkChild] private unowned MenuButton show_qrcode_button; [GtkChild] private unowned Image qrcode_image; [GtkChild] private unowned Popover qrcode_popover; + private ArrayList<Widget> new_keys_listbox_children = new ArrayList<Widget>(); + construct { // If we set the strings in the .ui file, they don't get translated title = _("OMEMO Key Management"); @@ -59,7 +61,7 @@ public class ContactDetailsDialog : Gtk.Dialog { this.jid = jid; if (Environment.get_variable("GTK_CSD") != "0") { - ((HeaderBar) get_header_bar()).set_subtitle(jid.bare_jid.to_string()); +// ((HeaderBar) get_header_bar()).set_subtitle(jid.bare_jid.to_string()); } keys_listbox.row_activated.connect(on_key_entry_clicked); @@ -89,7 +91,7 @@ public class ContactDetailsDialog : Gtk.Dialog { string fingerprint = fingerprint_from_base64(own_b64); own_fingerprint_label.set_markup(fingerprint_markup(fingerprint)); - copy_button.clicked.connect(() => {Clipboard.get_default(get_display()).set_text(fingerprint, fingerprint.length);}); + copy_button.clicked.connect(() => { copy_button.get_clipboard().set_text(fingerprint); }); int sid = plugin.db.identity.row_with(plugin.db.identity.account_id, account.id)[plugin.db.identity.device_id]; var iri_query = @"omemo-sid-$(sid)=$(fingerprint)"; @@ -104,12 +106,12 @@ public class ContactDetailsDialog : Gtk.Dialog { const int MODULE_SIZE_PX = 4; // arbitrary var qr_pixbuf = new QRcode(iri, 2) .to_pixbuf(MODULE_SIZE_PX * qrcode_image.scale_factor); - qrcode_image.set_from_surface( - Gdk.cairo_surface_create_from_pixbuf(qr_pixbuf,0,get_window())); - qrcode_image.margin = QUIET_ZONE_MODULES*MODULE_SIZE_PX; + qrcode_image.set_from_pixbuf(qr_pixbuf); + qrcode_image.margin_top = qrcode_image.margin_end = + qrcode_image.margin_bottom = qrcode_image.margin_start = QUIET_ZONE_MODULES*MODULE_SIZE_PX; qrcode_popover.get_style_context().add_class("qrcode-container"); - show_qrcode_button.clicked.connect(qrcode_popover.popup); + show_qrcode_button.popover = qrcode_popover; } new_keys_listbox.set_header_func(header_function); @@ -196,10 +198,10 @@ public class ContactDetailsDialog : Gtk.Dialog { if (device[plugin.db.identity_meta.now_active]) { keys_container.visible = true; - keys_listbox.add(fingerprint_row); + keys_listbox.append(fingerprint_row); } else { inactive_keys_expander.visible=true; - inactive_keys_listbox.add(fingerprint_row); + inactive_keys_listbox.append(fingerprint_row); } displayed_ids.add(device[plugin.db.identity_meta.device_id]); } @@ -210,7 +212,7 @@ public class ContactDetailsDialog : Gtk.Dialog { Row updated_device = plugin.db.identity_meta.get_device(fingerprint_row.row[plugin.db.identity_meta.identity_id], fingerprint_row.row[plugin.db.identity_meta.address_name], fingerprint_row.row[plugin.db.identity_meta.device_id]); ManageKeyDialog manage_dialog = new ManageKeyDialog(updated_device, plugin.db); - manage_dialog.set_transient_for((Gtk.Window) get_toplevel()); + manage_dialog.set_transient_for((Gtk.Window) get_root()); manage_dialog.present(); manage_dialog.response.connect((response) => { fingerprint_row.update_trust_state(response, fingerprint_row.row[plugin.db.identity_meta.now_active]); @@ -257,12 +259,12 @@ public class ContactDetailsDialog : Gtk.Dialog { Box box = new Box(Gtk.Orientation.HORIZONTAL, 40) { visible = true, margin_start = 20, margin_end = 20, margin_top = 14, margin_bottom = 14, hexpand = true }; Button accept_button = new Button() { visible = true, valign = Align.CENTER, hexpand = true }; - accept_button.add(new Image.from_icon_name("emblem-ok-symbolic", IconSize.BUTTON) { visible=true }); // using .image = sets .image-button. Together with .suggested/destructive action that breaks the button Adwaita + accept_button.set_icon_name("emblem-ok-symbolic"); // using .image = sets .image-button. Together with .suggested/destructive action that breaks the button Adwaita accept_button.get_style_context().add_class("suggested-action"); accept_button.tooltip_text = _("Accept key"); Button reject_button = new Button() { visible = true, valign = Align.CENTER, hexpand = true }; - reject_button.add(new Image.from_icon_name("action-unavailable-symbolic", IconSize.BUTTON) { visible=true }); + reject_button.set_icon_name("action-unavailable-symbolic"); reject_button.get_style_context().add_class("destructive-action"); reject_button.tooltip_text = _("Reject key"); @@ -270,35 +272,38 @@ public class ContactDetailsDialog : Gtk.Dialog { plugin.trust_manager.set_device_trust(account, jid, device[plugin.db.identity_meta.device_id], TrustLevel.TRUSTED); add_fingerprint(device, TrustLevel.TRUSTED); new_keys_listbox.remove(lbr); - if (new_keys_listbox.get_children().length() < 1) new_keys_container.visible = false; + new_keys_listbox_children.remove(lbr); + if (new_keys_listbox_children.size < 1) new_keys_container.visible = false; }); reject_button.clicked.connect(() => { plugin.trust_manager.set_device_trust(account, jid, device[plugin.db.identity_meta.device_id], TrustLevel.UNTRUSTED); add_fingerprint(device, TrustLevel.UNTRUSTED); new_keys_listbox.remove(lbr); - if (new_keys_listbox.get_children().length() < 1) new_keys_container.visible = false; + new_keys_listbox_children.remove(lbr); + if (new_keys_listbox_children.size < 1) new_keys_container.visible = false; }); string res = fingerprint_markup(fingerprint_from_base64(device[plugin.db.identity_meta.identity_key_public_base64])); Label fingerprint_label = new Label(res) { use_markup=true, justify=Justification.RIGHT, visible=true, halign = Align.START, valign = Align.CENTER, hexpand = false }; - box.add(fingerprint_label); + box.append(fingerprint_label); Box control_box = new Box(Gtk.Orientation.HORIZONTAL, 0) { visible = true, hexpand = true }; - control_box.add(accept_button); - control_box.add(reject_button); + control_box.append(accept_button); + control_box.append(reject_button); control_box.get_style_context().add_class("linked"); // .linked: Visually link the accept / reject buttons - box.add(control_box); + box.append(control_box); - lbr.add(box); - new_keys_listbox.add(lbr); + lbr.set_child(box); + new_keys_listbox.append(lbr); + new_keys_listbox_children.add(lbr); displayed_ids.add(device[plugin.db.identity_meta.device_id]); } } public class FingerprintRow : ListBoxRow { - private Image trust_image = new Image() { visible = true, halign = Align.END, icon_size = IconSize.BUTTON }; + private Image trust_image = new Image() { visible = true, halign = Align.END }; private Label fingerprint_label = new Label("") { use_markup=true, justify=Justification.RIGHT, visible=true, halign = Align.START, valign = Align.CENTER, hexpand = false }; private Label trust_label = new Label(null) { visible = true, hexpand = true, xalign = 0 }; @@ -308,13 +313,13 @@ public class FingerprintRow : ListBoxRow { Box box = new Box(Gtk.Orientation.HORIZONTAL, 40) { visible = true, margin_start = 20, margin_end = 20, margin_top = 14, margin_bottom = 14, hexpand = true }; Box status_box = new Box(Gtk.Orientation.HORIZONTAL, 5) { visible = true, hexpand = true }; - box.add(fingerprint_label); - box.add(status_box); + box.append(fingerprint_label); + box.append(status_box); - status_box.add(trust_label); - status_box.add(trust_image); + status_box.append(trust_label); + status_box.append(trust_image); - this.add(box); + this.set_child(box); } public FingerprintRow(Row row, string key_base64, int trust, bool now_active) { diff --git a/plugins/omemo/src/ui/contact_details_provider.vala b/plugins/omemo/src/ui/contact_details_provider.vala index 7250d135..822294cc 100644 --- a/plugins/omemo/src/ui/contact_details_provider.vala +++ b/plugins/omemo/src/ui/contact_details_provider.vala @@ -15,7 +15,7 @@ public class ContactDetailsProvider : Plugins.ContactDetailsProvider, Object { } public void populate(Conversation conversation, Plugins.ContactDetails contact_details, WidgetType type) { - if (conversation.type_ == Conversation.Type.CHAT && type == WidgetType.GTK) { + if (conversation.type_ == Conversation.Type.CHAT && type == WidgetType.GTK4) { int identity_id = plugin.db.identity.get_id(conversation.account.id); if (identity_id < 0) return; @@ -28,11 +28,11 @@ public class ContactDetailsProvider : Plugins.ContactDetailsProvider, Object { } if (i > 0) { - Button btn = new Button.from_icon_name("view-list-symbolic") { visible = true, valign = Align.CENTER, relief = ReliefStyle.NONE }; + Button btn = new Button.from_icon_name("view-list-symbolic") { visible = true, valign = Align.CENTER, has_frame = false }; btn.clicked.connect(() => { btn.activate(); ContactDetailsDialog dialog = new ContactDetailsDialog(plugin, conversation.account, conversation.counterpart); - dialog.set_transient_for((Window) btn.get_toplevel()); + dialog.set_transient_for((Window) btn.get_root()); dialog.response.connect((response_type) => { plugin.device_notification_populator.should_hide(); }); diff --git a/plugins/omemo/src/ui/device_notification_populator.vala b/plugins/omemo/src/ui/device_notification_populator.vala index 2f276f2b..9f40353d 100644 --- a/plugins/omemo/src/ui/device_notification_populator.vala +++ b/plugins/omemo/src/ui/device_notification_populator.vala @@ -70,18 +70,18 @@ private class ConversationNotification : MetaConversationNotification { this.account = account; Box box = new Box(Orientation.HORIZONTAL, 5) { visible=true }; - Button manage_button = new Button() { label=_("Manage"), visible=true }; + Button manage_button = new Button.with_label(_("Manage")) { visible=true }; manage_button.clicked.connect(() => { manage_button.activate(); ContactDetailsDialog dialog = new ContactDetailsDialog(plugin, account, jid); - dialog.set_transient_for((Window) manage_button.get_toplevel()); + dialog.set_transient_for((Window) manage_button.get_root()); dialog.response.connect((response_type) => { should_hide(); }); dialog.present(); }); - box.add(new Label(_("This contact has new devices")) { margin_end=10, visible=true }); - box.add(manage_button); + box.append(new Label(_("This contact has new devices")) { margin_end=10, visible=true }); + box.append(manage_button); widget = box; } diff --git a/plugins/omemo/src/ui/encryption_list_entry.vala b/plugins/omemo/src/ui/encryption_list_entry.vala index a0e6bb0f..b262ef81 100644 --- a/plugins/omemo/src/ui/encryption_list_entry.vala +++ b/plugins/omemo/src/ui/encryption_list_entry.vala @@ -22,9 +22,11 @@ public class EncryptionListEntry : Plugins.EncryptionListEntry, Object { return "OMEMO"; }} - public static IconSize ICON_SIZE_HEADER = Gtk.icon_size_register("im.dino.Dino.HEADER_ICON2", 17, 12); - public Object? get_encryption_icon(Entities.Conversation conversation, ContentItem content_item) { + return null; + } + + public string? get_encryption_icon_name(Entities.Conversation conversation, ContentItem content_item) { if (content_item.encryption != encryption) return null; RowOption row = db.content_item_meta.select( { db.identity_meta.trust_level } ).with(db.content_item_meta.content_item_id, "=", content_item.id) @@ -33,7 +35,7 @@ public class EncryptionListEntry : Plugins.EncryptionListEntry, Object { if (row.is_present() && (TrustLevel) row[db.identity_meta.trust_level] == TrustLevel.VERIFIED) { - return new Image.from_icon_name("dino-security-high-symbolic", ICON_SIZE_HEADER) { opacity=0.4, visible = true }; + return "dino-security-high-symbolic"; } return null; } diff --git a/plugins/omemo/src/ui/manage_key_dialog.vala b/plugins/omemo/src/ui/manage_key_dialog.vala index 9bdaaaee..a4b61f70 100644 --- a/plugins/omemo/src/ui/manage_key_dialog.vala +++ b/plugins/omemo/src/ui/manage_key_dialog.vala @@ -32,7 +32,7 @@ public class ManageKeyDialog : Gtk.Dialog { construct { // If we set the strings in the .ui file, they don't get translated - headerbar.title = _("Manage Key"); + this.title = _("Manage Key"); compare_fingerprint_label.label = _("Compare the fingerprint, character by character, with the one shown on your contact's device."); verify_no_button.label = _("Fingerprints differ"); verify_yes_button.label = _("Fingerprints match"); @@ -56,7 +56,7 @@ public class ManageKeyDialog : Gtk.Dialog { }); verify_yes_button.clicked.connect(() => { - confirm_image.set_from_icon_name("security-high-symbolic", IconSize.DIALOG); + confirm_image.set_from_icon_name("security-high-symbolic"); confirm_title_label.label = _("Verify key"); confirm_desc_label.set_markup(_("Future messages sent by %s from the device that uses this key will be highlighted accordingly in the chat window.").printf(@"<b>$(device[db.identity_meta.address_name])</b>")); manage_stack.set_visible_child_name("confirm"); @@ -67,7 +67,7 @@ public class ManageKeyDialog : Gtk.Dialog { verify_no_button.clicked.connect(() => { return_to_main = false; - confirm_image.set_from_icon_name("dialog-warning-symbolic", IconSize.DIALOG); + confirm_image.set_from_icon_name("dialog-warning-symbolic"); confirm_title_label.label = _("Fingerprints do not match"); confirm_desc_label.set_markup(_("Please verify that you are comparing the correct fingerprint. If fingerprints do not match, %s's account may be compromised and you should consider rejecting this key.").printf(@"<b>$(device[db.identity_meta.address_name])</b>")); manage_stack.set_visible_child_name("confirm"); @@ -107,8 +107,8 @@ public class ManageKeyDialog : Gtk.Dialog { lbl_desc.attributes = desc_attrs; lbl_desc.get_style_context().add_class("dim-label"); - box.add(lbl_title); - box.add(lbl_desc); + box.append(lbl_title); + box.append(lbl_desc); return box; } @@ -121,25 +121,25 @@ public class ManageKeyDialog : Gtk.Dialog { }); ListBoxRow verify_row = new ListBoxRow() { visible = true }; - verify_row.add(make_action_box(_("Verify key fingerprint"), _("Compare this key's fingerprint with the fingerprint displayed on the contact's device."))); + verify_row.set_child(make_action_box(_("Verify key fingerprint"), _("Compare this key's fingerprint with the fingerprint displayed on the contact's device."))); ListBoxRow reject_row = new ListBoxRow() { visible = true }; - reject_row.add(make_action_box(_("Reject key"), _("Block encrypted communication with the contact's device that uses this key."))); + reject_row.set_child(make_action_box(_("Reject key"), _("Block encrypted communication with the contact's device that uses this key."))); ListBoxRow accept_row = new ListBoxRow() {visible = true }; - accept_row.add(make_action_box(_("Accept key"), _("Allow encrypted communication with the contact's device that uses this key."))); + accept_row.set_child(make_action_box(_("Accept key"), _("Allow encrypted communication with the contact's device that uses this key."))); switch((TrustLevel) device[db.identity_meta.trust_level]) { case TrustLevel.TRUSTED: main_desc_label.set_markup(_("This key is currently %s.").printf("<span color='#1A63D9'>"+_("accepted")+"</span>")+" "+_("This means it can be used by %s to receive and send encrypted messages.").printf(@"<b>$(device[db.identity_meta.address_name])</b>")); - main_action_list.add(verify_row); - main_action_list.add(reject_row); + main_action_list.append(verify_row); + main_action_list.append(reject_row); break; case TrustLevel.VERIFIED: main_desc_label.set_markup(_("This key is currently %s.").printf("<span color='#1A63D9'>"+_("verified")+"</span>")+" "+_("This means it can be used by %s to receive and send encrypted messages.").printf(@"<b>$(device[db.identity_meta.address_name])</b>") + " " + _("Additionally it has been verified to match the key on the contact's device.")); - main_action_list.add(reject_row); + main_action_list.append(reject_row); break; case TrustLevel.UNTRUSTED: main_desc_label.set_markup(_("This key is currently %s.").printf("<span color='#D91900'>"+_("rejected")+"</span>")+" "+_("This means it cannot be used by %s to decipher your messages, and you won't see messages encrypted with it.").printf(@"<b>$(device[db.identity_meta.address_name])</b>")); - main_action_list.add(accept_row); + main_action_list.append(accept_row); break; } @@ -148,7 +148,7 @@ public class ManageKeyDialog : Gtk.Dialog { if(row == verify_row) { manage_stack.set_visible_child_name("verify"); } else if (row == reject_row) { - confirm_image.set_from_icon_name("action-unavailable-symbolic", IconSize.DIALOG); + confirm_image.set_from_icon_name("action-unavailable-symbolic"); confirm_title_label.label = _("Reject key"); confirm_desc_label.set_markup(_("You won't see encrypted messages from the device of %s that uses this key. Conversely, that device won't be able to decipher your messages anymore.").printf(@"<b>$(device[db.identity_meta.address_name])</b>")); manage_stack.set_visible_child_name("confirm"); @@ -156,7 +156,7 @@ public class ManageKeyDialog : Gtk.Dialog { return_to_main = true; current_response = TrustLevel.UNTRUSTED; } else if (row == accept_row) { - confirm_image.set_from_icon_name("emblem-ok-symbolic", IconSize.DIALOG); + confirm_image.set_from_icon_name("emblem-ok-symbolic"); confirm_title_label.label = _("Accept key"); confirm_desc_label.set_markup(_("You will be able to exchange encrypted messages with the device of %s that uses this key.").printf(@"<b>$(device[db.identity_meta.address_name])</b>")); manage_stack.set_visible_child_name("confirm"); diff --git a/plugins/openpgp/CMakeLists.txt b/plugins/openpgp/CMakeLists.txt index 769d517d..649a55ad 100644 --- a/plugins/openpgp/CMakeLists.txt +++ b/plugins/openpgp/CMakeLists.txt @@ -9,7 +9,7 @@ find_packages(OPENPGP_PACKAGES REQUIRED GLib GModule GObject - GTK3 + GTK4 ) set(RESOURCE_LIST @@ -32,7 +32,6 @@ SOURCES src/file_transfer/file_encryptor.vala src/account_settings_entry.vala - src/account_settings_widget.vala src/contact_details_provider.vala src/database.vala src/encryption_list_entry.vala diff --git a/plugins/openpgp/data/account_settings_item.ui b/plugins/openpgp/data/account_settings_item.ui index f9757c2b..56808be0 100644 --- a/plugins/openpgp/data/account_settings_item.ui +++ b/plugins/openpgp/data/account_settings_item.ui @@ -1,31 +1,31 @@ -<?xml version="1.0" encoding="UTF-8"?> <interface> - <template class="DinoPluginsOpenPgpAccountSettingsWidget"> - <property name="visible">True</property> + <requires lib="gtk" version="4.0"/> + <object class="GtkStack" id="stack"> <child> - <object class="GtkButton" id="button"> - <property name="relief">none</property> - <property name="sensitive">False</property> - <property name="visible">True</property> - <child> - <object class="GtkLabel" id="label"> - <property name="xalign">0</property> - <property name="visible">True</property> + <object class="GtkStackPage"> + <property name="name">label</property> + <property name="child"> + <object class="GtkButton" id="button"> + <property name="has-frame">0</property> + <property name="sensitive">0</property> + <child> + <object class="GtkLabel" id="label"> + <property name="xalign">0</property> + </object> + </child> </object> - </child> + </property> </object> - <packing> - <property name="name">label</property> - </packing> </child> <child> - <object class="GtkComboBox" id="combobox"> - <property name="hexpand">True</property> - <property name="visible">True</property> - </object> - <packing> + <object class="GtkStackPage"> <property name="name">entry</property> - </packing> + <property name="child"> + <object class="GtkComboBox" id="combobox"> + <property name="hexpand">1</property> + </object> + </property> + </object> </child> - </template> + </object> </interface>
\ No newline at end of file diff --git a/plugins/openpgp/src/account_settings_entry.vala b/plugins/openpgp/src/account_settings_entry.vala index 75220c30..d2e5ac23 100644 --- a/plugins/openpgp/src/account_settings_entry.vala +++ b/plugins/openpgp/src/account_settings_entry.vala @@ -1,27 +1,161 @@ +using Dino.Entities; +using Gtk; + namespace Dino.Plugins.OpenPgp { public class AccountSettingsEntry : Plugins.AccountSettingsEntry { + private Label label; + private Button button; + private ComboBox combobox; + private Stack stack; + private Plugin plugin; + private Account current_account; + private Gee.List<GPG.Key> keys = null; + private Gtk.ListStore list_store = new Gtk.ListStore(2, typeof(string), typeof(string?)); + + public override string id { get { return "pgp_key_picker"; }} + + public override string name { get { return "OpenPGP"; }} public AccountSettingsEntry(Plugin plugin) { this.plugin = plugin; + + Builder builder = new Builder.from_resource("/im/dino/Dino/openpgp/account_settings_item.ui"); + stack = (Stack) builder.get_object("stack"); + label = (Label) builder.get_object("label"); + button = (Button) builder.get_object("button"); + combobox = (ComboBox) builder.get_object("combobox"); + + CellRendererText renderer = new CellRendererText(); + renderer.set_padding(0, 0); + combobox.pack_start(renderer, true); + combobox.add_attribute(renderer, "markup", 0); + combobox.set_model(list_store); + + button.clicked.connect(on_button_clicked); + combobox.changed.connect(key_changed); } - public override string id { get { - return "pgp_key_picker"; - }} + public override void deactivate() { + stack.set_visible_child_name("label"); + } - public override string name { get { - return "OpenPGP"; - }} + public override void set_account(Account account) { + set_account_.begin(account); + } - public override Plugins.AccountSettingsWidget? get_widget(WidgetType type) { - if (type == WidgetType.GTK) { - return new AccountSettingsWidget(plugin); + private async void set_account_(Account account) { + this.current_account = account; + if (keys == null) { + yield fetch_keys(); + populate_list_store(); } - return null; + activate_current_account(); } -} + private void on_button_clicked() { + activated(); + stack.set_visible_child_name("entry"); + combobox.grab_focus(); + combobox.popup(); + } + + private void activate_current_account() { + combobox.changed.disconnect(key_changed); + if (keys == null) { + label.set_markup(build_markup_string(_("Key publishing disabled"), _("Error in GnuPG"))); + return; + } + if (keys.size == 0) { + label.set_markup(build_markup_string(_("Key publishing disabled"), _("No keys available. Generate one!"))); + return; + } + + string? account_key = plugin.db.get_account_key(current_account); + int activate_index = 0; + for (int i = 0; i < keys.size; i++) { + GPG.Key key = keys[i]; + if (key.fpr == account_key) { + activate_index = i + 1; + } + } + combobox.active = activate_index; + + TreeIter selected; + combobox.get_active_iter(out selected); + set_label_active(selected); + + combobox.changed.connect(key_changed); + } + + private void populate_list_store() { + if (keys == null || keys.size == 0) { + return; + } + + list_store.clear(); + TreeIter iter; + list_store.append(out iter); + list_store.set(iter, 0, build_markup_string(_("Key publishing disabled"), _("Select key") + "<span font_family='monospace' font='8'> \n </span>"), 1, ""); + for (int i = 0; i < keys.size; i++) { + list_store.append(out iter); + list_store.set(iter, 0, @"$(Markup.escape_text(keys[i].uids[0].uid))\n<span font_family='monospace' font='8'>$(markup_colorize_id(keys[i].fpr, true))</span><span font='8'> </span>"); + list_store.set(iter, 1, keys[i].fpr); + if (keys[i].fpr == plugin.db.get_account_key(current_account)) { + set_label_active(iter, i + 1); + } + } + button.sensitive = true; + } + + private async void fetch_keys() { + label.set_markup(build_markup_string(_("Loading…"), _("Querying GnuPG"))); + + SourceFunc callback = fetch_keys.callback; + new Thread<void*> (null, () => { // Querying GnuPG might take some time + try { + keys = GPGHelper.get_keylist(null, true); + } catch (Error e) { } + Idle.add((owned)callback); + return null; + }); + yield; + } + + private void set_label_active(TreeIter iter, int i = -1) { + Value text; + list_store.get_value(iter, 0, out text); + label.set_markup((string) text); + if (i != -1) combobox.active = i; + } + + private void key_changed() { + TreeIter selected; + bool iter_valid = combobox.get_active_iter(out selected); + if (iter_valid) { + Value key_value; + list_store.get_value(selected, 1, out key_value); + string? key_id = key_value as string; + if (key_id != null) { + if (plugin.modules.has_key(current_account)) { + plugin.modules[current_account].set_private_key_id(key_id); + } + plugin.db.set_account_key(current_account, key_id); + } + set_label_active(selected); + deactivate(); + } + } + + private string build_markup_string(string primary, string secondary) { + return @"$(Markup.escape_text(primary))\n<span font='8'>$secondary</span>"; + } + + public override Object? get_widget(WidgetType type) { + if (type != WidgetType.GTK4) return null; + return stack; + } +} }
\ No newline at end of file diff --git a/plugins/openpgp/src/account_settings_widget.vala b/plugins/openpgp/src/account_settings_widget.vala deleted file mode 100644 index 7c417001..00000000 --- a/plugins/openpgp/src/account_settings_widget.vala +++ /dev/null @@ -1,149 +0,0 @@ -using Gee; -using Gtk; - -using Dino.Entities; - -namespace Dino.Plugins.OpenPgp { - -[GtkTemplate (ui = "/im/dino/Dino/openpgp/account_settings_item.ui")] - -private class AccountSettingsWidget : Stack, Plugins.AccountSettingsWidget { - [GtkChild] private unowned Label label; - [GtkChild] private unowned Button button; - [GtkChild] private unowned ComboBox combobox; - - private Plugin plugin; - private Account current_account; - private Gee.List<GPG.Key> keys = null; - private Gtk.ListStore list_store = new Gtk.ListStore(2, typeof(string), typeof(string?)); - - public AccountSettingsWidget(Plugin plugin) { - this.plugin = plugin; - - CellRendererText renderer = new CellRendererText(); - renderer.set_padding(0, 0); - combobox.pack_start(renderer, true); - combobox.add_attribute(renderer, "markup", 0); - combobox.set_model(list_store); - - button.clicked.connect(on_button_clicked); - combobox.changed.connect(key_changed); - } - - public void deactivate() { - set_visible_child_name("label"); - } - - public void set_account(Account account) { - set_account_.begin(account); - } - - private async void set_account_(Account account) { - this.current_account = account; - if (keys == null) { - yield fetch_keys(); - populate_list_store(); - } - activate_current_account(); - } - - private void on_button_clicked() { - activated(); - set_visible_child_name("entry"); - combobox.grab_focus(); - combobox.popup(); - } - - private void activate_current_account() { - combobox.changed.disconnect(key_changed); - if (keys == null) { - label.set_markup(build_markup_string(_("Key publishing disabled"), _("Error in GnuPG"))); - return; - } - if (keys.size == 0) { - label.set_markup(build_markup_string(_("Key publishing disabled"), _("No keys available. Generate one!"))); - return; - } - - string? account_key = plugin.db.get_account_key(current_account); - int activate_index = 0; - for (int i = 0; i < keys.size; i++) { - GPG.Key key = keys[i]; - if (key.fpr == account_key) { - activate_index = i + 1; - } - } - combobox.active = activate_index; - - TreeIter selected; - combobox.get_active_iter(out selected); - set_label_active(selected); - - combobox.changed.connect(key_changed); - } - - private void populate_list_store() { - if (keys == null || keys.size == 0) { - return; - } - - list_store.clear(); - TreeIter iter; - list_store.append(out iter); - list_store.set(iter, 0, build_markup_string(_("Key publishing disabled"), _("Select key") + "<span font_family='monospace' font='8'> \n </span>"), 1, ""); - for (int i = 0; i < keys.size; i++) { - list_store.append(out iter); - list_store.set(iter, 0, @"$(Markup.escape_text(keys[i].uids[0].uid))\n<span font_family='monospace' font='8'>$(markup_colorize_id(keys[i].fpr, true))</span><span font='8'> </span>"); - list_store.set(iter, 1, keys[i].fpr); - if (keys[i].fpr == plugin.db.get_account_key(current_account)) { - set_label_active(iter, i + 1); - } - } - button.sensitive = true; - } - - private async void fetch_keys() { - label.set_markup(build_markup_string(_("Loading…"), _("Querying GnuPG"))); - - SourceFunc callback = fetch_keys.callback; - new Thread<void*> (null, () => { // Querying GnuPG might take some time - try { - keys = GPGHelper.get_keylist(null, true); - } catch (Error e) { } - Idle.add((owned)callback); - return null; - }); - yield; - } - - private void set_label_active(TreeIter iter, int i = -1) { - Value text; - list_store.get_value(iter, 0, out text); - label.set_markup((string) text); - if (i != -1) combobox.active = i; - } - - private void key_changed() { - TreeIter selected; - bool iter_valid = combobox.get_active_iter(out selected); - if (iter_valid) { - Value key_value; - list_store.get_value(selected, 1, out key_value); - string? key_id = key_value as string; - if (key_id != null) { - if (plugin.modules.has_key(current_account)) { - plugin.modules[current_account].set_private_key_id(key_id); - } - plugin.db.set_account_key(current_account, key_id); - } - set_label_active(selected); - deactivate(); - } - } - - private string build_markup_string(string primary, string secondary) { - return @"$(Markup.escape_text(primary))\n<span font='8'>$secondary</span>"; - } -} - -} diff --git a/plugins/openpgp/src/contact_details_provider.vala b/plugins/openpgp/src/contact_details_provider.vala index aa2b1c47..db085a4d 100644 --- a/plugins/openpgp/src/contact_details_provider.vala +++ b/plugins/openpgp/src/contact_details_provider.vala @@ -14,7 +14,7 @@ public class ContactDetailsProvider : Plugins.ContactDetailsProvider, Object { } public void populate(Conversation conversation, Plugins.ContactDetails contact_details, WidgetType type) { - if (conversation.type_ == Conversation.Type.CHAT && type == WidgetType.GTK) { + if (conversation.type_ == Conversation.Type.CHAT && type == WidgetType.GTK4) { string? key_id = stream_interactor.get_module(Manager.IDENTITY).get_key_id(conversation.account, conversation.counterpart); if (key_id != null) { Label label = new Label("") { use_markup=true, justify=Justification.RIGHT, selectable=true, visible=true }; diff --git a/plugins/openpgp/src/encryption_list_entry.vala b/plugins/openpgp/src/encryption_list_entry.vala index 4169a2a2..cf5da8c4 100644 --- a/plugins/openpgp/src/encryption_list_entry.vala +++ b/plugins/openpgp/src/encryption_list_entry.vala @@ -24,12 +24,14 @@ private class EncryptionListEntry : Plugins.EncryptionListEntry, Object { return "OpenPGP"; }} - public static IconSize ICON_SIZE_HEADER = Gtk.icon_size_register("im.dino.Dino.HEADER_ICON3", 17, 12); - public Object? get_encryption_icon(Entities.Conversation conversation, ContentItem content_item) { return null; } + public string? get_encryption_icon_name(Entities.Conversation conversation, ContentItem content_item) { + return null; + } + public void encryption_activated(Entities.Conversation conversation, Plugins.SetInputFieldStatus input_status_callback) { try { GPGHelper.get_public_key(db.get_account_key(conversation.account) ?? ""); diff --git a/plugins/rtp/CMakeLists.txt b/plugins/rtp/CMakeLists.txt index c8652c54..61d53c97 100644 --- a/plugins/rtp/CMakeLists.txt +++ b/plugins/rtp/CMakeLists.txt @@ -7,7 +7,7 @@ find_packages(RTP_PACKAGES REQUIRED GModule GnuTLS GObject - GTK3 + GTK4 Gst GstApp GstAudio diff --git a/plugins/rtp/src/plugin.vala b/plugins/rtp/src/plugin.vala index dc446530..aefe41ff 100644 --- a/plugins/rtp/src/plugin.vala +++ b/plugins/rtp/src/plugin.vala @@ -297,7 +297,7 @@ public class Dino.Plugins.Rtp.Plugin : RootInterface, VideoCallPlugin, Object { public VideoCallWidget? create_widget(WidgetType type) { init_call_pipe(); - if (type == WidgetType.GTK) { + if (type == WidgetType.GTK4) { return new VideoWidget(this); } return null; diff --git a/plugins/rtp/src/video_widget.vala b/plugins/rtp/src/video_widget.vala index 82693b09..7a56f787 100644 --- a/plugins/rtp/src/video_widget.vala +++ b/plugins/rtp/src/video_widget.vala @@ -3,7 +3,7 @@ private static extern void gst_value_set_fraction(ref GLib.Value value, int numerator, int denominator); #endif -public class Dino.Plugins.Rtp.VideoWidget : Gtk.Bin, Dino.Plugins.VideoCallWidget { +public class Dino.Plugins.Rtp.VideoWidget : Gtk.Widget, Dino.Plugins.VideoCallWidget { private const int RECAPS_AFTER_CHANGE = 5; private static uint last_id = 0; @@ -29,7 +29,7 @@ public class Dino.Plugins.Rtp.VideoWidget : Gtk.Bin, Dino.Plugins.VideoCallWidge this.plugin = plugin; id = last_id++; - sink = Gst.ElementFactory.make("gtksink", @"video_widget_$id") as Gst.Base.Sink; +// sink = Gst.ElementFactory.make("gtksink", @"video_widget_$id") as Gst.Base.Sink; if (sink != null) { Gtk.Widget widget; sink.@get("widget", out widget); @@ -37,13 +37,13 @@ public class Dino.Plugins.Rtp.VideoWidget : Gtk.Bin, Dino.Plugins.VideoCallWidge sink.@set("sync", true); sink.@set("ignore-alpha", false); this.widget = widget; - this.widget.draw.connect_after(fix_caps_issues); - add(widget); - widget.visible = true; +// this.widget.draw.connect_after(fix_caps_issues); + this.widget.insert_after(this, null); + this.widget.visible = true; } else { warning("Could not create GTK video sink. Won't display videos."); } - size_allocate.connect_after(after_size_allocate); +// size_allocate.connect_after(after_size_allocate); } public void input_caps_changed(GLib.Object pad, ParamSpec spec) { |