Improve Gtk4 port

This commit is contained in:
fiaxh 2022-05-14 14:45:59 +02:00
parent 2b3ce5fc95
commit f44cbe02c1
91 changed files with 587 additions and 1465 deletions

View file

@ -13,16 +13,14 @@ find_packages(MAIN_PACKAGES REQUIRED
) )
set(RESOURCE_LIST set(RESOURCE_LIST
icons/dino-account-plus-symbolic.svg
icons/dino-conversation-list-placeholder-arrow.svg
icons/dino-qr-code-symbolic.svg
icons/dino-party-popper-symbolic.svg
icons/dino-status-away.svg icons/dino-status-away.svg
icons/dino-status-chat.svg icons/dino-status-chat.svg
icons/dino-status-dnd.svg icons/dino-status-dnd.svg
icons/dino-status-online.svg icons/dino-status-online.svg
icons/im.dino.Dino.svg icons/im.dino.Dino.svg
icons/scalable/actions/dino-account-plus-symbolic.svg
icons/scalable/apps/im.dino.Dino-symbolic.svg icons/scalable/apps/im.dino.Dino-symbolic.svg
icons/scalable/devices/dino-device-desktop-symbolic.svg icons/scalable/devices/dino-device-desktop-symbolic.svg
@ -43,8 +41,6 @@ set(RESOURCE_LIST
icons/scalable/mimetypes/dino-file-table-symbolic.svg icons/scalable/mimetypes/dino-file-table-symbolic.svg
icons/scalable/mimetypes/dino-file-video-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-double-tick-symbolic.svg
icons/scalable/status/dino-microphone-off-symbolic.svg icons/scalable/status/dino-microphone-off-symbolic.svg
icons/scalable/status/dino-microphone-symbolic.svg icons/scalable/status/dino-microphone-symbolic.svg
@ -53,6 +49,10 @@ set(RESOURCE_LIST
icons/scalable/status/dino-video-off-symbolic.svg icons/scalable/status/dino-video-off-symbolic.svg
icons/scalable/status/dino-video-symbolic.svg icons/scalable/status/dino-video-symbolic.svg
icons/scalable/ui/dino-conversation-list-placeholder-arrow.svg
icons/scalable/ui/dino-qr-code-symbolic.svg
icons/scalable/ui/dino-party-popper-symbolic.svg
add_conversation/add_contact_dialog.ui add_conversation/add_contact_dialog.ui
add_conversation/add_groupchat_dialog.ui add_conversation/add_groupchat_dialog.ui
add_conversation/conference_details_fragment.ui add_conversation/conference_details_fragment.ui
@ -103,11 +103,6 @@ compile_gresources(
unset(MAIN_EXTRA_OPTIONS) unset(MAIN_EXTRA_OPTIONS)
unset(MAIN_EXTRA_PACKAGES) 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)
set(MAIN_EXTRA_OPTIONS ${MAIN_EXTRA_OPTIONS} --vapidir=${CMAKE_CURRENT_SOURCE_DIR}/vapi) set(MAIN_EXTRA_OPTIONS ${MAIN_EXTRA_OPTIONS} --vapidir=${CMAKE_CURRENT_SOURCE_DIR}/vapi)
vala_precompile(MAIN_VALA_C vala_precompile(MAIN_VALA_C
@ -226,16 +221,3 @@ install(FILES data/im.dino.Dino.desktop DESTINATION ${DESKTOP_FILE_INSTALL_DIR})
install(FILES data/im.dino.Dino.service DESTINATION ${SERVICE_FILE_INSTALL_DIR}) install(FILES data/im.dino.Dino.service DESTINATION ${SERVICE_FILE_INSTALL_DIR})
install(FILES data/icons/im.dino.Dino.svg DESTINATION ${ICON_INSTALL_DIR}/hicolor/scalable/apps) install(FILES data/icons/im.dino.Dino.svg DESTINATION ${ICON_INSTALL_DIR}/hicolor/scalable/apps)
install(FILES data/icons/im.dino.Dino-symbolic.svg DESTINATION ${ICON_INSTALL_DIR}/hicolor/symbolic/apps) install(FILES data/icons/im.dino.Dino-symbolic.svg DESTINATION ${ICON_INSTALL_DIR}/hicolor/symbolic/apps)
install(FILES
data/icons/dino-status-away.svg
data/icons/dino-status-chat.svg
data/icons/dino-status-dnd.svg
data/icons/dino-status-online.svg
data/icons/dino-changes-prevent-symbolic.svg
data/icons/dino-double-tick-symbolic.svg
data/icons/dino-qr-code-symbolic.svg
data/icons/dino-tick-symbolic.svg
DESTINATION
${ICON_INSTALL_DIR}/hicolor/scalable/status
)

View file

@ -49,7 +49,6 @@
<object class="DinoUiAccountComboBox" id="account_combobox"> <object class="DinoUiAccountComboBox" id="account_combobox">
<property name="hexpand">True</property> <property name="hexpand">True</property>
<property name="width_request">200</property> <property name="width_request">200</property>
<property name="visible">True</property>
<layout> <layout>
<property name="column">1</property> <property name="column">1</property>
<property name="row">0</property> <property name="row">0</property>

View file

@ -54,7 +54,6 @@
<object class="DinoUiAccountComboBox" id="account_combobox"> <object class="DinoUiAccountComboBox" id="account_combobox">
<property name="hexpand">True</property> <property name="hexpand">True</property>
<property name="width_request">200</property> <property name="width_request">200</property>
<property name="visible">True</property>
</object> </object>
</property> </property>
</object> </object>

View file

@ -2,7 +2,6 @@
<interface> <interface>
<requires lib="gtk" version="4.0"/> <requires lib="gtk" version="4.0"/>
<template class="DinoUiConferenceDetailsFragment"> <template class="DinoUiConferenceDetailsFragment">
<property name="visible">True</property>
<child> <child>
<object class="GtkOverlay"> <object class="GtkOverlay">
<property name="child"> <property name="child">
@ -53,7 +52,6 @@
<object class="DinoUiAccountComboBox" id="account_combobox"> <object class="DinoUiAccountComboBox" id="account_combobox">
<property name="hexpand">True</property> <property name="hexpand">True</property>
<property name="width_request">200</property> <property name="width_request">200</property>
<property name="visible">True</property>
</object> </object>
</property> </property>
</object> </object>

View file

@ -4,7 +4,6 @@
<template class="DinoUiSelectJidFragment"> <template class="DinoUiSelectJidFragment">
<property name="height_request">500</property> <property name="height_request">500</property>
<property name="width_request">460</property> <property name="width_request">460</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkGrid"> <object class="GtkGrid">
<property name="hexpand">1</property> <property name="hexpand">1</property>

View file

@ -10,45 +10,38 @@
<property name="target-width">350</property> <property name="target-width">350</property>
<property name="max-width">350</property> <property name="max-width">350</property>
<property name="hexpand">True</property> <property name="hexpand">True</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkBox"> <object class="GtkBox">
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<property name="halign">fill</property> <property name="halign">fill</property>
<property name="hexpand">true</property> <property name="hexpand">true</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkBox"> <object class="GtkBox">
<property name="orientation">horizontal</property> <property name="orientation">horizontal</property>
<property name="spacing">10</property> <property name="spacing">10</property>
<property name="visible">True</property>
<style> <style>
<class name="call-box"/> <class name="call-box"/>
</style> </style>
<child> <child>
<object class="GtkImage" id="image"> <object class="GtkImage" id="image">
<property name="opacity">0.7</property> <property name="opacity">0.7</property>
<property name="visible">True</property>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkBox"> <object class="GtkBox">
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<property name="hexpand">True</property> <property name="hexpand">True</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkLabel" id="title_label"> <object class="GtkLabel" id="title_label">
<property name="ellipsize">middle</property> <property name="ellipsize">middle</property>
<property name="xalign">0</property> <property name="xalign">0</property>
<property name="yalign">0</property> <property name="yalign">0</property>
<property name="visible">True</property>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkLabel" id="subtitle_label"> <object class="GtkLabel" id="subtitle_label">
<property name="xalign">0</property> <property name="xalign">0</property>
<property name="yalign">1</property> <property name="yalign">1</property>
<property name="visible">True</property>
<attributes> <attributes>
<attribute name="scale" value="0.8"/> <attribute name="scale" value="0.8"/>
</attributes> </attributes>
@ -63,10 +56,8 @@
<property name="transition-type">slide-down</property> <property name="transition-type">slide-down</property>
<property name="transition-duration">200</property> <property name="transition-duration">200</property>
<property name="reveal-child">True</property> <property name="reveal-child">True</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkBox" id="outer_additional_box"> <object class="GtkBox" id="outer_additional_box">
<property name="visible">True</property>
<child> <child>
<object class="GtkBox" id="incoming_call_box"> <object class="GtkBox" id="incoming_call_box">
<property name="halign">end</property> <property name="halign">end</property>
@ -80,7 +71,6 @@
<child> <child>
<object class="GtkButton" id="reject_call_button"> <object class="GtkButton" id="reject_call_button">
<property name="label" translatable="yes">Reject</property> <property name="label" translatable="yes">Reject</property>
<property name="visible">True</property>
<style> <style>
<class name="destructive-action"/> <class name="destructive-action"/>
</style> </style>
@ -89,7 +79,6 @@
<child> <child>
<object class="GtkButton" id="accept_call_button"> <object class="GtkButton" id="accept_call_button">
<property name="label" translatable="yes">Accept</property> <property name="label" translatable="yes">Accept</property>
<property name="visible">True</property>
<style> <style>
<class name="suggested-action"/> <class name="suggested-action"/>
</style> </style>

View file

@ -4,7 +4,6 @@
<template class="DinoUiChatInputView"> <template class="DinoUiChatInputView">
<property name="hexpand">True</property> <property name="hexpand">True</property>
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<property name="visible">True</property>
<style> <style>
<class name="dino-chatinput"/> <class name="dino-chatinput"/>
</style> </style>

View file

@ -33,7 +33,6 @@
<object class="DinoUiAvatarImage" id="avatar"> <object class="DinoUiAvatarImage" id="avatar">
<property name="height">50</property> <property name="height">50</property>
<property name="width">50</property> <property name="width">50</property>
<property name="visible">True</property>
<property name="allow_gray">False</property> <property name="allow_gray">False</property>
<layout> <layout>
<property name="column">0</property> <property name="column">0</property>
@ -46,7 +45,6 @@
<object class="DinoUiUtilEntryLabelHybrid" id="name_hybrid"> <object class="DinoUiUtilEntryLabelHybrid" id="name_hybrid">
<property name="xalign">0</property> <property name="xalign">0</property>
<property name="hexpand">True</property> <property name="hexpand">True</property>
<property name="visible">True</property>
<layout> <layout>
<property name="column">1</property> <property name="column">1</property>
<property name="row">0</property> <property name="row">0</property>

View file

@ -8,7 +8,6 @@
<property name="ellipsize">end</property> <property name="ellipsize">end</property>
<property name="xalign">0</property> <property name="xalign">0</property>
<property name="valign">baseline</property> <property name="valign">baseline</property>
<property name="visible">True</property>
<attributes> <attributes>
<attribute name="weight" value="PANGO_WEIGHT_BOLD"/> <attribute name="weight" value="PANGO_WEIGHT_BOLD"/>
</attributes> </attributes>
@ -18,7 +17,6 @@
<object class="GtkLabel" id="time_label"> <object class="GtkLabel" id="time_label">
<property name="xalign">0</property> <property name="xalign">0</property>
<property name="valign">baseline</property> <property name="valign">baseline</property>
<property name="visible">True</property>
<attributes> <attributes>
<attribute name="scale" value="0.8"/> <attribute name="scale" value="0.8"/>
</attributes> </attributes>

View file

@ -22,12 +22,10 @@
<object class="GtkOverlay"> <object class="GtkOverlay">
<child> <child>
<object class="GtkBox" id="main_event_box"> <object class="GtkBox" id="main_event_box">
<property name="visible">True</property>
<child> <child>
<object class="DinoUiSizeRequestBox" id="main"> <object class="DinoUiSizeRequestBox" id="main">
<property name="margin-bottom">15</property> <property name="margin-bottom">15</property>
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<property name="visible">True</property>
<property name="size-request-mode">height-for-width</property> <property name="size-request-mode">height-for-width</property>
</object> </object>
</child> </child>

View file

@ -50,7 +50,8 @@
<child> <child>
<object class="GtkImage" id="encrypted_image"> <object class="GtkImage" id="encrypted_image">
<property name="opacity">0.4</property> <property name="opacity">0.4</property>
<property name="pixel-size">14</property> <property name="pixel-size">12</property>
<property name="margin-top">2</property>
<layout> <layout>
<property name="column">3</property> <property name="column">3</property>
<property name="row">0</property> <property name="row">0</property>
@ -60,7 +61,8 @@
<child> <child>
<object class="GtkImage" id="marked_image"> <object class="GtkImage" id="marked_image">
<property name="opacity">0.4</property> <property name="opacity">0.4</property>
<property name="pixel-size">14</property> <property name="pixel-size">13</property>
<property name="margin-top">2</property>
<property name="halign">start</property> <property name="halign">start</property>
<property name="hexpand">True</property> <property name="hexpand">True</property>
<layout> <layout>

View file

@ -1,48 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<interface> <interface>
<requires lib="gtk" version="4.0"/>
<template class="DinoUiConversationListTitlebar" parent="GtkBox"> <template class="DinoUiConversationListTitlebar" parent="GtkBox">
<property name="hexpand">False</property> <property name="hexpand">0</property>
<property name="visible">True</property>
<style> <style>
<class name="dino-header-left"/> <class name="dino-header-left"/>
</style> </style>
<child> <child>
<object class="GtkBox"> <object class="GtkBox">
<property name="margin">5</property> <property name="margin-start">5</property>
<property name="hexpand">True</property> <property name="margin-end">5</property>
<property name="visible">True</property> <property name="margin-top">5</property>
<property name="margin-bottom">5</property>
<property name="hexpand">1</property>
<child> <child>
<object class="GtkMenuButton" id="add_button"> <object class="GtkMenuButton" id="add_button">
<property name="tooltip_text" translatable="yes">Start Conversation</property> <property name="tooltip_text" translatable="1">Start Conversation</property>
<property name="relief">none</property> <property name="has-frame">False</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkImage"> <object class="GtkImage">
<property name="visible">True</property>
<property name="icon-name">list-add-symbolic</property> <property name="icon-name">list-add-symbolic</property>
<property name="icon-size">1</property> <property name="icon-size">normal</property>
</object> </object>
</child> </child>
</object> </object>
<packing>
<property name="pack_type">start</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkMenuButton" id="menu_button"> <object class="GtkMenuButton" id="menu_button">
<property name="relief">none</property> <property name="halign">end</property>
<property name="visible">True</property> <property name="hexpand">True</property>
<property name="has-frame">False</property>
<child> <child>
<object class="GtkImage"> <object class="GtkImage">
<property name="visible">True</property>
<property name="icon-name">open-menu-symbolic</property> <property name="icon-name">open-menu-symbolic</property>
<property name="icon-size">1</property> <property name="icon-size">normal</property>
</object> </object>
</child> </child>
</object> </object>
<packing>
<property name="pack_type">end</property>
</packing>
</child> </child>
</object> </object>
</child> </child>

View file

@ -1,13 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<interface> <interface>
<template class="DinoUiConversationSelectorRow"> <template class="DinoUiConversationSelectorRow">
<property name="visible">True</property>
<child> <child>
<object class="GtkRevealer" id="main_revealer"> <object class="GtkRevealer" id="main_revealer">
<property name="transition-type">slide-down</property> <property name="transition-type">slide-down</property>
<property name="transition-duration">200</property> <property name="transition-duration">200</property>
<property name="reveal-child">True</property> <property name="reveal-child">True</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkBox"> <object class="GtkBox">
<property name="orientation">horizontal</property> <property name="orientation">horizontal</property>
@ -15,24 +13,20 @@
<property name="margin-bottom">10</property> <property name="margin-bottom">10</property>
<property name="margin-start">7</property> <property name="margin-start">7</property>
<property name="margin-end">14</property> <property name="margin-end">14</property>
<property name="visible">True</property>
<child> <child>
<object class="DinoUiAvatarImage" id="image"> <object class="DinoUiAvatarImage" id="image">
<property name="height">35</property> <property name="height">35</property>
<property name="width">35</property> <property name="width">35</property>
<property name="valign">center</property> <property name="valign">center</property>
<property name="visible">True</property>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkBox"> <object class="GtkBox">
<property name="margin-start">10</property> <property name="margin-start">10</property>
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkBox"> <object class="GtkBox">
<property name="valign">start</property> <property name="valign">start</property>
<property name="visible">True</property>
<property name="orientation">horizontal</property> <property name="orientation">horizontal</property>
<child> <child>
<object class="GtkLabel" id="name_label"> <object class="GtkLabel" id="name_label">
@ -41,7 +35,6 @@
<property name="hexpand">True</property> <property name="hexpand">True</property>
<property name="margin-end">7</property> <property name="margin-end">7</property>
<property name="xalign">0</property> <property name="xalign">0</property>
<property name="visible">True</property>
</object> </object>
</child> </child>
<child> <child>
@ -49,7 +42,6 @@
<property name="transition-type">slide-right</property> <property name="transition-type">slide-right</property>
<property name="transition-duration">50</property> <property name="transition-duration">50</property>
<property name="reveal-child">True</property> <property name="reveal-child">True</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkLabel" id="time_label"> <object class="GtkLabel" id="time_label">
<property name="hexpand">False</property> <property name="hexpand">False</property>
@ -66,7 +58,6 @@
<child> <child>
<object class="GtkBox"> <object class="GtkBox">
<property name="valign">end</property> <property name="valign">end</property>
<property name="visible">True</property>
<property name="orientation">horizontal</property> <property name="orientation">horizontal</property>
<child> <child>
<object class="GtkLabel" id="nick_label"> <object class="GtkLabel" id="nick_label">
@ -101,7 +92,6 @@
<property name="transition-type">slide-right</property> <property name="transition-type">slide-right</property>
<property name="transition-duration">50</property> <property name="transition-duration">50</property>
<property name="reveal-child">True</property> <property name="reveal-child">True</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkLabel" id="unread_count_label"> <object class="GtkLabel" id="unread_count_label">
<property name="vexpand">False</property> <property name="vexpand">False</property>
@ -123,11 +113,9 @@
<child> <child>
<object class="GtkBox"> <object class="GtkBox">
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkBox"> <object class="GtkBox">
<property name="vexpand">True</property> <property name="vexpand">True</property>
<property name="visible">True</property>
</object> </object>
</child> </child>
<child> <child>
@ -135,13 +123,11 @@
<property name="transition-type">slide-left</property> <property name="transition-type">slide-left</property>
<property name="transition-duration">100</property> <property name="transition-duration">100</property>
<property name="reveal-child">False</property> <property name="reveal-child">False</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkButton" id="x_button"> <object class="GtkButton" id="x_button">
<property name="width-request">27</property> <property name="width-request">27</property>
<property name="height-request">27</property> <property name="height-request">27</property>
<property name="vexpand">False</property> <property name="vexpand">False</property>
<property name="visible">True</property>
<property name="margin-start">5</property> <property name="margin-start">5</property>
<style> <style>
<class name="conversation_list_row_xbutton"/> <class name="conversation_list_row_xbutton"/>
@ -152,7 +138,6 @@
<object class="GtkImage"> <object class="GtkImage">
<property name="icon-name">window-close-symbolic</property> <property name="icon-name">window-close-symbolic</property>
<property name="icon-size">1</property> <property name="icon-size">1</property>
<property name="visible">True</property>
</object> </object>
</child> </child>
</object> </object>
@ -162,7 +147,6 @@
<child> <child>
<object class="GtkBox"> <object class="GtkBox">
<property name="vexpand">True</property> <property name="vexpand">True</property>
<property name="visible">True</property>
</object> </object>
</child> </child>
</object> </object>

View file

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<interface> <interface>
<template class="DinoUiConversationView"> <template class="DinoUiConversationView">
<property name="visible">True</property>
<child> <child>
<object class="GtkOverlay" id="overlay"> <object class="GtkOverlay" id="overlay">
<child> <child>
@ -22,7 +21,6 @@
<property name="halign">end</property> <property name="halign">end</property>
<property name="valign">end</property> <property name="valign">end</property>
<property name="transition-type">crossfade</property> <property name="transition-type">crossfade</property>
<property name="visible">True</property>
<property name="margin-end">30</property> <property name="margin-end">30</property>
<property name="margin-bottom">30</property> <property name="margin-bottom">30</property>
<child> <child>
@ -32,7 +30,6 @@
<property name="vexpand">False</property> <property name="vexpand">False</property>
<property name="halign">end</property> <property name="halign">end</property>
<property name="valign">end</property> <property name="valign">end</property>
<property name="visible">True</property>
<style> <style>
<class name="circular-button"/> <class name="circular-button"/>
</style> </style>
@ -40,7 +37,6 @@
<object class="GtkImage"> <object class="GtkImage">
<property name="icon-name">go-down-symbolic</property> <property name="icon-name">go-down-symbolic</property>
<property name="icon-size">1</property> <property name="icon-size">1</property>
<property name="visible">True</property>
</object> </object>
</child> </child>
</object> </object>
@ -51,7 +47,6 @@
</child> </child>
<child> <child>
<object class="DinoUiChatInputView" id="chat_input"> <object class="DinoUiChatInputView" id="chat_input">
<property name="visible">True</property>
</object> </object>
</child> </child>
</object> </object>
@ -65,7 +60,6 @@
<child> <child>
<object class="GtkBox"> <object class="GtkBox">
<property name="opacity">0.7</property> <property name="opacity">0.7</property>
<property name="visible">true</property>
<style> <style>
<class name="dino-white-overlay"/> <class name="dino-white-overlay"/>
</style> </style>

View file

Before

Width:  |  Height:  |  Size: 456 B

After

Width:  |  Height:  |  Size: 456 B

View file

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg version="1.1" viewBox="0 0 16 12" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(-154.64 -256.66)">
<g transform="matrix(.74332 0 0 .74332 157.02 256.73)" fill="#000000">
<path d="m3 9h10c0.554 0 1 0.446 1 1v3c0 0.554-0.446 1-1 1h-10c-0.554 0-1-0.446-1-1v-3c0-0.554 0.446-1 1-1z" overflow="visible"/>
<path d="M 7,0 C 7,0 6.291,-0.014 5.553,0.356 4.814,0.725 4,1.666 4,3 V 4.4127188 H 6 V 3 C 6,2.333 6.186,2.275 6.447,2.145 6.71,2.014 7,2 7,2 H 9 C 9,2 9.291,2.014 9.553,2.145 9.814,2.275 10,2.333 10,3 v 8 h 2 V 3 C 12,1.667 11.186,0.725 10.447,0.356 9.71,-0.014 9,0 9,0 Z" font-family="sans-serif" font-weight="400" overflow="visible" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;isolation:auto;mix-blend-mode:normal;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none" white-space="normal"/>
<path d="m2 12h12v4h-12z" overflow="visible"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -1,67 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
width="16"
version="1.1"
id="svg7384"
height="12">
<metadata
id="metadata90">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title>Gnome Symbolic Icon Theme</dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<title
id="title9167">Gnome Symbolic Icon Theme</title>
<defs
id="defs7386" />
<g
transform="translate(-181.0002,-241)"
style="display:inline"
id="layer9">
<path
style="color:#bebebe;display:inline;overflow:visible;visibility:visible;fill:#bebebe;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.76307714;marker:none"
id="rect4063"
d="m 188.13566,241.5 c -1.44483,0 -2.63542,1.19893 -2.63542,2.65384 v 2.65385 h -0.87847 c -0.48668,0 -0.87847,0.39453 -0.87847,0.88462 V 248.16225 253 h 4.39236 1.75695 4.39235 v -4.83775 -0.46994 c 0,-0.49009 -0.39179,-0.88462 -0.87846,-0.88462 h -0.87848 v -2.65385 c 0,-1.45491 -1.19059,-2.65384 -2.63541,-2.65384 z m 0,1.76922 h 1.75695 c 0.50187,0 0.87848,0.37925 0.87848,0.88462 v 2.65385 h -3.5139 v -2.65385 c 0,-0.50537 0.3766,-0.88462 0.87847,-0.88462 z" />
</g>
<g
transform="translate(-181.0002,-241)"
style="display:inline"
id="layer10" />
<g
transform="translate(-181.0002,-241)"
id="layer11" />
<g
transform="translate(-181.0002,-241)"
style="display:inline"
id="layer13" />
<g
transform="translate(-181.0002,-241)"
id="layer14" />
<g
transform="translate(-181.0002,-241)"
style="display:inline"
id="layer15" />
<g
transform="translate(-181.0002,-241)"
style="display:inline"
id="g71291" />
<g
transform="translate(-181.0002,-241)"
style="display:inline"
id="g4953" />
<g
transform="translate(-181.0002,-241)"
style="display:inline"
id="layer12" />
</svg>

Before

Width:  |  Height:  |  Size: 2.2 KiB

View file

Before

Width:  |  Height:  |  Size: 673 B

After

Width:  |  Height:  |  Size: 673 B

View file

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View file

Before

Width:  |  Height:  |  Size: 568 B

After

Width:  |  Height:  |  Size: 568 B

View file

@ -2,7 +2,6 @@
<interface> <interface>
<requires lib="gtk" version="4.0"/> <requires lib="gtk" version="4.0"/>
<template class="DinoUiManageAccountsAccountRow"> <template class="DinoUiManageAccountsAccountRow">
<property name="visible">True</property>
<child> <child>
<object class="GtkGrid"> <object class="GtkGrid">
<property name="margin-start">6</property> <property name="margin-start">6</property>

View file

@ -11,7 +11,6 @@
</child> </child>
<child internal-child="content_area"> <child internal-child="content_area">
<object class="GtkBox"> <object class="GtkBox">
<property name="visible">True</property>
<child> <child>
<object class="GtkStack" id="main_stack"> <object class="GtkStack" id="main_stack">
<child> <child>

View file

@ -6,8 +6,6 @@
<attribute name="action">app.add_chat</attribute> <attribute name="action">app.add_chat</attribute>
<attribute name="label" translatable="yes">Start Conversation</attribute> <attribute name="label" translatable="yes">Start Conversation</attribute>
</item> </item>
</section>
<section>
<item> <item>
<attribute name="action">app.add_conference</attribute> <attribute name="action">app.add_conference</attribute>
<attribute name="label" translatable="yes">Join Channel</attribute> <attribute name="label" translatable="yes">Join Channel</attribute>

View file

@ -2,7 +2,6 @@
<interface> <interface>
<object class="GtkBox" id="root"> <object class="GtkBox" id="root">
<property name="orientation">horizontal</property> <property name="orientation">horizontal</property>
<property name="visible">True</property>
<child> <child>
<object class="DinoUiAvatarImage" id="image"> <object class="DinoUiAvatarImage" id="image">
<property name="margin-top">4</property> <property name="margin-top">4</property>

View file

@ -4,22 +4,18 @@
<property name="modal">True</property> <property name="modal">True</property>
<child> <child>
<object class="GtkShortcutsSection"> <object class="GtkShortcutsSection">
<property name="visible">True</property>
<property name="section-name">shortcuts</property> <property name="section-name">shortcuts</property>
<child> <child>
<object class="GtkShortcutsGroup"> <object class="GtkShortcutsGroup">
<property name="visible">True</property>
<property name="title" translatable="yes">General</property> <property name="title" translatable="yes">General</property>
<child> <child>
<object class="GtkShortcutsShortcut"> <object class="GtkShortcutsShortcut">
<property name="visible">True</property>
<property name="accelerator">&lt;ctrl&gt;T</property> <property name="accelerator">&lt;ctrl&gt;T</property>
<property name="title" translatable="yes">Start Conversation</property> <property name="title" translatable="yes">Start Conversation</property>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkShortcutsShortcut"> <object class="GtkShortcutsShortcut">
<property name="visible">True</property>
<property name="accelerator">&lt;ctrl&gt;G</property> <property name="accelerator">&lt;ctrl&gt;G</property>
<property name="title" translatable="yes">Join Channel</property> <property name="title" translatable="yes">Join Channel</property>
</object> </object>
@ -28,18 +24,15 @@
</child> </child>
<child> <child>
<object class="GtkShortcutsGroup"> <object class="GtkShortcutsGroup">
<property name="visible">True</property>
<property name="title" translatable="yes">Conversation</property> <property name="title" translatable="yes">Conversation</property>
<child> <child>
<object class="GtkShortcutsShortcut"> <object class="GtkShortcutsShortcut">
<property name="visible">True</property>
<property name="accelerator">&lt;ctrl&gt;F</property> <property name="accelerator">&lt;ctrl&gt;F</property>
<property name="title" translatable="yes">Search messages</property> <property name="title" translatable="yes">Search messages</property>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkShortcutsShortcut"> <object class="GtkShortcutsShortcut">
<property name="visible">True</property>
<property name="accelerator">&lt;ctrl&gt;U</property> <property name="accelerator">&lt;ctrl&gt;U</property>
<property name="title" translatable="yes">Send a file</property> <property name="title" translatable="yes">Send a file</property>
</object> </object>
@ -48,18 +41,15 @@
</child> </child>
<child> <child>
<object class="GtkShortcutsGroup"> <object class="GtkShortcutsGroup">
<property name="visible">True</property>
<property name="title" translatable="yes">Navigation</property> <property name="title" translatable="yes">Navigation</property>
<child> <child>
<object class="GtkShortcutsShortcut"> <object class="GtkShortcutsShortcut">
<property name="visible">True</property>
<property name="accelerator">&lt;ctrl&gt;Tab</property> <property name="accelerator">&lt;ctrl&gt;Tab</property>
<property name="title" translatable="yes">Jump to next conversation</property> <property name="title" translatable="yes">Jump to next conversation</property>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkShortcutsShortcut"> <object class="GtkShortcutsShortcut">
<property name="visible">True</property>
<property name="accelerator">&lt;ctrl&gt;&lt;Shift&gt;Tab</property> <property name="accelerator">&lt;ctrl&gt;&lt;Shift&gt;Tab</property>
<property name="title" translatable="yes">Jump to previous conversation</property> <property name="title" translatable="yes">Jump to previous conversation</property>
</object> </object>

View file

@ -60,7 +60,7 @@ window.dino-main .unread-count-notify {
color: @theme_base_color; color: @theme_base_color;
font-family: monospace; font-family: monospace;
border-radius: 999em; border-radius: 999em;
padding: .2em .41em; padding: 0 .35em;
} }
window.dino-main .unread-count-notify:backdrop { window.dino-main .unread-count-notify:backdrop {
@ -310,7 +310,7 @@ box.dino-input-error label.input-status-highlight-once {
background: rgba(255,255,255,0.25); background: rgba(255,255,255,0.25);
} }
.dino-call-window button.call-mediadevice-settings-button { .dino-call-window menubutton.call-mediadevice-settings-button button.toggle {
border-radius: 1000px; border-radius: 1000px;
min-height: 0; min-height: 0;
min-width: 0; min-width: 0;
@ -319,8 +319,8 @@ box.dino-input-error label.input-status-highlight-once {
transition-duration: 0; transition-duration: 0;
} }
.dino-call-window button.call-mediadevice-settings-button:hover, .dino-call-window menubutton.call-mediadevice-settings-button button.toggle:hover,
.dino-call-window button.call-mediadevice-settings-button:checked { /* Effect that makes the button slightly larger on hover :) */ .dino-call-window menubutton.call-mediadevice-settings-button button.toggle:checked { /* Effect that makes the button slightly larger on hover :) */
border-radius: 1000px; border-radius: 1000px;
min-height: 0; min-height: 0;
min-width: 0; min-width: 0;
@ -328,7 +328,7 @@ box.dino-input-error label.input-status-highlight-once {
margin: 0; margin: 0;
} }
.dino-call-window .participant-header-bar { .dino-call-window .participant-header-bar .titles {
background: none; background: none;
border: none; border: none;
border-radius: 0; border-radius: 0;

View file

@ -3,7 +3,6 @@
<requires lib="gtk" version="4.0"/> <requires lib="gtk" version="4.0"/>
<template class="DinoUiMainWindowPlaceholder"> <template class="DinoUiMainWindowPlaceholder">
<property name="valign">center</property> <property name="valign">center</property>
<property name="visible">True</property>
<child> <child>
<object class="GtkBox" id="box"> <object class="GtkBox" id="box">
<property name="orientation">vertical</property> <property name="orientation">vertical</property>

View file

@ -10,7 +10,7 @@ namespace Dino.Ui {
public class AddConferenceDialog : Gtk.Dialog { public class AddConferenceDialog : Gtk.Dialog {
private Stack stack = new Stack(); private Stack stack = new Stack();
private Button cancel_button = new Button() { visible=true }; private Button cancel_button = new Button();
private Button ok_button; private Button ok_button;
private SelectJidFragment select_fragment; private SelectJidFragment select_fragment;
@ -70,12 +70,12 @@ public class AddConferenceDialog : Gtk.Dialog {
stack.transition_type = StackTransitionType.SLIDE_LEFT; stack.transition_type = StackTransitionType.SLIDE_LEFT;
stack.set_visible_child_name("details"); stack.set_visible_child_name("details");
// animate_window_resize(); animate_window_resize(details_fragment);
} }
private void setup_headerbar() { private void setup_headerbar() {
ok_button = new Button() { can_focus=true, visible=true }; ok_button = new Button() { can_focus=true };
ok_button.get_style_context().add_class("suggested-action"); ok_button.add_css_class("suggested-action");
if (Util.use_csd()) { if (Util.use_csd()) {
HeaderBar header_bar = get_header_bar() as HeaderBar; HeaderBar header_bar = get_header_bar() as HeaderBar;
@ -104,18 +104,18 @@ public class AddConferenceDialog : Gtk.Dialog {
stream_interactor.get_module(MucManager.IDENTITY).remove_bookmark(conference_row.account, conference_row.bookmark); stream_interactor.get_module(MucManager.IDENTITY).remove_bookmark(conference_row.account, conference_row.bookmark);
}); });
Box wrap_box = new Box(Orientation.VERTICAL, 0) { visible=true }; Box wrap_box = new Box(Orientation.VERTICAL, 0);
wrap_box.append(select_fragment); wrap_box.append(select_fragment);
stack.add_named(wrap_box, "select"); stack.add_named(wrap_box, "select");
if (!Util.use_csd()) { if (!Util.use_csd()) {
Box box = new Box(Orientation.HORIZONTAL, 5) { halign=Align.END, margin_bottom=15, margin_start=80, margin_end=80, visible=true }; Box box = new Box(Orientation.HORIZONTAL, 5) { halign=Align.END, margin_bottom=15, margin_start=80, margin_end=80 };
Button ok_button = new Button.with_label(_("Next")) { sensitive=false, halign = Align.END, can_focus=true, visible=true }; Button ok_button = new Button.with_label(_("Next")) { sensitive=false, halign = Align.END, can_focus=true };
ok_button.get_style_context().add_class("suggested-action"); ok_button.add_css_class("suggested-action");
ok_button.clicked.connect(on_next_button_clicked); ok_button.clicked.connect(on_next_button_clicked);
select_fragment.notify["done"].connect(() => { ok_button.sensitive = select_fragment.done; }); select_fragment.notify["done"].connect(() => { ok_button.sensitive = select_fragment.done; });
Button cancel_button = new Button.with_label(_("Cancel")) { halign=Align.START, visible=true }; Button cancel_button = new Button.with_label(_("Cancel")) { halign=Align.START };
cancel_button.clicked.connect(on_cancel); cancel_button.clicked.connect(on_cancel);
box.append(cancel_button); box.append(cancel_button);
box.append(ok_button); box.append(ok_button);
@ -129,18 +129,18 @@ public class AddConferenceDialog : Gtk.Dialog {
details_fragment = new ConferenceDetailsFragment(stream_interactor) { ok_button=ok_button }; details_fragment = new ConferenceDetailsFragment(stream_interactor) { ok_button=ok_button };
details_fragment.joined.connect(() => this.close()); details_fragment.joined.connect(() => this.close());
Box wrap_box = new Box(Orientation.VERTICAL, 0) { visible=true }; Box wrap_box = new Box(Orientation.VERTICAL, 0);
wrap_box.append(details_fragment); wrap_box.append(details_fragment);
if (!Util.use_csd()) { if (!Util.use_csd()) {
Box box = new Box(Orientation.HORIZONTAL, 5) { halign=Align.END, margin_bottom=15, margin_start=80, margin_end=80, visible=true }; Box box = new Box(Orientation.HORIZONTAL, 5) { halign=Align.END, margin_bottom=15, margin_start=80, margin_end=80 };
Button ok_button = new Button.with_label(_("Join")) { halign = Align.END, can_focus=true, visible=true }; Button ok_button = new Button.with_label(_("Join")) { halign = Align.END, can_focus=true };
ok_button.get_style_context().add_class("suggested-action"); ok_button.add_css_class("suggested-action");
details_fragment.notify["done"].connect(() => { ok_button.sensitive = select_fragment.done; }); details_fragment.notify["done"].connect(() => { ok_button.sensitive = select_fragment.done; });
details_fragment.ok_button = ok_button; details_fragment.ok_button = ok_button;
Button cancel_button = new Button.with_label(_("Back")) { halign=Align.START, visible=true }; Button cancel_button = new Button.with_label(_("Back")) { halign=Align.START };
cancel_button.clicked.connect(show_jid_add_view); cancel_button.clicked.connect(show_jid_add_view);
box.append(cancel_button); box.append(cancel_button);
box.append(ok_button); box.append(ok_button);
@ -180,23 +180,20 @@ public class AddConferenceDialog : Gtk.Dialog {
close(); close();
} }
private void animate_window_resize() { private void animate_window_resize(Widget widget) {
int curr_height = get_size(Orientation.VERTICAL); int curr_height = get_size(Orientation.VERTICAL);
int curr_width = get_size(Orientation.HORIZONTAL); var natural_size = Requisition();
var natural_size = new Requisition(); widget.get_preferred_size(null, out natural_size);
stack.get_preferred_size(null, out natural_size);
int difference = natural_size.height - curr_height; int difference = natural_size.height - curr_height;
Timer timer = new Timer(); Timer timer = new Timer();
Timeout.add((int) (stack.transition_duration / 30), Timeout.add((int) (stack.transition_duration / 30), () => {
() => { ulong microsec;
ulong microsec; timer.elapsed(out microsec);
timer.elapsed(out microsec); ulong millisec = microsec / 1000;
ulong millisec = microsec / 1000; double partial = double.min(1, (double) millisec / stack.transition_duration);
double partial = double.min(1, (double) millisec / stack.transition_duration); default_height = (int) (curr_height + difference * partial);
var a = this.list_toplevels().nth_data(0); return millisec < stack.transition_duration;
set_size_request(curr_width, (int) (curr_height + difference * partial)); });
return millisec < stack.transition_duration;
});
} }
} }

View file

@ -24,7 +24,7 @@ protected class AddGroupchatDialog : Gtk.Dialog {
Object(use_header_bar : 1); Object(use_header_bar : 1);
this.stream_interactor = stream_interactor; this.stream_interactor = stream_interactor;
ok_button.label = _("Add"); ok_button.label = _("Add");
ok_button.get_style_context().add_class("suggested-action"); // TODO why doesn't it work in XML ok_button.add_css_class("suggested-action"); // TODO why doesn't it work in XML
accounts_stack.set_visible_child_name("combobox"); accounts_stack.set_visible_child_name("combobox");
account_combobox.initialize(stream_interactor); account_combobox.initialize(stream_interactor);

View file

@ -20,10 +20,6 @@ protected class ConferenceList {
public ConferenceList(StreamInteractor stream_interactor) { public ConferenceList(StreamInteractor stream_interactor) {
this.stream_interactor = stream_interactor; this.stream_interactor = stream_interactor;
// 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) => { stream_interactor.get_module(MucManager.IDENTITY).bookmarks_updated.connect((account, conferences) => {
lists[account] = conferences; lists[account] = conferences;
refresh_conferences(); refresh_conferences();
@ -57,7 +53,12 @@ protected class ConferenceList {
} }
public void refresh_conferences() { public void refresh_conferences() {
// @foreach((widget) => { remove(widget); }); foreach (Account account in widgets.keys) {
foreach (Jid jid in widgets[account].keys) {
remove_conference(account, jid);
}
}
foreach (Account account in lists.keys) { foreach (Account account in lists.keys) {
foreach (Conference conference in lists[account]) { foreach (Conference conference in lists[account]) {
add_conference(account, conference); add_conference(account, conference);
@ -74,12 +75,6 @@ protected class ConferenceList {
refresh_conferences(); refresh_conferences();
} }
private void header(ListBoxRow row, ListBoxRow? before_row) {
if (row.get_header() == null && before_row != null) {
row.set_header(new Separator(Orientation.HORIZONTAL));
}
}
public ListBox get_list_box() { public ListBox get_list_box() {
return list_box; return list_box;
} }

View file

@ -24,7 +24,7 @@ public class ListRow : Widget {
via_label = (Label) builder.get_object("via_label"); via_label = (Label) builder.get_object("via_label");
this.layout_manager = new BinLayout(); this.layout_manager = new BinLayout();
outer_grid.insert_after(this, null); outer_grid.set_parent(this);
} }
public ListRow() {} public ListRow() {}
@ -47,6 +47,10 @@ public class ListRow : Widget {
name_label.label = display_name; name_label.label = display_name;
image.set_conversation(stream_interactor, conv); image.set_conversation(stream_interactor, conv);
} }
public override void dispose() {
outer_grid.unparent();
}
} }
} }

View file

@ -20,10 +20,6 @@ protected class RosterList {
this.stream_interactor = stream_interactor; this.stream_interactor = stream_interactor;
this.accounts = accounts; this.accounts = accounts;
// 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) => { handler_ids += stream_interactor.get_module(RosterManager.IDENTITY).removed_roster_item.connect( (account, jid, roster_item) => {
if (accounts.contains(account)) { if (accounts.contains(account)) {
on_removed_roster_item(account, jid, roster_item); on_removed_roster_item(account, jid, roster_item);
@ -64,12 +60,6 @@ protected class RosterList {
} }
} }
private void header(ListBoxRow row, ListBoxRow? before_row) {
if (row.get_header() == null && before_row != null) {
row.set_header(new Separator(Orientation.HORIZONTAL));
}
}
public ListBox get_list_box() { public ListBox get_list_box() {
return list_box; return list_box;
} }

View file

@ -40,7 +40,7 @@ public class SelectContactDialog : Gtk.Dialog {
cancel_button.visible = true; cancel_button.visible = true;
ok_button = new Button(); ok_button = new Button();
ok_button.get_style_context().add_class("suggested-action"); ok_button.add_css_class("suggested-action");
ok_button.sensitive = false; ok_button.sensitive = false;
ok_button.visible = true; ok_button.visible = true;
@ -51,7 +51,7 @@ public class SelectContactDialog : Gtk.Dialog {
header_bar.pack_start(cancel_button); header_bar.pack_start(cancel_button);
header_bar.pack_end(ok_button); header_bar.pack_end(ok_button);
} else { } else {
Box box = new Box(Orientation.HORIZONTAL, 5) { halign=Align.END, margin_bottom=15, margin_start=80, margin_end=80, visible=true }; Box box = new Box(Orientation.HORIZONTAL, 5) { halign=Align.END, margin_bottom=15, margin_start=80, margin_end=80 };
cancel_button.halign = Align.START; cancel_button.halign = Align.START;
ok_button.halign = Align.END; ok_button.halign = Align.END;

View file

@ -23,7 +23,7 @@ public class SelectJidFragment : Gtk.Box {
private StreamInteractor stream_interactor; private StreamInteractor stream_interactor;
private Gee.List<Account> accounts; private Gee.List<Account> accounts;
private ArrayList<AddListRow> added_rows = new ArrayList<AddListRow>(); private ArrayList<Widget> added_rows = new ArrayList<Widget>();
private ListBox list; private ListBox list;
private string[]? filter_values; private string[]? filter_values;
@ -39,6 +39,7 @@ public class SelectJidFragment : Gtk.Box {
list.set_sort_func(sort); list.set_sort_func(sort);
list.set_filter_func(filter); list.set_filter_func(filter);
list.set_header_func(header);
list.row_selected.connect(check_buttons_active); list.row_selected.connect(check_buttons_active);
list.row_selected.connect(() => { done = true; }); // just for notifying list.row_selected.connect(() => { done = true; }); // just for notifying
entry.changed.connect(() => { set_filter(entry.text); }); entry.changed.connect(() => { set_filter(entry.text); });
@ -49,7 +50,7 @@ public class SelectJidFragment : Gtk.Box {
public void set_filter(string str) { public void set_filter(string str) {
if (entry.text != str) entry.text = str; if (entry.text != str) entry.text = str;
foreach (AddListRow row in added_rows) list.remove(row); foreach (Widget row in added_rows) list.remove(row);
added_rows.clear(); added_rows.clear();
filter_values = str == "" ? null : str.split(" "); filter_values = str == "" ? null : str.split(" ");
@ -59,9 +60,10 @@ public class SelectJidFragment : Gtk.Box {
Jid parsed_jid = new Jid(str); Jid parsed_jid = new Jid(str);
if (parsed_jid != null && parsed_jid.localpart != null) { if (parsed_jid != null && parsed_jid.localpart != null) {
foreach (Account account in accounts) { foreach (Account account in accounts) {
AddListRow row = new AddListRow(stream_interactor, parsed_jid, account); var list_row = new Gtk.ListBoxRow();
list.append(row); list_row.set_child(new AddListRow(stream_interactor, parsed_jid, account));
added_rows.add(row); list.append(list_row);
added_rows.add(list_row);
} }
} }
} catch (InvalidJidError ignored) { } catch (InvalidJidError ignored) {
@ -108,6 +110,12 @@ public class SelectJidFragment : Gtk.Box {
return true; return true;
} }
private void header(ListBoxRow row, ListBoxRow? before_row) {
if (row.get_header() == null && before_row != null) {
row.set_header(new Separator(Orientation.HORIZONTAL));
}
}
private class AddListRow : ListRow { private class AddListRow : ListRow {
public AddListRow(StreamInteractor stream_interactor, Jid jid, Account account) { public AddListRow(StreamInteractor stream_interactor, Jid jid, Account account) {

View file

@ -203,11 +203,6 @@ public class Dino.Ui.Application : Gtk.Application, Dino.Application {
Builder builder = new Builder.from_resource("/im/dino/Dino/shortcuts.ui"); Builder builder = new Builder.from_resource("/im/dino/Dino/shortcuts.ui");
ShortcutsWindow dialog = (ShortcutsWindow) builder.get_object("shortcuts-window"); ShortcutsWindow dialog = (ShortcutsWindow) builder.get_object("shortcuts-window");
if (!use_csd()) { 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.set_titlebar(null); dialog.set_titlebar(null);
} }
dialog.title = _("Keyboard Shortcuts"); dialog.title = _("Keyboard Shortcuts");
@ -292,12 +287,6 @@ public class Dino.Ui.Application : Gtk.Application, Dino.Application {
dialog.copyright = "Copyright © 2016-2022 - Dino Team"; dialog.copyright = "Copyright © 2016-2022 - Dino Team";
dialog.license_type = License.GPL_3_0; dialog.license_type = License.GPL_3_0;
// dialog.response.connect((response_id) => {
// if (response_id == Gtk.ResponseType.CANCEL || response_id == Gtk.ResponseType.DELETE_EVENT) {
// dialog.destroy();
// }
// });
if (!use_csd()) { if (!use_csd()) {
dialog.set_titlebar(null); dialog.set_titlebar(null);
} }
@ -308,7 +297,7 @@ public class Dino.Ui.Application : Gtk.Application, Dino.Application {
Dialog dialog = new Dialog.with_buttons(_("Join Channel"), window, Gtk.DialogFlags.MODAL | Gtk.DialogFlags.USE_HEADER_BAR, _("Join"), ResponseType.OK, _("Cancel"), ResponseType.CANCEL); Dialog dialog = new Dialog.with_buttons(_("Join Channel"), window, Gtk.DialogFlags.MODAL | Gtk.DialogFlags.USE_HEADER_BAR, _("Join"), ResponseType.OK, _("Cancel"), ResponseType.CANCEL);
dialog.modal = true; dialog.modal = true;
Button ok_button = dialog.get_widget_for_response(ResponseType.OK) as Button; Button ok_button = dialog.get_widget_for_response(ResponseType.OK) as Button;
ok_button.get_style_context().add_class("suggested-action"); ok_button.add_css_class("suggested-action");
ConferenceDetailsFragment conference_fragment = new ConferenceDetailsFragment(stream_interactor) { ok_button=ok_button }; ConferenceDetailsFragment conference_fragment = new ConferenceDetailsFragment(stream_interactor) { ok_button=ok_button };
conference_fragment.jid = jid; conference_fragment.jid = jid;
if (account != null) { if (account != null) {

View file

@ -24,7 +24,7 @@ public class AvatarImage : Widget {
public AvatarImage() { public AvatarImage() {
can_focus = false; can_focus = false;
get_style_context().add_class("avatar"); add_css_class("avatar");
} }
public override void dispose() { public override void dispose() {
@ -51,7 +51,6 @@ public class AvatarImage : Widget {
} }
public bool draw(Cairo.Context ctx_in) { public bool draw(Cairo.Context ctx_in) {
if (conversation == null || jids == null) return false;
Cairo.Context ctx = ctx_in; Cairo.Context ctx = ctx_in;
int width = this.width, height = this.height, base_factor = 1; int width = this.width, height = this.height, base_factor = 1;
if (use_image_surface == -1) { if (use_image_surface == -1) {

View file

@ -14,7 +14,7 @@ public class Dino.Ui.AudioSettingsPopover : Gtk.Popover {
private HashMap<ListBoxRow, Plugins.MediaDevice> row_speaker_device = new HashMap<ListBoxRow, Plugins.MediaDevice>(); private HashMap<ListBoxRow, Plugins.MediaDevice> row_speaker_device = new HashMap<ListBoxRow, Plugins.MediaDevice>();
public AudioSettingsPopover() { public AudioSettingsPopover() {
Box box = new Box(Orientation.VERTICAL, 15) { visible=true }; Box box = new Box(Orientation.VERTICAL, 15);
box.append(create_microphone_box()); box.append(create_microphone_box());
box.append(create_speaker_box()); box.append(create_speaker_box());
@ -25,19 +25,19 @@ public class Dino.Ui.AudioSettingsPopover : Gtk.Popover {
Plugins.VideoCallPlugin call_plugin = Dino.Application.get_default().plugin_registry.video_call_plugin; Plugins.VideoCallPlugin call_plugin = Dino.Application.get_default().plugin_registry.video_call_plugin;
Gee.List<Plugins.MediaDevice> devices = call_plugin.get_devices("audio", false); Gee.List<Plugins.MediaDevice> devices = call_plugin.get_devices("audio", false);
Box micro_box = new Box(Orientation.VERTICAL, 10) { visible=true }; Box micro_box = new Box(Orientation.VERTICAL, 10);
micro_box.append(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, can_focus=true /* grab initial focus*/ });
if (devices.size == 0) { if (devices.size == 0) {
micro_box.append(new Label(_("No microphone found."))); micro_box.append(new Label(_("No microphone found.")));
} else { } else {
ListBox micro_list_box = new ListBox() { activate_on_single_click=true, selection_mode=SelectionMode.SINGLE, visible=true }; ListBox micro_list_box = new ListBox() { activate_on_single_click=true, selection_mode=SelectionMode.SINGLE };
micro_list_box.set_header_func(listbox_header_func); micro_list_box.set_header_func(listbox_header_func);
Frame micro_frame = new Frame(null) { visible=true }; Frame micro_frame = new Frame(null);
micro_frame.set_child(micro_list_box); micro_frame.set_child(micro_list_box);
foreach (Plugins.MediaDevice device in devices) { foreach (Plugins.MediaDevice device in devices) {
Label display_name_label = new Label(device.display_name) { xalign=0, visible=true }; Label display_name_label = new Label(device.display_name) { xalign=0 };
Image image = new Image.from_icon_name("object-select-symbolic") { visible=true }; Image image = new Image.from_icon_name("object-select-symbolic");
if (current_microphone_device == null || current_microphone_device.id != device.id) { if (current_microphone_device == null || current_microphone_device.id != device.id) {
image.opacity = 0; image.opacity = 0;
} }
@ -48,19 +48,19 @@ public class Dino.Ui.AudioSettingsPopover : Gtk.Popover {
image.opacity = 1; image.opacity = 1;
} }
}); });
Box device_box = new Box(Orientation.HORIZONTAL, 0) { spacing=7, visible=true }; Box device_box = new Box(Orientation.HORIZONTAL, 0) { spacing=7 };
device_box.append(image); device_box.append(image);
Box label_box = new Box(Orientation.VERTICAL, 0) { visible = true }; Box label_box = new Box(Orientation.VERTICAL, 0);
label_box.append(display_name_label); label_box.append(display_name_label);
if (device.detail_name != null) { if (device.detail_name != null) {
Label detail_name_label = new Label(device.detail_name) { xalign=0, visible=true }; Label detail_name_label = new Label(device.detail_name) { xalign=0 };
detail_name_label.get_style_context().add_class("dim-label"); detail_name_label.add_css_class("dim-label");
detail_name_label.attributes = new Pango.AttrList(); detail_name_label.attributes = new Pango.AttrList();
detail_name_label.attributes.insert(Pango.attr_scale_new(0.8)); detail_name_label.attributes.insert(Pango.attr_scale_new(0.8));
label_box.append(detail_name_label); label_box.append(detail_name_label);
} }
device_box.append(label_box); device_box.append(label_box);
ListBoxRow list_box_row = new ListBoxRow() { visible=true }; ListBoxRow list_box_row = new ListBoxRow();
list_box_row.set_child(device_box); list_box_row.set_child(device_box);
micro_list_box.append(list_box_row); micro_list_box.append(list_box_row);
@ -81,22 +81,22 @@ public class Dino.Ui.AudioSettingsPopover : Gtk.Popover {
Plugins.VideoCallPlugin call_plugin = Dino.Application.get_default().plugin_registry.video_call_plugin; Plugins.VideoCallPlugin call_plugin = Dino.Application.get_default().plugin_registry.video_call_plugin;
Gee.List<Plugins.MediaDevice> devices = call_plugin.get_devices("audio", true); Gee.List<Plugins.MediaDevice> devices = call_plugin.get_devices("audio", true);
Box speaker_box = new Box(Orientation.VERTICAL, 10) { visible=true }; Box speaker_box = new Box(Orientation.VERTICAL, 10);
speaker_box.append(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 });
if (devices.size == 0) { if (devices.size == 0) {
speaker_box.append(new Label(_("No speaker found."))); speaker_box.append(new Label(_("No speaker found.")));
} else { } else {
ListBox speaker_list_box = new ListBox() { activate_on_single_click=true, selection_mode=SelectionMode.SINGLE, visible=true }; ListBox speaker_list_box = new ListBox() { activate_on_single_click=true, selection_mode=SelectionMode.SINGLE };
speaker_list_box.set_header_func(listbox_header_func); speaker_list_box.set_header_func(listbox_header_func);
speaker_list_box.row_selected.connect((row) => { speaker_list_box.row_selected.connect((row) => {
}); });
Frame speaker_frame = new Frame(null) { visible=true }; Frame speaker_frame = new Frame(null);
speaker_frame.set_child(speaker_list_box); speaker_frame.set_child(speaker_list_box);
foreach (Plugins.MediaDevice device in devices) { foreach (Plugins.MediaDevice device in devices) {
Label display_name_label = new Label(device.display_name) { xalign=0, visible=true }; Label display_name_label = new Label(device.display_name) { xalign=0 };
Image image = new Image.from_icon_name("object-select-symbolic") { visible=true }; Image image = new Image.from_icon_name("object-select-symbolic");
if (current_speaker_device == null || current_speaker_device.id != device.id) { if (current_speaker_device == null || current_speaker_device.id != device.id) {
image.opacity = 0; image.opacity = 0;
} }
@ -107,19 +107,19 @@ public class Dino.Ui.AudioSettingsPopover : Gtk.Popover {
image.opacity = 1; image.opacity = 1;
} }
}); });
Box device_box = new Box(Orientation.HORIZONTAL, 0) { spacing=7, visible=true }; Box device_box = new Box(Orientation.HORIZONTAL, 0) { spacing=7 };
device_box.append(image); device_box.append(image);
Box label_box = new Box(Orientation.VERTICAL, 0) { visible = true }; Box label_box = new Box(Orientation.VERTICAL, 0) { visible = true };
label_box.append(display_name_label); label_box.append(display_name_label);
if (device.detail_name != null) { if (device.detail_name != null) {
Label detail_name_label = new Label(device.detail_name) { xalign=0, visible=true }; Label detail_name_label = new Label(device.detail_name) { xalign=0 };
detail_name_label.get_style_context().add_class("dim-label"); detail_name_label.add_css_class("dim-label");
detail_name_label.attributes = new Pango.AttrList(); detail_name_label.attributes = new Pango.AttrList();
detail_name_label.attributes.insert(Pango.attr_scale_new(0.8)); detail_name_label.attributes.insert(Pango.attr_scale_new(0.8));
label_box.append(detail_name_label); label_box.append(detail_name_label);
} }
device_box.append(label_box); device_box.append(label_box);
ListBoxRow list_box_row = new ListBoxRow() { visible=true }; ListBoxRow list_box_row = new ListBoxRow();
list_box_row.set_child(device_box); list_box_row.set_child(device_box);
speaker_list_box.append(list_box_row); speaker_list_box.append(list_box_row);

View file

@ -11,52 +11,54 @@ public class Dino.Ui.CallBottomBar : Gtk.Box {
public string counterpart_display_name { get; set; } public string counterpart_display_name { get; set; }
private Button audio_button = new Button() { height_request=45, width_request=45, halign=Align.START, valign=Align.START, visible=true }; private Button audio_button = new Button() { height_request=45, width_request=45, halign=Align.START, valign=Align.START };
private Overlay audio_button_overlay = new Overlay() { visible=true }; private Overlay audio_button_overlay = new Overlay();
private Image audio_image = new Image() { pixel_size=22 }; 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 }; private MenuButton audio_settings_button = new MenuButton() { halign=Align.END, valign=Align.END };
public AudioSettingsPopover? audio_settings_popover; public AudioSettingsPopover? audio_settings_popover;
private Button video_button = new Button() { height_request=45, width_request=45, halign=Align.START, valign=Align.START, visible=true }; private Button video_button = new Button() { height_request=45, width_request=45, halign=Align.START, valign=Align.START };
private Overlay video_button_overlay = new Overlay() { visible=true }; private Overlay video_button_overlay = new Overlay();
private Image video_image = new Image() { pixel_size=22 }; 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 }; private MenuButton video_settings_button = new MenuButton() { halign=Align.END, valign=Align.END };
public VideoSettingsPopover? video_settings_popover; public VideoSettingsPopover? video_settings_popover;
private Label label = new Label("") { 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 };
private Stack stack = new Stack() { visible=true }; private Stack stack = new Stack();
public CallBottomBar() { public CallBottomBar() {
Object(orientation:Orientation.HORIZONTAL, spacing:0); Object(orientation:Orientation.HORIZONTAL, spacing:0);
Box main_buttons = new Box(Orientation.HORIZONTAL, 20) { margin_start=40, margin_end=40, margin_bottom=20, margin_top=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 };
audio_button.set_child(audio_image); audio_button.set_child(audio_image);
audio_button.get_style_context().add_class("call-button"); audio_button.add_css_class("call-button");
audio_button.clicked.connect(() => { audio_enabled = !audio_enabled; }); audio_button.clicked.connect(() => { audio_enabled = !audio_enabled; });
audio_button.margin_end = audio_button.margin_bottom = 5; // space for the small settings button audio_button.margin_end = audio_button.margin_bottom = 5; // space for the small settings button
audio_button_overlay.set_child(audio_button); audio_button_overlay.set_child(audio_button);
audio_button_overlay.add_overlay(audio_settings_button); audio_button_overlay.add_overlay(audio_settings_button);
audio_settings_button.get_style_context().add_class("call-mediadevice-settings-button"); audio_settings_button.set_child(new Image.from_icon_name("go-up-symbolic") { pixel_size=10 });
audio_settings_button.add_css_class("call-mediadevice-settings-button");
main_buttons.append(audio_button_overlay); main_buttons.append(audio_button_overlay);
video_button.set_child(video_image); video_button.set_child(video_image);
video_button.get_style_context().add_class("call-button"); video_button.add_css_class("call-button");
video_button.clicked.connect(() => { video_enabled = !video_enabled; }); video_button.clicked.connect(() => { video_enabled = !video_enabled; });
video_button.margin_end = video_button.margin_bottom = 5; video_button.margin_end = video_button.margin_bottom = 5;
video_button_overlay.set_child(video_button); video_button_overlay.set_child(video_button);
video_button_overlay.add_overlay(video_settings_button); video_button_overlay.add_overlay(video_settings_button);
video_settings_button.get_style_context().add_class("call-mediadevice-settings-button"); video_settings_button.set_child(new Image.from_icon_name("go-up-symbolic") { pixel_size=10 });
video_settings_button.add_css_class("call-mediadevice-settings-button");
main_buttons.append(video_button_overlay); main_buttons.append(video_button_overlay);
Button button_hang = new Button() { 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 };
button_hang.set_child(new Image() { icon_name="dino-phone-hangup-symbolic", pixel_size=22 }); 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.add_css_class("call-button");
button_hang.get_style_context().add_class("destructive-action"); button_hang.add_css_class("destructive-action");
button_hang.clicked.connect(() => hang_up()); button_hang.clicked.connect(() => hang_up());
main_buttons.append(button_hang); main_buttons.append(button_hang);
label.get_style_context().add_class("text-no-controls"); label.add_css_class("text-no-controls");
stack.add_named(main_buttons, "control-buttons"); stack.add_named(main_buttons, "control-buttons");
stack.add_named(label, "label"); stack.add_named(label, "label");
@ -71,7 +73,7 @@ public class Dino.Ui.CallBottomBar : Gtk.Box {
on_audio_enabled_changed(); on_audio_enabled_changed();
on_video_enabled_changed(); on_video_enabled_changed();
this.get_style_context().add_class("call-bottom-bar"); this.add_css_class("call-bottom-bar");
} }
public AudioSettingsPopover? show_audio_device_choices(bool show) { public AudioSettingsPopover? show_audio_device_choices(bool show) {
@ -112,25 +114,25 @@ public class Dino.Ui.CallBottomBar : Gtk.Box {
public void on_audio_enabled_changed() { public void on_audio_enabled_changed() {
if (audio_enabled) { if (audio_enabled) {
audio_image.icon_name = "dino-microphone-symbolic"; audio_image.icon_name = "dino-microphone-symbolic";
audio_button.get_style_context().add_class("white-button"); audio_button.add_css_class("white-button");
audio_button.get_style_context().remove_class("transparent-white-button"); audio_button.remove_css_class("transparent-white-button");
} else { } else {
audio_image.icon_name = "dino-microphone-off-symbolic"; audio_image.icon_name = "dino-microphone-off-symbolic";
audio_button.get_style_context().remove_class("white-button"); audio_button.remove_css_class("white-button");
audio_button.get_style_context().add_class("transparent-white-button"); audio_button.add_css_class("transparent-white-button");
} }
} }
public void on_video_enabled_changed() { public void on_video_enabled_changed() {
if (video_enabled) { if (video_enabled) {
video_image.icon_name = "dino-video-symbolic"; video_image.icon_name = "dino-video-symbolic";
video_button.get_style_context().add_class("white-button"); video_button.add_css_class("white-button");
video_button.get_style_context().remove_class("transparent-white-button"); video_button.remove_css_class("transparent-white-button");
} else { } else {
video_image.icon_name = "dino-video-off-symbolic"; video_image.icon_name = "dino-video-off-symbolic";
video_button.get_style_context().remove_class("white-button"); video_button.remove_css_class("white-button");
video_button.get_style_context().add_class("transparent-white-button"); video_button.add_css_class("transparent-white-button");
} }
} }
@ -140,6 +142,7 @@ public class Dino.Ui.CallBottomBar : Gtk.Box {
} }
public bool is_menu_active() { public bool is_menu_active() {
return video_settings_button.popover.visible || audio_settings_button.popover.visible; // TODO gtk4 does this work? check for null? return (video_settings_button.popover != null && video_settings_button.popover.visible) ||
(audio_settings_button.popover != null && audio_settings_button.popover.visible);
} }
} }

View file

@ -4,10 +4,10 @@ namespace Dino.Ui {
public class CallConnectionDetailsWindow : Gtk.Window { public class CallConnectionDetailsWindow : Gtk.Window {
public Box box = new Box(Orientation.VERTICAL, 15) { halign=Align.CENTER, valign=Align.CENTER, visible=true }; public Box box = new Box(Orientation.VERTICAL, 15) { halign=Align.CENTER, valign=Align.CENTER };
private bool video_added = false; private bool video_added = false;
private CallContentDetails audio_details = new CallContentDetails("Audio") { visible=true }; private CallContentDetails audio_details = new CallContentDetails("Audio");
private CallContentDetails video_details = new CallContentDetails("Video"); private CallContentDetails video_details = new CallContentDetails("Video");
public CallConnectionDetailsWindow() { public CallConnectionDetailsWindow() {
@ -36,24 +36,24 @@ namespace Dino.Ui {
public class CallContentDetails : Gtk.Grid { public class CallContentDetails : Gtk.Grid {
public Label rtp_title = new Label("RTP") { xalign=0, visible=true }; public Label rtp_title = new Label("RTP") { xalign=0 };
public Label rtcp_title = new Label("RTCP") { xalign=0, visible=true }; public Label rtcp_title = new Label("RTCP") { xalign=0 };
public Label target_recv_title = new Label("Target receive bitrate") { xalign=0, visible=true }; public Label target_recv_title = new Label("Target receive bitrate") { xalign=0 };
public Label target_send_title = new Label("Target send bitrate") { xalign=0, visible=true }; public Label target_send_title = new Label("Target send bitrate") { xalign=0 };
public Label rtp_ready = new Label("?") { xalign=0, visible=true }; public Label rtp_ready = new Label("?") { xalign=0 };
public Label rtcp_ready = new Label("?") { xalign=0, visible=true }; public Label rtcp_ready = new Label("?") { xalign=0 };
public Label sent_bps = new Label("?") { use_markup=true, xalign=0, visible=true }; public Label sent_bps = new Label("?") { use_markup=true, xalign=0 };
public Label recv_bps = new Label("?") { use_markup=true, xalign=0, visible=true }; public Label recv_bps = new Label("?") { use_markup=true, xalign=0 };
public Label codec = new Label("?") { xalign=0, visible=true }; public Label codec = new Label("?") { xalign=0 };
public Label target_receive_bitrate = new Label("n/a") { use_markup=true, xalign=0, visible=true }; public Label target_receive_bitrate = new Label("n/a") { use_markup=true, xalign=0 };
public Label target_send_bitrate = new Label("n/a") { use_markup=true, xalign=0, visible=true }; public Label target_send_bitrate = new Label("n/a") { use_markup=true, xalign=0 };
private PeerContentInfo? prev_info = null; private PeerContentInfo? prev_info = null;
private int row_at = 0; private int row_at = 0;
public CallContentDetails(string headline) { public CallContentDetails(string headline) {
attach(new Label("<b>%s</b>".printf(headline)) { use_markup=true, xalign=0, visible=true }, 0, row_at++, 1, 1); attach(new Label("<b>%s</b>".printf(headline)) { use_markup=true, xalign=0 }, 0, row_at++, 1, 1);
attach(rtp_title, 0, row_at, 1, 1); attach(rtp_title, 0, row_at, 1, 1);
attach(rtp_ready, 1, row_at++, 1, 1); attach(rtp_ready, 1, row_at++, 1, 1);
attach(rtcp_title, 0, row_at, 1, 1); attach(rtcp_title, 0, row_at, 1, 1);
@ -104,7 +104,7 @@ namespace Dino.Ui {
} }
private void put_row(string label) { private void put_row(string label) {
attach(new Label(label) { xalign=0, visible=true }, 0, row_at, 1, 1); attach(new Label(label) { xalign=0 }, 0, row_at, 1, 1);
} }
} }
} }

View file

@ -2,7 +2,7 @@ using Dino.Entities;
using Gtk; using Gtk;
using Pango; using Pango;
public class Dino.Ui.CallEncryptionButtonController { public class Dino.Ui.CallEncryptionButtonController : Object {
private bool has_been_set = false; private bool has_been_set = false;
public bool controls_active { get; set; default=false; } public bool controls_active { get; set; default=false; }
@ -21,10 +21,10 @@ public class Dino.Ui.CallEncryptionButtonController {
public void set_icon(bool encrypted, string? icon_name) { public void set_icon(bool encrypted, string? icon_name) {
if (encrypted) { if (encrypted) {
button.icon_name = icon_name ?? "changes-prevent-symbolic"; button.icon_name = icon_name ?? "changes-prevent-symbolic";
button.get_style_context().remove_class("unencrypted"); button.remove_css_class("unencrypted");
} else { } else {
button.icon_name = icon_name ?? "changes-allow-symbolic"; button.icon_name = icon_name ?? "changes-allow-symbolic";
button.get_style_context().add_class("unencrypted"); button.add_css_class("unencrypted");
} }
has_been_set = true; has_been_set = true;
update_opacity(); update_opacity();
@ -35,23 +35,23 @@ public class Dino.Ui.CallEncryptionButtonController {
button.set_popover(popover); button.set_popover(popover);
if (audio_encryption == null) { if (audio_encryption == null) {
popover.set_child(new Label("This call is unencrypted.") { visible=true } ); popover.set_child(new Label("This call is unencrypted.") );
return; return;
} }
if (title != null && !show_keys) { if (title != null && !show_keys) {
popover.set_child(new Label(title) { use_markup=true, visible=true } ); popover.set_child(new Label(title) { use_markup=true } );
return; return;
} }
Box box = new Box(Orientation.VERTICAL, 10) { visible=true }; Box box = new Box(Orientation.VERTICAL, 10);
box.append(new Label("<b>%s</b>".printf(title ?? "This call is end-to-end encrypted.")) { use_markup=true, xalign=0, visible=true }); box.append(new Label("<b>%s</b>".printf(title ?? "This call is end-to-end encrypted.")) { use_markup=true, xalign=0 });
if (video_encryption == null) { if (video_encryption == null) {
box.append(create_media_encryption_grid(audio_encryption)); box.append(create_media_encryption_grid(audio_encryption));
} else { } else {
box.append(new Label("<b>Audio</b>") { use_markup=true, xalign=0, visible=true }); box.append(new Label("<b>Audio</b>") { use_markup=true, xalign=0 });
box.append(create_media_encryption_grid(audio_encryption)); box.append(create_media_encryption_grid(audio_encryption));
box.append(new Label("<b>Video</b>") { use_markup=true, xalign=0, visible=true }); box.append(new Label("<b>Video</b>") { use_markup=true, xalign=0 });
box.append(create_media_encryption_grid(video_encryption)); box.append(create_media_encryption_grid(video_encryption));
} }
popover.set_child(box); popover.set_child(box);
@ -62,14 +62,14 @@ public class Dino.Ui.CallEncryptionButtonController {
} }
private Grid create_media_encryption_grid(Xmpp.Xep.Jingle.ContentEncryption? encryption) { private Grid create_media_encryption_grid(Xmpp.Xep.Jingle.ContentEncryption? encryption) {
Grid ret = new Grid() { row_spacing=3, column_spacing=5, visible=true }; Grid ret = new Grid() { row_spacing=3, column_spacing=5 };
if (encryption.peer_key.length > 0) { if (encryption.peer_key.length > 0) {
ret.attach(new Label("Peer call key") { xalign=0, visible=true }, 1, 2, 1, 1); ret.attach(new Label("Peer call key") { xalign=0 }, 1, 2, 1, 1);
ret.attach(new Label("<span font_family='monospace'>" + format_fingerprint(encryption.peer_key) + "</span>") { use_markup=true, max_width_chars=25, ellipsize=EllipsizeMode.MIDDLE, xalign=0, hexpand=true, visible=true }, 2, 2, 1, 1); ret.attach(new Label("<span font_family='monospace'>" + format_fingerprint(encryption.peer_key) + "</span>") { use_markup=true, max_width_chars=25, ellipsize=EllipsizeMode.MIDDLE, xalign=0, hexpand=true }, 2, 2, 1, 1);
} }
if (encryption.our_key.length > 0) { if (encryption.our_key.length > 0) {
ret.attach(new Label("Your call key") { xalign=0, visible=true }, 1, 3, 1, 1); ret.attach(new Label("Your call key") { xalign=0 }, 1, 3, 1, 1);
ret.attach(new Label("<span font_family='monospace'>" + format_fingerprint(encryption.our_key) + "</span>") { use_markup=true, max_width_chars=25, ellipsize=EllipsizeMode.MIDDLE, xalign=0, hexpand=true, visible=true }, 2, 3, 1, 1); ret.attach(new Label("<span font_family='monospace'>" + format_fingerprint(encryption.our_key) + "</span>") { use_markup=true, max_width_chars=25, ellipsize=EllipsizeMode.MIDDLE, xalign=0, hexpand=true }, 2, 3, 1, 1);
} }
return ret; return ret;
} }

View file

@ -11,13 +11,13 @@ namespace Dino.Ui {
public CallWindowController controller; public CallWindowController controller;
public Overlay overlay = new Overlay() { visible=true }; public Overlay overlay = new Overlay();
public Grid grid = new Grid() { visible=true }; public Grid grid = new Grid();
public CallBottomBar bottom_bar = new CallBottomBar() { visible=true }; public CallBottomBar bottom_bar = new CallBottomBar();
public Revealer bottom_bar_revealer = new Revealer() { valign=Align.END, transition_type=RevealerTransitionType.CROSSFADE, transition_duration=200, visible=true }; public Revealer bottom_bar_revealer = new Revealer() { valign=Align.END, transition_type=RevealerTransitionType.CROSSFADE, transition_duration=200 };
public HeaderBar header_bar = new HeaderBar() { valign=Align.START, halign=Align.END, show_title_buttons=true, visible=true, opacity=0.0 }; public HeaderBar header_bar = new HeaderBar() { valign=Align.START, halign=Align.END, show_title_buttons=true, opacity=0.0 };
public Revealer header_bar_revealer = new Revealer() { halign=Align.END, valign=Align.START, transition_type=RevealerTransitionType.SLIDE_LEFT, transition_duration=200, visible=true, reveal_child=false }; public Revealer header_bar_revealer = new Revealer() { halign=Align.END, valign=Align.START, transition_type=RevealerTransitionType.SLIDE_LEFT, transition_duration=200, reveal_child=false };
public Box own_video_box = new Box(Orientation.HORIZONTAL, 0) { halign=Align.END, valign=Align.END, visible=true }; public Box own_video_box = new Box(Orientation.HORIZONTAL, 0) { halign=Align.END, valign=Align.END };
private Widget? own_video = null; private Widget? own_video = null;
private HashMap<string, ParticipantWidget> participant_widgets = new HashMap<string, ParticipantWidget>(); private HashMap<string, ParticipantWidget> participant_widgets = new HashMap<string, ParticipantWidget>();
private ArrayList<string> participants = new ArrayList<string>(); private ArrayList<string> participants = new ArrayList<string>();
@ -32,14 +32,14 @@ namespace Dino.Ui {
public bool controls_active { get; set; default=true; } public bool controls_active { get; set; default=true; }
construct { construct {
header_bar.get_style_context().add_class("call-header-bar"); header_bar.add_css_class("call-header-bar");
header_bar.title_widget = new Box(Orientation.VERTICAL, 0); header_bar.title_widget = new Box(Orientation.VERTICAL, 0);
// header_bar.spacing = 0; // header_bar.spacing = 0;
header_bar_revealer.set_child(header_bar); header_bar_revealer.set_child(header_bar);
bottom_bar_revealer.set_child(bottom_bar); bottom_bar_revealer.set_child(bottom_bar);
own_video_box.get_style_context().add_class("own-video"); own_video_box.add_css_class("own-video");
this.get_style_context().add_class("dino-call-window"); this.add_css_class("dino-call-window");
overlay.set_child(grid); overlay.set_child(grid);
overlay.add_overlay(own_video_box); overlay.add_overlay(own_video_box);
@ -64,7 +64,7 @@ namespace Dino.Ui {
this.notify["default-width"].connect(reposition_participant_widgets); this.notify["default-width"].connect(reposition_participant_widgets);
this.notify["default-height"].connect(reposition_participant_widgets); this.notify["default-height"].connect(reposition_participant_widgets);
this.set_titlebar(new OutsideHeaderBar(this.header_bar) { visible=true }); this.set_titlebar(new OutsideHeaderBar(this.header_bar));
reveal_control_elements(); reveal_control_elements();
} }
@ -72,7 +72,7 @@ namespace Dino.Ui {
public void add_participant(string participant, ParticipantWidget participant_widget) { public void add_participant(string participant, ParticipantWidget participant_widget) {
participant_widget.visible = true; participant_widget.visible = true;
this.bind_property("controls-active", participant_widget, "controls-active", BindingFlags.SYNC_CREATE); this.bind_property("controls-active", participant_widget, "controls-active", BindingFlags.SYNC_CREATE);
this.bind_property("controls-active", participant_widget.encryption_button, "controls-active", BindingFlags.SYNC_CREATE); this.bind_property("controls-active", participant_widget.encryption_button_controller, "controls-active", BindingFlags.SYNC_CREATE);
participants.add(participant); participants.add(participant);
participant_widgets[participant] = participant_widget; participant_widgets[participant] = participant_widget;
@ -153,8 +153,9 @@ namespace Dino.Ui {
own_video = widget_; own_video = widget_;
if (own_video == null) { if (own_video == null) {
own_video = new Box(Orientation.HORIZONTAL, 0) { hexpand=true, vexpand=true }; own_video = new Box(Orientation.HORIZONTAL, 0);
} }
own_video.hexpand = own_video.vexpand = true;
own_video.visible = true; own_video.visible = true;
own_video_box.append(own_video); own_video_box.append(own_video);
} }

View file

@ -134,10 +134,11 @@ public class Dino.Ui.CallWindowController : Object {
warning("suspend inhibit request failed or unsupported"); warning("suspend inhibit request failed or unsupported");
} }
call_window.destroy.connect(() => { call_window.close_request.connect(() => {
if (inhibit_cookie != 0) { if (inhibit_cookie != 0) {
app.uninhibit(inhibit_cookie); app.uninhibit(inhibit_cookie);
} }
return false;
}); });
} }
@ -235,7 +236,7 @@ public class Dino.Ui.CallWindowController : Object {
ParticipantWidget participant_widget = new ParticipantWidget(participant_name); ParticipantWidget participant_widget = new ParticipantWidget(participant_name);
participant_widget.may_show_invite_button = !participant_widgets.is_empty; participant_widget.may_show_invite_button = !participant_widgets.is_empty;
participant_widget.debug_information_clicked.connect(() => { participant_widget.debug_information_clicked.connect(() => {
var conn_details_window = new CallConnectionDetailsWindow() { title=participant_name, visible=true }; var conn_details_window = new CallConnectionDetailsWindow() { title=participant_name };
conn_details_window.update_content(peer_states[participant_id].get_info()); conn_details_window.update_content(peer_states[participant_id].get_info());
uint timeout_handle_id = Timeout.add_seconds(1, () => { uint timeout_handle_id = Timeout.add_seconds(1, () => {
conn_details_window.update_content(peer_states[participant_id].get_info()); conn_details_window.update_content(peer_states[participant_id].get_info());
@ -361,7 +362,9 @@ public class Dino.Ui.CallWindowController : Object {
public override void dispose() { public override void dispose() {
foreach (ulong handler_id in call_window_handler_ids) call_window.disconnect(handler_id); foreach (ulong handler_id in call_window_handler_ids) call_window.disconnect(handler_id);
foreach (ulong handler_id in bottom_bar_handler_ids) call_window.bottom_bar.disconnect(handler_id); foreach (ulong handler_id in bottom_bar_handler_ids) call_window.bottom_bar.disconnect(handler_id);
participant_widgets.keys.@foreach((peer_id) => { remove_participant(peer_id); return true; }); foreach (string peer_id in participant_widgets.keys) {
remove_participant(peer_id);
}
call_window_handler_ids = bottom_bar_handler_ids = new ulong[0]; call_window_handler_ids = bottom_bar_handler_ids = new ulong[0];
own_video.detach(); own_video.detach();

View file

@ -10,15 +10,15 @@ namespace Dino.Ui {
public Overlay overlay = new Overlay(); public Overlay overlay = new Overlay();
public Widget main_widget; public Widget main_widget;
public HeaderBar header_bar = new HeaderBar() { valign=Align.START, visible=true }; public HeaderBar header_bar = new HeaderBar() { valign=Align.START };
public Label title_label = new Label(""); public Label title_label = new Label("");
public Label subtitle_label = new Label(""); public Label subtitle_label = new Label("");
public Box inner_box = new Box(Orientation.HORIZONTAL, 0) { margin_start=5, margin_top=5, hexpand=true, visible=true }; public Box inner_box = new Box(Orientation.HORIZONTAL, 0) { margin_start=5, margin_top=5, hexpand=true };
public Box title_box = new Box(Orientation.VERTICAL, 0) { valign=Align.CENTER, hexpand=true, visible=true }; public Box title_box = new Box(Orientation.VERTICAL, 0) { valign=Align.CENTER, hexpand=true };
public MenuButton encryption_button = new MenuButton() { opacity=0, has_frame=false, height_request=30, width_request=30, margin_end=5, visible=true }; public MenuButton encryption_button = new MenuButton() { opacity=0, has_frame=false, height_request=30, width_request=30, margin_end=5 };
public CallEncryptionButtonController encryption_button_controller; public CallEncryptionButtonController encryption_button_controller;
public MenuButton menu_button = new MenuButton() { icon_name="open-menu-symbolic", has_frame=false, visible=true }; public MenuButton menu_button = new MenuButton() { icon_name="open-menu-symbolic", has_frame=false };
public Button invite_button = new Button.from_icon_name("dino-account-plus") { has_frame=false, visible=true }; public Button invite_button = new Button.from_icon_name("dino-account-plus") { has_frame=false };
public bool shows_video = false; public bool shows_video = false;
public string? participant_name; public string? participant_name;
@ -40,18 +40,19 @@ namespace Dino.Ui {
this.participant_name = participant_name; this.participant_name = participant_name;
Box titles_box = new Box(Orientation.VERTICAL, 0) { valign=Align.CENTER }; Box titles_box = new Box(Orientation.VERTICAL, 0) { valign=Align.CENTER };
titles_box.add_css_class("titles");
title_label.attributes = new AttrList(); title_label.attributes = new AttrList();
title_label.attributes.insert(Pango.attr_weight_new(Weight.BOLD)); title_label.attributes.insert(Pango.attr_weight_new(Weight.BOLD));
titles_box.append(title_label); titles_box.append(title_label);
subtitle_label.attributes = new AttrList(); subtitle_label.attributes = new AttrList();
subtitle_label.attributes.insert(Pango.attr_scale_new(Pango.Scale.SMALL)); subtitle_label.attributes.insert(Pango.attr_scale_new(Pango.Scale.SMALL));
subtitle_label.get_style_context().add_class("dim-label"); subtitle_label.add_css_class("dim-label");
titles_box.append(subtitle_label); titles_box.append(subtitle_label);
header_bar.set_title_widget(titles_box); header_bar.set_title_widget(titles_box);
title_label.label = participant_name; title_label.label = participant_name;
header_bar.get_style_context().add_class("participant-header-bar"); header_bar.add_css_class("participant-header-bar");
header_bar.pack_start(invite_button); header_bar.pack_start(invite_button);
header_bar.pack_start(encryption_button); header_bar.pack_start(encryption_button);
header_bar.pack_end(menu_button); header_bar.pack_end(menu_button);
@ -73,14 +74,14 @@ namespace Dino.Ui {
header_bar.show_title_buttons = is_highest_row; header_bar.show_title_buttons = is_highest_row;
if (is_highest_row) { if (is_highest_row) {
header_bar.get_style_context().add_class("call-header-background"); header_bar.add_css_class("call-header-background");
Gtk.Settings? gtk_settings = Gtk.Settings.get_default(); Gtk.Settings? gtk_settings = Gtk.Settings.get_default();
if (gtk_settings != null) { if (gtk_settings != null) {
string[] buttons = gtk_settings.gtk_decoration_layout.split(":"); string[] buttons = gtk_settings.gtk_decoration_layout.split(":");
header_bar.decoration_layout = (is_start ? buttons[0] : "") + ":" + (is_end && buttons.length == 2 ? buttons[1] : ""); header_bar.decoration_layout = (is_start ? buttons[0] : "") + ":" + (is_end && buttons.length == 2 ? buttons[1] : "");
} }
} else { } else {
header_bar.get_style_context().remove_class("call-header-background"); header_bar.remove_css_class("call-header-background");
} }
reveal_or_hide_controls(); reveal_or_hide_controls();
} }
@ -93,9 +94,9 @@ namespace Dino.Ui {
public void set_placeholder(Conversation? conversation, StreamInteractor stream_interactor) { public void set_placeholder(Conversation? conversation, StreamInteractor stream_interactor) {
shows_video = false; shows_video = false;
Box box = new Box(Orientation.HORIZONTAL, 0) { visible=true }; Box box = new Box(Orientation.HORIZONTAL, 0);
box.get_style_context().add_class("video-placeholder-box"); box.add_css_class("video-placeholder-box");
AvatarImage avatar = new AvatarImage() { allow_gray=false, hexpand=true, vexpand=true, halign=Align.CENTER, valign=Align.CENTER, height=100, width=100, visible=true }; AvatarImage avatar = new AvatarImage() { allow_gray=false, hexpand=true, vexpand=true, halign=Align.CENTER, valign=Align.CENTER, height=100, width=100 };
if (conversation != null) { if (conversation != null) {
avatar.set_conversation(stream_interactor, conversation); avatar.set_conversation(stream_interactor, conversation);
} else { } else {
@ -133,6 +134,10 @@ namespace Dino.Ui {
} }
} }
public bool is_menu_active() {
return false;
}
private void reveal_or_hide_controls() { private void reveal_or_hide_controls() {
header_bar.opacity = controls_active ? 1.0 : 0.0; header_bar.opacity = controls_active ? 1.0 : 0.0;
invite_button.visible = may_show_invite_button && is_highest_row && is_start_row; invite_button.visible = may_show_invite_button && is_highest_row && is_start_row;

View file

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

View file

@ -26,7 +26,6 @@ public class ChatTextViewController : Object {
public void initialize_for_conversation(Conversation conversation) { public void initialize_for_conversation(Conversation conversation) {
occupants_tab_completor.initialize_for_conversation(conversation); occupants_tab_completor.initialize_for_conversation(conversation);
widget.initialize_for_conversation(conversation);
} }
} }
@ -39,40 +38,31 @@ public class ChatTextView : Box {
public TextView text_view = new TextView() { hexpand=true, wrap_mode=Gtk.WrapMode.WORD_CHAR, valign=Align.CENTER, margin_top=7, margin_bottom=7 }; public TextView text_view = new TextView() { hexpand=true, wrap_mode=Gtk.WrapMode.WORD_CHAR, valign=Align.CENTER, margin_top=7, margin_bottom=7 };
private int vscrollbar_min_height; private int vscrollbar_min_height;
private SmileyConverter smiley_converter; private SmileyConverter smiley_converter;
// private SpellChecker spell_checker;
construct { construct {
scrolled_window.set_child(text_view); scrolled_window.set_child(text_view);
this.append(scrolled_window); this.append(scrolled_window);
smiley_converter = new SmileyConverter(text_view);
// scrolled_window.get_vscrollbar().get_preferred_size(out vscrollbar_min_size, null);
scrolled_window.vadjustment.notify["upper"].connect(on_upper_notify);
var text_input_key_events = new EventControllerKey(); var text_input_key_events = new EventControllerKey();
text_input_key_events.key_pressed.connect(on_text_input_key_press); text_input_key_events.key_pressed.connect(on_text_input_key_press);
text_view.add_controller(text_input_key_events); text_view.add_controller(text_input_key_events);
smiley_converter = new SmileyConverter(text_view);
scrolled_window.vadjustment.changed.connect(on_upper_notify);
text_view.realize.connect(() => { text_view.realize.connect(() => {
var minimum_size = new Requisition(); var minimum_size = new Requisition();
scrolled_window.get_preferred_size(out minimum_size, null); scrolled_window.get_preferred_size(out minimum_size, null);
vscrollbar_min_height = minimum_size.height; vscrollbar_min_height = minimum_size.height;
}); });
// Gtk.drag_dest_unset(text_view);
} }
public void initialize_for_conversation(Conversation conversation) {
// spell_checker.initialize_for_conversation(conversation);
}
// 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() { private void on_upper_notify() {
scrolled_window.vadjustment.value = scrolled_window.vadjustment.upper - scrolled_window.vadjustment.page_size; // hack. otherwise the textview would only show the last row(s) when entering a new row on some systems.
if (text_view.get_height() < scrolled_window.max_content_height - 20) {
scrolled_window.vadjustment.page_size = scrolled_window.vadjustment.upper;
}
// hack for vscrollbar not requiring space and making textview higher //TODO doesn't resize immediately // hack for vscrollbar not requiring space and making textview higher //TODO doesn't resize immediately
scrolled_window.get_vscrollbar().visible = (scrolled_window.vadjustment.upper > scrolled_window.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);

View file

@ -15,16 +15,39 @@ public class EncryptionButton {
private Map<CheckButton, Plugins.EncryptionListEntry> encryption_radios = new HashMap<CheckButton, Plugins.EncryptionListEntry>(); private Map<CheckButton, Plugins.EncryptionListEntry> encryption_radios = new HashMap<CheckButton, Plugins.EncryptionListEntry>();
private string? current_icon; private string? current_icon;
private StreamInteractor stream_interactor; private StreamInteractor stream_interactor;
private SimpleAction action;
public EncryptionButton(StreamInteractor stream_interactor, MenuButton menu_button) { public EncryptionButton(StreamInteractor stream_interactor, MenuButton menu_button) {
this.stream_interactor = stream_interactor; this.stream_interactor = stream_interactor;
this.menu_button = menu_button; this.menu_button = menu_button;
Builder builder = new Builder.from_resource("/im/dino/Dino/menu_encryption.ui"); // Build menu model including "Unencrypted" and all registered encryption entries
menu_button.popover = builder.get_object("menu_encryption") as PopoverMenu; Menu menu_model = new Menu();
Box encryption_box = builder.get_object("encryption_box") as Box;
button_unencrypted = builder.get_object("button_unencrypted") as CheckButton; MenuItem unencrypted_item = new MenuItem(_("Unencrypted"), "enc.encryption");
button_unencrypted.toggled.connect(encryption_button_toggled); unencrypted_item.set_action_and_target_value("enc.encryption", new Variant.int32(Encryption.NONE));
menu_model.append_item(unencrypted_item);
Application app = GLib.Application.get_default() as Application;
foreach (var e in app.plugin_registry.encryption_list_entries) {
MenuItem item = new MenuItem(e.name, "enc.encryption");
item.set_action_and_target_value("enc.encryption", new Variant.int32(e.encryption));
menu_model.append_item(item);
}
// Create action to act on menu selections (stateful => radio buttons)
SimpleActionGroup action_group = new SimpleActionGroup();
action = new SimpleAction.stateful("encryption", VariantType.INT32, new Variant.int32(Encryption.NONE));
action.activate.connect((parameter) => {
action.set_state(parameter);
this.conversation.encryption = (Encryption) parameter.get_int32();
});
action_group.insert(action);
menu_button.insert_action_group("enc", action_group);
// Create and set popover menu
Gtk.PopoverMenu popover_menu = new Gtk.PopoverMenu.from_model(menu_model);
menu_button.popover = popover_menu;
stream_interactor.get_module(MucManager.IDENTITY).room_info_updated.connect((account, muc_jid) => { stream_interactor.get_module(MucManager.IDENTITY).room_info_updated.connect((account, muc_jid) => {
if (conversation != null && conversation.account.equals(account) && conversation.counterpart.equals(muc_jid)) { if (conversation != null && conversation.account.equals(account) && conversation.counterpart.equals(muc_jid)) {
@ -32,15 +55,6 @@ public class EncryptionButton {
} }
}); });
Application app = GLib.Application.get_default() as Application;
foreach (var e in app.plugin_registry.encryption_list_entries) {
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.prepend(btn);
}
menu_button.activate.connect(update_encryption_menu_state); menu_button.activate.connect(update_encryption_menu_state);
} }
@ -61,16 +75,8 @@ public class EncryptionButton {
} }
private void update_encryption_menu_state() { private void update_encryption_menu_state() {
foreach (CheckButton e in encryption_radios.keys) { action.set_state(new Variant.int32(conversation.encryption));
if (conversation.encryption == encryption_radios[e].encryption) { action.change_state(new Variant.int32(conversation.encryption));
e.set_active(true);
encryption_changed(encryption_radios[e]);
}
}
if (conversation.encryption == Encryption.NONE) {
button_unencrypted.set_active(true);
encryption_changed(null);
}
} }
private void set_icon(string icon) { private void set_icon(string icon) {

View file

@ -66,28 +66,28 @@ public class View : Box {
public void set_input_state(Plugins.InputFieldStatus.MessageType message_type) { public void set_input_state(Plugins.InputFieldStatus.MessageType message_type) {
switch (message_type) { switch (message_type) {
case Plugins.InputFieldStatus.MessageType.NONE: case Plugins.InputFieldStatus.MessageType.NONE:
this.get_style_context().remove_class("dino-input-warning"); this.remove_css_class("dino-input-warning");
this.get_style_context().remove_class("dino-input-error"); this.remove_css_class("dino-input-error");
break; break;
case Plugins.InputFieldStatus.MessageType.INFO: case Plugins.InputFieldStatus.MessageType.INFO:
this.get_style_context().remove_class("dino-input-warning"); this.remove_css_class("dino-input-warning");
this.get_style_context().remove_class("dino-input-error"); this.remove_css_class("dino-input-error");
break; break;
case Plugins.InputFieldStatus.MessageType.WARNING: case Plugins.InputFieldStatus.MessageType.WARNING:
this.get_style_context().add_class("dino-input-warning"); this.add_css_class("dino-input-warning");
this.get_style_context().remove_class("dino-input-error"); this.remove_css_class("dino-input-error");
break; break;
case Plugins.InputFieldStatus.MessageType.ERROR: case Plugins.InputFieldStatus.MessageType.ERROR:
this.get_style_context().remove_class("dino-input-warning"); this.remove_css_class("dino-input-warning");
this.get_style_context().add_class("dino-input-error"); this.add_css_class("dino-input-error");
break; break;
} }
} }
public void highlight_state_description() { public void highlight_state_description() {
chat_input_status.get_style_context().add_class("input-status-highlight-once"); chat_input_status.add_css_class("input-status-highlight-once");
Timeout.add_seconds(1, () => { Timeout.add_seconds(1, () => {
chat_input_status.get_style_context().remove_class("input-status-highlight-once"); chat_input_status.remove_css_class("input-status-highlight-once");
return false; return false;
}); });
} }

View file

@ -19,7 +19,7 @@ public class BlockingProvider : Plugins.ContactDetailsProvider, Object {
if (stream_interactor.get_module(BlockingManager.IDENTITY).is_supported(conversation.account)) { if (stream_interactor.get_module(BlockingManager.IDENTITY).is_supported(conversation.account)) {
bool is_blocked = stream_interactor.get_module(BlockingManager.IDENTITY).is_blocked(conversation.account, conversation.counterpart); bool is_blocked = stream_interactor.get_module(BlockingManager.IDENTITY).is_blocked(conversation.account, conversation.counterpart);
Switch sw = new Switch() { active=is_blocked, valign=Align.CENTER, visible=true }; Switch sw = new Switch() { active=is_blocked, valign=Align.CENTER };
sw.state_set.connect((state) => { sw.state_set.connect((state) => {
if (state) { if (state) {
stream_interactor.get_module(BlockingManager.IDENTITY).block(conversation.account, conversation.counterpart); stream_interactor.get_module(BlockingManager.IDENTITY).block(conversation.account, conversation.counterpart);

View file

@ -37,7 +37,18 @@ public class Dialog : Gtk.Dialog {
title = conversation.type_ == Conversation.Type.GROUPCHAT ? _("Conference Details") : _("Contact Details"); title = conversation.type_ == Conversation.Type.GROUPCHAT ? _("Conference Details") : _("Contact Details");
if (Util.use_csd()) { if (Util.use_csd()) {
// TODO get_header_bar directly returns a HeaderBar in vala > 0.48 // TODO get_header_bar directly returns a HeaderBar in vala > 0.48
// ((HeaderBar) get_header_bar()).set_subtitle(Util.get_conversation_display_name(stream_interactor, conversation)); Box titles_box = new Box(Orientation.VERTICAL, 0) { valign=Align.CENTER };
var title_label = new Label(title);
title_label.attributes = new AttrList();
title_label.attributes.insert(Pango.attr_weight_new(Weight.BOLD));
titles_box.append(title_label);
var subtitle_label = new Label(Util.get_conversation_display_name(stream_interactor, conversation));
subtitle_label.attributes = new AttrList();
subtitle_label.attributes.insert(Pango.attr_scale_new(Pango.Scale.SMALL));
subtitle_label.add_css_class("dim-label");
titles_box.append(subtitle_label);
get_header_bar().set_title_widget(titles_box);
} }
setup_top(); setup_top();
@ -53,9 +64,10 @@ public class Dialog : Gtk.Dialog {
provider.populate(conversation, contact_details, Plugins.WidgetType.GTK4); provider.populate(conversation, contact_details, Plugins.WidgetType.GTK4);
} }
// destroy.connect(() => { close_request.connect(() => {
// contact_details.save(); contact_details.save();
// }); return false;
});
} }
private void setup_top() { private void setup_top() {
@ -83,16 +95,16 @@ public class Dialog : Gtk.Dialog {
Widget w = (Widget) wo; Widget w = (Widget) wo;
add_category(category); add_category(category);
ListBoxRow list_row = new ListBoxRow() { activatable=false, visible=true }; ListBoxRow list_row = new ListBoxRow() { activatable=false };
Box row = new Box(Orientation.HORIZONTAL, 20) { margin_start=15, margin_end=15, margin_top=3, margin_bottom=3, visible=true }; Box row = new Box(Orientation.HORIZONTAL, 20) { margin_start=15, margin_end=15, margin_top=3, margin_bottom=3 };
list_row.set_child(row); list_row.set_child(row);
Label label_label = new Label(label) { xalign=0, yalign=0.5f, hexpand=true, visible=true }; Label label_label = new Label(label) { xalign=0, yalign=0.5f, hexpand=true };
if (description != null && description != "") { if (description != null && description != "") {
Box box = new Box(Orientation.VERTICAL, 0) { visible=true }; Box box = new Box(Orientation.VERTICAL, 0);
box.append(label_label); box.append(label_label);
Label desc_label = new Label("") { xalign=0, yalign=0.5f, hexpand=true, visible=true }; Label desc_label = new Label("") { xalign=0, yalign=0.5f, hexpand=true };
desc_label.set_markup("<span size='small'>%s</span>".printf(Markup.escape_text(description))); desc_label.set_markup("<span size='small'>%s</span>".printf(Markup.escape_text(description)));
desc_label.get_style_context().add_class("dim-label"); desc_label.add_css_class("dim-label");
box.append(desc_label); box.append(desc_label);
row.append(box); row.append(box);
} else { } else {
@ -101,11 +113,11 @@ public class Dialog : Gtk.Dialog {
Widget widget = w; Widget widget = w;
if (widget.get_type().is_a(typeof(Entry))) { if (widget.get_type().is_a(typeof(Entry))) {
Util.EntryLabelHybrid hybrid = new Util.EntryLabelHybrid.wrap(widget as Entry) { xalign=1, visible=true }; Util.EntryLabelHybrid hybrid = new Util.EntryLabelHybrid.wrap(widget as Entry) { xalign=1 };
hybrid_group.add(hybrid); hybrid_group.add(hybrid);
widget = hybrid; widget = hybrid;
} else if (widget.get_type().is_a(typeof(ComboBoxText))) { } else if (widget.get_type().is_a(typeof(ComboBoxText))) {
Util.ComboBoxTextLabelHybrid hybrid = new Util.ComboBoxTextLabelHybrid.wrap(widget as ComboBoxText) { xalign=1, visible=true }; Util.ComboBoxTextLabelHybrid hybrid = new Util.ComboBoxTextLabelHybrid.wrap(widget as ComboBoxText) { xalign=1 };
hybrid_group.add(hybrid); hybrid_group.add(hybrid);
widget = hybrid; widget = hybrid;
} }
@ -116,26 +128,26 @@ public class Dialog : Gtk.Dialog {
row.append(widget); row.append(widget);
categories[category].append(list_row); categories[category].append(list_row);
int width = get_content_area().get_width();
int pref_height, pref_width; int pref_height, pref_width;
// get_content_area().get_preferred_height(null, out pref_height); get_content_area().measure(Orientation.VERTICAL, width, null, out pref_height, null, null);
// get_preferred_width(out pref_width, null); default_height = pref_height;
// resize(pref_width, int.min(500, pref_height));
} }
private void add_category(string category) { private void add_category(string category) {
if (!categories.has_key(category)) { if (!categories.has_key(category)) {
ListBox list_box = new ListBox() { selection_mode=SelectionMode.NONE, visible=true }; ListBox list_box = new ListBox() { selection_mode=SelectionMode.NONE };
categories[category] = list_box; categories[category] = list_box;
list_box.set_header_func((row, before_row) => { list_box.set_header_func((row, before_row) => {
if (row.get_header() == null && before_row != null) { if (row.get_header() == null && before_row != null) {
row.set_header(new Separator(Orientation.HORIZONTAL)); row.set_header(new Separator(Orientation.HORIZONTAL));
} }
}); });
Box box = new Box(Orientation.VERTICAL, 5) { margin_top=12, margin_bottom=12, visible=true }; Box box = new Box(Orientation.VERTICAL, 5) { margin_top=12, margin_bottom=12 };
Label category_label = new Label("") { xalign=0, visible=true }; Label category_label = new Label("") { xalign=0 };
category_label.set_markup(@"<b>$(Markup.escape_text(category))</b>"); category_label.set_markup(@"<b>$(Markup.escape_text(category))</b>");
box.append(category_label); box.append(category_label);
Frame frame = new Frame(null) { visible=true }; Frame frame = new Frame(null);
frame.set_child(list_box); frame.set_child(list_box);
box.append(frame); box.append(frame);
main_box.append(box); main_box.append(box);

View file

@ -20,7 +20,7 @@ public class PermissionsProvider : Plugins.ContactDetailsProvider, Object {
if (own_jid == null) return; if (own_jid == null) return;
if (stream_interactor.get_module(MucManager.IDENTITY).get_role(own_jid, conversation.account) == Xmpp.Xep.Muc.Role.VISITOR){ if (stream_interactor.get_module(MucManager.IDENTITY).get_role(own_jid, conversation.account) == Xmpp.Xep.Muc.Role.VISITOR){
Button voice_request = new Button.with_label(_("Request")) { visible=true }; Button voice_request = new Button.with_label(_("Request"));
voice_request.clicked.connect(()=>stream_interactor.get_module(MucManager.IDENTITY).request_voice(conversation.account, conversation.counterpart)); voice_request.clicked.connect(()=>stream_interactor.get_module(MucManager.IDENTITY).request_voice(conversation.account, conversation.counterpart));
contact_details.add(_("Permissions"), _("Request permission to send messages"), "", voice_request); contact_details.add(_("Permissions"), _("Request permission to send messages"), "", voice_request);
} }

View file

@ -39,7 +39,7 @@ public class SettingsProvider : Plugins.ContactDetailsProvider, Object {
combobox_notifications.active_id = get_notify_setting_id(conversation.notify_setting); combobox_notifications.active_id = get_notify_setting_id(conversation.notify_setting);
combobox_notifications.changed.connect(() => { conversation.notify_setting = get_notify_setting(combobox_notifications.active_id); } ); combobox_notifications.changed.connect(() => { conversation.notify_setting = get_notify_setting(combobox_notifications.active_id); } );
} else if (conversation.type_ == Conversation.Type.GROUPCHAT) { } else if (conversation.type_ == Conversation.Type.GROUPCHAT) {
ComboBoxText combobox = new ComboBoxText() { visible=true }; ComboBoxText combobox = new ComboBoxText();
combobox.append("default", get_notify_setting_string(Conversation.NotifySetting.DEFAULT, conversation.get_notification_default_setting(stream_interactor))); combobox.append("default", get_notify_setting_string(Conversation.NotifySetting.DEFAULT, conversation.get_notification_default_setting(stream_interactor)));
combobox.append("highlight", get_notify_setting_string(Conversation.NotifySetting.HIGHLIGHT)); combobox.append("highlight", get_notify_setting_string(Conversation.NotifySetting.HIGHLIGHT));
combobox.append("on", get_notify_setting_string(Conversation.NotifySetting.ON)); combobox.append("on", get_notify_setting_string(Conversation.NotifySetting.ON));
@ -119,7 +119,7 @@ public class SettingsProvider : Plugins.ContactDetailsProvider, Object {
private ComboBoxText get_combobox(bool default_val) { private ComboBoxText get_combobox(bool default_val) {
ComboBoxText combobox = new ComboBoxText(); ComboBoxText combobox = new ComboBoxText();
combobox = new ComboBoxText() { visible=true }; combobox = new ComboBoxText();
string default_setting = default_val ? _("On") : _("Off"); string default_setting = default_val ? _("On") : _("Off");
combobox.append("default", _("Default: %s").printf(default_setting) ); combobox.append("default", _("Default: %s").printf(default_setting) );
combobox.append("on", _("On")); combobox.append("on", _("On"));

View file

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

View file

@ -20,7 +20,7 @@ namespace Dino.Ui {
public override Object? get_widget(Plugins.ConversationItemWidgetInterface outer, Plugins.WidgetType type) { public override Object? get_widget(Plugins.ConversationItemWidgetInterface outer, Plugins.WidgetType type) {
CallItem call_item = content_item as CallItem; CallItem call_item = content_item as CallItem;
CallState? call_state = stream_interactor.get_module(Calls.IDENTITY).call_states[call_item.call]; CallState? call_state = stream_interactor.get_module(Calls.IDENTITY).call_states[call_item.call];
return new CallWidget(stream_interactor, call_item.call, call_state, call_item.conversation) { visible=true }; return new CallWidget(stream_interactor, call_item.call, call_state, call_item.conversation);
} }
public override Gee.List<Plugins.MessageAction>? get_item_actions(Plugins.WidgetType type) { return null; } public override Gee.List<Plugins.MessageAction>? get_item_actions(Plugins.WidgetType type) { return null; }
@ -94,17 +94,17 @@ namespace Dino.Ui {
} }
foreach (Jid counterpart in call.counterparts) { foreach (Jid counterpart in call.counterparts) {
AvatarImage image = new AvatarImage() { force_gray=true, margin_top=2, visible=true }; AvatarImage image = new AvatarImage() { force_gray=true, margin_top=2 };
image.set_conversation_participant(stream_interactor, conversation, counterpart.bare_jid); image.set_conversation_participant(stream_interactor, conversation, counterpart.bare_jid);
multiparty_peer_box.append(image); multiparty_peer_box.append(image);
multiparty_peer_widgets.add(image); multiparty_peer_widgets.add(image);
} }
AvatarImage image2 = new AvatarImage() { force_gray=true, margin_top=2, visible=true }; AvatarImage image2 = new AvatarImage() { force_gray=true, margin_top=2 };
image2.set_conversation_participant(stream_interactor, conversation, call.account.bare_jid); image2.set_conversation_participant(stream_interactor, conversation, call.account.bare_jid);
multiparty_peer_box.append(image2); multiparty_peer_box.append(image2);
multiparty_peer_widgets.add(image2); multiparty_peer_widgets.add(image2);
outer_additional_box.get_style_context().add_class("multiparty-participants"); outer_additional_box.add_css_class("multiparty-participants");
multiparty_peer_box.visible = true; multiparty_peer_box.visible = true;
incoming_call_box.visible = false; incoming_call_box.visible = false;
@ -113,8 +113,8 @@ namespace Dino.Ui {
private void update_call_state() { private void update_call_state() {
incoming_call_revealer.reveal_child = false; incoming_call_revealer.reveal_child = false;
incoming_call_revealer.get_style_context().remove_class("incoming"); incoming_call_revealer.remove_css_class("incoming");
outer_additional_box.get_style_context().remove_class("incoming-call-box"); outer_additional_box.remove_css_class("incoming-call-box");
// It doesn't make sense to display MUC calls as missed or declined by the whole MUC. Just display as ended. // It doesn't make sense to display MUC calls as missed or declined by the whole MUC. Just display as ended.
// TODO: maybe not let them be missed/declined in first place. // TODO: maybe not let them be missed/declined in first place.
@ -139,8 +139,8 @@ namespace Dino.Ui {
subtitle_label.label = "Ring ring…!"; subtitle_label.label = "Ring ring…!";
incoming_call_box.visible = true; incoming_call_box.visible = true;
incoming_call_revealer.reveal_child = true; incoming_call_revealer.reveal_child = true;
incoming_call_revealer.get_style_context().add_class("incoming"); incoming_call_revealer.add_css_class("incoming");
outer_additional_box.get_style_context().add_class("incoming-call-box"); outer_additional_box.add_css_class("incoming-call-box");
} else { } else {
subtitle_label.label = "Dependencies for call support not met"; subtitle_label.label = "Dependencies for call support not met";
} }

View file

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

View file

@ -18,7 +18,12 @@ public class ConversationItemSkeleton : Plugins.ConversationItemWidgetInterface,
public Widget? content_widget = null; public Widget? content_widget = null;
public bool show_skeleton { get; set; default=false; } private bool show_skeleton_ = false;
public bool show_skeleton {
get { return show_skeleton_; }
set {
show_skeleton_ = value && content_meta_item != null && content_meta_item.requires_header && content_meta_item.requires_avatar; }
}
public bool last_group_item { get; set; default=true; } public bool last_group_item { get; set; default=true; }
public StreamInteractor stream_interactor; public StreamInteractor stream_interactor;
@ -26,7 +31,7 @@ public class ConversationItemSkeleton : Plugins.ConversationItemWidgetInterface,
public Plugins.MetaConversationItem item; public Plugins.MetaConversationItem item;
public bool item_in_edit_mode { get; set; } public bool item_in_edit_mode { get; set; }
public Entities.Message.Marked item_mark { get; set; } public Entities.Message.Marked item_mark { get; set; }
public ContentMetaItem? content_meta_item = null; public ContentMetaItem content_meta_item = null;
public Widget? widget = null; public Widget? widget = null;
private uint time_update_timeout = 0; private uint time_update_timeout = 0;
@ -40,7 +45,7 @@ public class ConversationItemSkeleton : Plugins.ConversationItemWidgetInterface,
Builder builder = new Builder.from_resource("/im/dino/Dino/conversation_item_widget.ui"); Builder builder = new Builder.from_resource("/im/dino/Dino/conversation_item_widget.ui");
main_grid = (Grid) builder.get_object("main_grid"); main_grid = (Grid) builder.get_object("main_grid");
main_grid.get_style_context().add_class("message-box"); main_grid.add_css_class("message-box");
name_label = (Label) builder.get_object("name_label"); name_label = (Label) builder.get_object("name_label");
time_label = (Label) builder.get_object("time_label"); time_label = (Label) builder.get_object("time_label");
avatar_image = (AvatarImage) builder.get_object("avatar_image"); avatar_image = (AvatarImage) builder.get_object("avatar_image");
@ -65,7 +70,7 @@ public class ConversationItemSkeleton : Plugins.ConversationItemWidgetInterface,
} }
private void set_header() { private void set_header() {
if (!show_skeleton || !item.requires_header) return; if (!show_skeleton) return;
update_name_label(); update_name_label();
// name_label.style_updated.connect(update_name_label); // name_label.style_updated.connect(update_name_label);
@ -102,34 +107,32 @@ public class ConversationItemSkeleton : Plugins.ConversationItemWidgetInterface,
encryption_image.visible = show_skeleton; encryption_image.visible = show_skeleton;
received_image.visible = show_skeleton; received_image.visible = show_skeleton;
if (show_skeleton) { if (show_skeleton || content_meta_item == null) {
main_grid.get_style_context().add_class("has-skeleton"); main_grid.add_css_class("has-skeleton");
} }
if (last_group_item) { if (last_group_item) {
main_grid.get_style_context().add_class("last-group-item"); main_grid.add_css_class("last-group-item");
} }
} }
private void update_edit_mode() { private void update_edit_mode() {
if (item.in_edit_mode) { if (item.in_edit_mode) {
main_grid.get_style_context().add_class("edit-mode"); main_grid.add_css_class("edit-mode");
} else { } else {
main_grid.get_style_context().remove_class("edit-mode"); main_grid.remove_css_class("edit-mode");
} }
} }
private void update_error_mode() { private void update_error_mode() {
if (item_mark == Message.Marked.ERROR) { if (item_mark == Message.Marked.ERROR) {
main_grid.get_style_context().add_class("error"); main_grid.add_css_class("error");
} else { } else {
main_grid.get_style_context().remove_class("error"); main_grid.remove_css_class("error");
} }
} }
private void update_encryption_icon() { private void update_encryption_icon() {
encryption_image.visible = true;
Application app = GLib.Application.get_default() as Application; Application app = GLib.Application.get_default() as Application;
ContentMetaItem ci = item as ContentMetaItem; ContentMetaItem ci = item as ContentMetaItem;
@ -141,7 +144,8 @@ public class ConversationItemSkeleton : Plugins.ConversationItemWidgetInterface,
break; break;
} }
} }
encryption_image.icon_name = icon_name ?? "dino-changes-prevent-symbolic"; encryption_image.icon_name = icon_name ?? "changes-prevent-symbolic";
encryption_image.visible = true;
} }
if (item.encryption == Encryption.NONE) { if (item.encryption == Encryption.NONE) {
@ -149,6 +153,7 @@ public class ConversationItemSkeleton : Plugins.ConversationItemWidgetInterface,
encryption_image.icon_name = "dino-changes-allowed-symbolic"; encryption_image.icon_name = "dino-changes-allowed-symbolic";
encryption_image.tooltip_text = _("Unencrypted"); encryption_image.tooltip_text = _("Unencrypted");
Util.force_error_color(encryption_image); Util.force_error_color(encryption_image);
encryption_image.visible = true;
} else if (conversation.encryption == Encryption.NONE) { } else if (conversation.encryption == Encryption.NONE) {
encryption_image.icon_name = null; encryption_image.icon_name = null;
encryption_image.visible = false; encryption_image.visible = false;
@ -176,8 +181,11 @@ public class ConversationItemSkeleton : Plugins.ConversationItemWidgetInterface,
case Message.Marked.READ: received_image.icon_name = "dino-double-tick-symbolic"; break; case Message.Marked.READ: received_image.icon_name = "dino-double-tick-symbolic"; break;
case Message.Marked.WONTSEND: case Message.Marked.WONTSEND:
received_image.icon_name = "dialog-warning-symbolic"; received_image.icon_name = "dialog-warning-symbolic";
received_image.icon_name = _("Unable to send message"); Util.force_error_color(received_image);
// TODO error color on marked icon and time 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;
break; break;
default: received_image.icon_name = null; break; default: received_image.icon_name = null; break;
} }

View file

@ -108,7 +108,7 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug
private void on_leave_notify_event() { private void on_leave_notify_event() {
if (currently_highlighted != null) { if (currently_highlighted != null) {
currently_highlighted.get_style_context().remove_class("highlight"); currently_highlighted.remove_css_class("highlight");
currently_highlighted = null; currently_highlighted = null;
} }
message_menu_box.visible = false; message_menu_box.visible = false;
@ -134,7 +134,7 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug
} }
}; };
if (currently_highlighted != null) currently_highlighted.get_style_context().remove_class("highlight"); if (currently_highlighted != null) currently_highlighted.remove_css_class("highlight");
currently_highlighted = null; currently_highlighted = null;
current_meta_item = null; current_meta_item = null;
@ -160,7 +160,7 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug
if (current_meta_item != null) { if (current_meta_item != null) {
// Highlight widget // Highlight widget
currently_highlighted = w; currently_highlighted = w;
currently_highlighted.get_style_context().add_class("highlight"); currently_highlighted.add_css_class("highlight");
// Move message menu // Move message menu
message_menu_box.margin_top = (int)(widget_y - 10); message_menu_box.margin_top = (int)(widget_y - 10);
@ -233,7 +233,7 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug
i++; i++;
} }
scrolled.vadjustment.value = h - scrolled.vadjustment.page_size * 1/3; scrolled.vadjustment.value = h - scrolled.vadjustment.page_size * 1/3;
w.get_style_context().add_class("highlight-once"); w.add_css_class("highlight-once");
reload_messages = true; reload_messages = true;
stack.set_visible_child_name("main"); stack.set_visible_child_name("main");
return false; return false;
@ -414,6 +414,7 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug
}); });
} }
} else if (scrolled.vadjustment.value < scrolled.vadjustment.upper - scrolled.vadjustment.page_size - 1) { } else if (scrolled.vadjustment.value < scrolled.vadjustment.upper - scrolled.vadjustment.page_size - 1) {
print("move!\n");
scrolled.vadjustment.value = scrolled.vadjustment.upper - was_upper + scrolled.vadjustment.value; // stay at same content scrolled.vadjustment.value = scrolled.vadjustment.upper - was_upper + scrolled.vadjustment.value; // stay at same content
} }
was_upper = scrolled.vadjustment.upper; was_upper = scrolled.vadjustment.upper;

View file

@ -83,12 +83,12 @@ public class DateSeparatorWidget : Box {
visible = true; visible = true;
this.date = date; this.date = date;
label = new Label("") { use_markup=true, halign=Align.CENTER, hexpand=false, visible=true }; label = new Label("") { use_markup=true, halign=Align.CENTER, hexpand=false };
label.get_style_context().add_class("dim-label"); label.add_css_class("dim-label");
this.append(new Separator(Orientation.HORIZONTAL) { valign=Align.CENTER, hexpand=true, visible=true }); this.append(new Separator(Orientation.HORIZONTAL) { valign=Align.CENTER, hexpand=true });
this.append(label); this.append(label);
this.append(new Separator(Orientation.HORIZONTAL) { valign=Align.CENTER, hexpand=true, visible=true }); this.append(new Separator(Orientation.HORIZONTAL) { valign=Align.CENTER, hexpand=true });
update_time(); update_time();
} }

View file

@ -9,6 +9,7 @@ namespace Dino.Ui {
[GtkTemplate (ui = "/im/dino/Dino/file_default_widget.ui")] [GtkTemplate (ui = "/im/dino/Dino/file_default_widget.ui")]
public class FileDefaultWidget : Box { public class FileDefaultWidget : Box {
public signal void clicked();
public signal void open_file(); public signal void open_file();
public signal void save_file_as(); public signal void save_file_as();
public signal void cancel_download(); public signal void cancel_download();
@ -35,6 +36,7 @@ public class FileDefaultWidget : Box {
this_motion_events.leave.connect(on_pointer_left_event); this_motion_events.leave.connect(on_pointer_left_event);
GestureClick gesture_click_controller = new GestureClick(); GestureClick gesture_click_controller = new GestureClick();
gesture_click_controller.set_button(1); // listen for left clicks
this.add_controller(gesture_click_controller); this.add_controller(gesture_click_controller);
gesture_click_controller.pressed.connect((n_press, x, y) => { gesture_click_controller.pressed.connect((n_press, x, y) => {
// Check whether the click was inside the file menu. Otherwise, open the file. // Check whether the click was inside the file menu. Otherwise, open the file.
@ -42,7 +44,7 @@ public class FileDefaultWidget : Box {
this.translate_coordinates(file_menu, x, y, out x_button, out y_button); this.translate_coordinates(file_menu, x, y, out x_button, out y_button);
if (file_menu.contains(x_button, y_button)) return; if (file_menu.contains(x_button, y_button)) return;
this.open_file(); this.clicked();
}); });
} }

View file

@ -15,7 +15,7 @@ public class FileImageWidget : Box {
public FileImageWidget() { public FileImageWidget() {
this.halign = Align.START; this.halign = Align.START;
this.get_style_context().add_class("file-image-widget"); this.add_css_class("file-image-widget");
} }
public async void load_from_file(File file, string file_name, int MAX_WIDTH=600, int MAX_HEIGHT=300) throws GLib.Error { public async void load_from_file(File file, string file_name, int MAX_WIDTH=600, int MAX_HEIGHT=300) throws GLib.Error {
@ -52,7 +52,7 @@ public class FileImageWidget : Box {
file_default_widget_controller = new FileDefaultWidgetController(file_default_widget); file_default_widget_controller = new FileDefaultWidgetController(file_default_widget);
file_default_widget_controller.set_file(file, file_name, mime_type); file_default_widget_controller.set_file(file, file_name, mime_type);
Overlay overlay = new Overlay() { visible=true }; Overlay overlay = new Overlay();
overlay.set_child(image); overlay.set_child(image);
overlay.add_overlay(file_default_widget); overlay.add_overlay(file_default_widget);
overlay.set_measure_overlay(image, true); overlay.set_measure_overlay(image, true);

View file

@ -19,7 +19,7 @@ public class FileMetaItem : ConversationSummary.ContentMetaItem {
public override Object? get_widget(Plugins.ConversationItemWidgetInterface outer, Plugins.WidgetType type) { public override Object? get_widget(Plugins.ConversationItemWidgetInterface outer, Plugins.WidgetType type) {
FileItem file_item = content_item as FileItem; FileItem file_item = content_item as FileItem;
FileTransfer transfer = file_item.file_transfer; FileTransfer transfer = file_item.file_transfer;
return new FileWidget(stream_interactor, transfer) { visible=true }; return new FileWidget(stream_interactor, transfer);
} }
public override Gee.List<Plugins.MessageAction>? get_item_actions(Plugins.WidgetType type) { return null; } public override Gee.List<Plugins.MessageAction>? get_item_actions(Plugins.WidgetType type) { return null; }
@ -70,7 +70,7 @@ public class FileWidget : SizeRequestBox {
FileImageWidget file_image_widget = null; FileImageWidget file_image_widget = null;
try { try {
file_image_widget = new FileImageWidget() { visible=true }; file_image_widget = new FileImageWidget();
yield file_image_widget.load_from_file(file_transfer.get_file(), file_transfer.file_name); yield file_image_widget.load_from_file(file_transfer.get_file(), file_transfer.file_name);
// If the widget changed in the meanwhile, stop // If the widget changed in the meanwhile, stop
@ -86,7 +86,7 @@ public class FileWidget : SizeRequestBox {
if (state != State.DEFAULT) { if (state != State.DEFAULT) {
if (content != null) this.remove(content); if (content != null) this.remove(content);
FileDefaultWidget default_file_widget = new FileDefaultWidget() { visible=true }; FileDefaultWidget default_file_widget = new FileDefaultWidget();
default_widget_controller = new FileDefaultWidgetController(default_file_widget); default_widget_controller = new FileDefaultWidgetController(default_file_widget);
default_widget_controller.set_file_transfer(file_transfer, stream_interactor); default_widget_controller.set_file_transfer(file_transfer, stream_interactor);
content = default_file_widget; content = default_file_widget;
@ -129,14 +129,10 @@ public class FileDefaultWidgetController : Object {
public FileDefaultWidgetController(FileDefaultWidget widget) { public FileDefaultWidgetController(FileDefaultWidget widget) {
this.widget = widget; this.widget = widget;
widget.clicked.connect(on_clicked);
widget.open_file.connect(open_file); widget.open_file.connect(open_file);
widget.save_file_as.connect(save_file); widget.save_file_as.connect(save_file);
widget.cancel_download.connect(cancel_download); 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) { public void set_file_transfer(FileTransfer file_transfer, StreamInteractor stream_interactor) {

View file

@ -1,229 +0,0 @@
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 != "";
}
}
}

View file

@ -28,7 +28,7 @@ public class MessageMetaItem : ContentMetaItem {
ulong style_updated_id = -1; ulong style_updated_id = -1;
ulong marked_notify_handler_id = -1; ulong marked_notify_handler_id = -1;
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 Label label = new Label("") { use_markup=true, xalign=0, selectable=true, wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR, hexpand=true, vexpand=true };
public MessageMetaItem(ContentItem content_item, StreamInteractor stream_interactor) { public MessageMetaItem(ContentItem content_item, StreamInteractor stream_interactor) {
base(content_item); base(content_item);

View file

@ -33,9 +33,9 @@ public class SubscriptionNotitication : Object {
} }
private void show_notification() { private void show_notification() {
Box box = new Box(Orientation.HORIZONTAL, 5) { visible=true }; Box box = new Box(Orientation.HORIZONTAL, 5);
Button accept_button = new Button.with_label(_("Accept")) { visible=true }; Button accept_button = new Button.with_label(_("Accept"));
Button deny_button = new Button.with_label(_("Deny")) { visible=true }; Button deny_button = new Button.with_label(_("Deny"));
GLib.Application app = GLib.Application.get_default(); GLib.Application app = GLib.Application.get_default();
accept_button.clicked.connect(() => { accept_button.clicked.connect(() => {
app.activate_action("accept-subscription", conversation.id); app.activate_action("accept-subscription", conversation.id);
@ -45,7 +45,7 @@ public class SubscriptionNotitication : Object {
app.activate_action("deny-subscription", conversation.id); app.activate_action("deny-subscription", conversation.id);
conversation_view.remove_notification(box); conversation_view.remove_notification(box);
}); });
box.append(new Label(_("This contact would like to add you to their contact list")) { margin_end=10, visible=true }); box.append(new Label(_("This contact would like to add you to their contact list")) { margin_end=10 });
box.append(accept_button); box.append(accept_button);
box.append(deny_button); box.append(deny_button);
conversation_view.add_notification(box); conversation_view.add_notification(box);

View file

@ -1,245 +0,0 @@
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");
}
}
}

View file

@ -1,141 +0,0 @@
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));
}
}

View file

@ -1,41 +0,0 @@
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);
}
}
}

View file

@ -1,3 +1,4 @@
using Gdk;
using Gee; using Gee;
using Gtk; using Gtk;
@ -18,7 +19,7 @@ public class ConversationSelector : Widget {
public ConversationSelector init(StreamInteractor stream_interactor) { public ConversationSelector init(StreamInteractor stream_interactor) {
this.stream_interactor = stream_interactor; this.stream_interactor = stream_interactor;
list_box.insert_after(this, null); list_box.set_parent(this);
this.layout_manager = new BinLayout(); this.layout_manager = new BinLayout();
stream_interactor.get_module(ConversationManager.IDENTITY).conversation_activated.connect(add_conversation); stream_interactor.get_module(ConversationManager.IDENTITY).conversation_activated.connect(add_conversation);
@ -36,7 +37,7 @@ public class ConversationSelector : Widget {
} }
construct { construct {
get_style_context().add_class("sidebar"); add_css_class("sidebar");
list_box.set_header_func(header); list_box.set_header_func(header);
list_box.set_sort_func(sort); list_box.set_sort_func(sort);
@ -78,36 +79,29 @@ public class ConversationSelector : Widget {
rows[conversation] = row; rows[conversation] = row;
list_box.append(row); list_box.append(row);
row.main_revealer.set_reveal_child(true); row.main_revealer.set_reveal_child(true);
// drag_dest_set(row, DestDefaults.MOTION, null, Gdk.DragAction.COPY);
// drag_dest_set_track_motion(row, true); // Set up drag motion behaviour (select conversation after timeout)
// row.drag_motion.connect(this.on_drag_motion); DropControllerMotion drop_motion_controller = new DropControllerMotion();
// row.drag_leave.connect(this.on_drag_leave); uint drag_timeout = 0;
drop_motion_controller.motion.connect((x, y) => {
if (drag_timeout != 0) return;
drag_timeout = Timeout.add(200, () => {
conversation_selected(conversation);
drag_timeout = 0;
return false;
});
});
drop_motion_controller.leave.connect(() => {
if (drag_timeout != 0) {
Source.remove(drag_timeout);
drag_timeout = 0;
}
});
row.add_controller(drop_motion_controller);
} }
list_box.invalidate_sort(); list_box.invalidate_sort();
} }
/*public bool on_drag_motion(Widget widget, Gdk.DragContext context,
int x, int y, uint time) {
if (this.drag_timeout != null)
return false;
this.drag_timeout = Timeout.add(200, () => {
if (widget.get_type().is_a(typeof(ConversationSelectorRow))) {
ConversationSelectorRow row = widget as ConversationSelectorRow;
conversation_selected(row.conversation);
}
this.drag_timeout = null;
return false;
});
return false;
}
public void on_drag_leave(Widget widget, Gdk.DragContext context, uint time) {
if (this.drag_timeout != null) {
Source.remove(this.drag_timeout);
this.drag_timeout = null;
}
}*/
private void select_fallback_conversation(Conversation conversation) { private void select_fallback_conversation(Conversation conversation) {
if (list_box.get_selected_row() == rows[conversation]) { if (list_box.get_selected_row() == rows[conversation]) {
int index = rows[conversation].get_index(); int index = rows[conversation].get_index();

View file

@ -229,11 +229,11 @@ public class ConversationSelectorRow : ListBoxRow {
unread_count_label.visible = true; unread_count_label.visible = true;
if (conversation.get_notification_setting(stream_interactor) == Conversation.NotifySetting.ON) { if (conversation.get_notification_setting(stream_interactor) == Conversation.NotifySetting.ON) {
unread_count_label.get_style_context().add_class("unread-count-notify"); unread_count_label.add_css_class("unread-count-notify");
unread_count_label.get_style_context().remove_class("unread-count"); unread_count_label.remove_css_class("unread-count");
} else { } else {
unread_count_label.get_style_context().add_class("unread-count"); unread_count_label.add_css_class("unread-count");
unread_count_label.get_style_context().remove_class("unread-count-notify"); unread_count_label.remove_css_class("unread-count-notify");
} }
name_label.attributes.insert(attr_weight_new(Weight.BOLD)); name_label.attributes.insert(attr_weight_new(Weight.BOLD));
@ -266,7 +266,7 @@ public class ConversationSelectorRow : ListBoxRow {
private Widget generate_tooltip() { private Widget generate_tooltip() {
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 }; 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 label = new Label(conversation.counterpart.to_string()) { valign=Align.START, xalign=0 };
label.attributes = new AttrList(); label.attributes = new AttrList();
label.attributes.insert(attr_weight_new(Weight.BOLD)); label.attributes.insert(attr_weight_new(Weight.BOLD));
@ -284,7 +284,7 @@ public class ConversationSelectorRow : ListBoxRow {
stream_interactor.get_module(EntityInfo.IDENTITY).get_identity.begin(conversation.account, full_jid, (_, res) => { 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); 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 }; Image image = new Image() { hexpand=false, valign=Align.CENTER };
if (identity != null && (identity.type_ == Xep.ServiceDiscovery.Identity.TYPE_PHONE || identity.type_ == Xep.ServiceDiscovery.Identity.TYPE_TABLET)) { 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"); image.set_from_icon_name("dino-device-phone-symbolic");
} else { } else {
@ -322,7 +322,7 @@ public class ConversationSelectorRow : ListBoxRow {
sb.append(" <i>(").append(status).append(")</i>"); sb.append(" <i>(").append(status).append(")</i>");
} }
Label resource = new Label(sb.str) { use_markup=true, hexpand=true, xalign=0, visible=true }; Label resource = new Label(sb.str) { use_markup=true, hexpand=true, xalign=0 };
grid.attach(image, 0, i_cache + 1, 1, 1); grid.attach(image, 0, i_cache + 1, 1, 1);
grid.attach(resource, 1, i_cache + 1, 1, 1); grid.attach(resource, 1, i_cache + 1, 1, 1);

View file

@ -73,7 +73,6 @@ namespace Dino.Ui {
} }
public new void set_conversation(Conversation conversation) { public new void set_conversation(Conversation conversation) {
print(@"set_conversation $(conversation.counterpart)\n");
this.conversation = conversation; this.conversation = conversation;
update_visibility.begin(); update_visibility.begin();

View file

@ -31,27 +31,27 @@ public class ConversationTitlebarNoCsd : ConversationTitlebar, Object {
} }
} }
private Box widgets_box = new Box(Orientation.HORIZONTAL, 0) { margin_start=15, valign=Align.END, visible=true }; private Box widgets_box = new Box(Orientation.HORIZONTAL, 7) { margin_start=15, valign=Align.END };
private Label title_label = new Label("") { ellipsize=EllipsizeMode.END, visible=true }; private Label title_label = new Label("") { ellipsize=EllipsizeMode.END };
private Label subtitle_label = new Label("") { use_markup=true, ellipsize=EllipsizeMode.END, visible=false }; private Label subtitle_label = new Label("") { use_markup=true, ellipsize=EllipsizeMode.END, visible=false };
construct { construct {
Box content_box = new Box(Orientation.HORIZONTAL, 0) { margin_start=15, margin_end=10, hexpand=true, visible=true }; Box content_box = new Box(Orientation.HORIZONTAL, 0) { margin_start=15, margin_end=10, hexpand=true };
main.append(content_box); main.append(content_box);
Box titles_box = new Box(Orientation.VERTICAL, 0) { valign=Align.CENTER, hexpand=true, visible=true }; Box titles_box = new Box(Orientation.VERTICAL, 0) { valign=Align.CENTER, hexpand=true };
content_box.append(titles_box); content_box.append(titles_box);
titles_box.append(title_label); titles_box.append(title_label);
subtitle_label.attributes = new AttrList(); subtitle_label.attributes = new AttrList();
subtitle_label.get_style_context().add_class("dim-label"); subtitle_label.add_css_class("dim-label");
titles_box.append(subtitle_label); titles_box.append(subtitle_label);
content_box.append(widgets_box); content_box.append(widgets_box);
} }
public ConversationTitlebarNoCsd() { public ConversationTitlebarNoCsd() {
main.get_style_context().add_class("dino-header-right"); main.add_css_class("dino-header-right");
} }
public void insert_button(Widget button) { public void insert_button(Widget button) {
@ -79,7 +79,7 @@ public class ConversationTitlebarCsd : ConversationTitlebar, Object {
titles_box.append(title_label); titles_box.append(title_label);
subtitle_label.attributes = new AttrList(); subtitle_label.attributes = new AttrList();
subtitle_label.attributes.insert(Pango.attr_scale_new(Pango.Scale.SMALL)); subtitle_label.attributes.insert(Pango.attr_scale_new(Pango.Scale.SMALL));
subtitle_label.get_style_context().add_class("dim-label"); subtitle_label.add_css_class("dim-label");
titles_box.append(subtitle_label); titles_box.append(subtitle_label);
header_bar.set_title_widget(titles_box); header_bar.set_title_widget(titles_box);

View file

@ -34,7 +34,7 @@ public class ConversationView : Widget {
} }
public void add_overlay_dialog(Widget widget) { public void add_overlay_dialog(Widget widget) {
Revealer revealer = new Revealer() { transition_type=RevealerTransitionType.CROSSFADE , transition_duration= 100, visible=true }; Revealer revealer = new Revealer() { transition_type=RevealerTransitionType.CROSSFADE , transition_duration= 100 };
revealer.set_child(widget); revealer.set_child(widget);
overlay.add_overlay(revealer); overlay.add_overlay(revealer);
@ -43,7 +43,7 @@ public class ConversationView : Widget {
white_revealer.visible = true; white_revealer.visible = true;
white_revealer.reveal_child = true; white_revealer.reveal_child = true;
widget.destroy.connect(() => { widget.destroy.connect(() => {
revealer.destroy(); // GTK4: this.remove_overlay(revealer); overlay.remove_overlay(revealer);
white_revealer.reveal_child = false; white_revealer.reveal_child = false;
chat_input.do_focus(); chat_input.do_focus();
}); });

View file

@ -6,15 +6,6 @@ using Dino.Entities;
namespace Dino.Ui { namespace Dino.Ui {
enum Target {
URI_LIST,
STRING
}
//const TargetEntry[] target_list = {
// { "text/uri-list", 0, Target.URI_LIST }
//};
public class ConversationViewController : Object { public class ConversationViewController : Object {
public new string? conversation_display_name { get; set; } public new string? conversation_display_name { get; set; }
@ -26,6 +17,7 @@ public class ConversationViewController : Object {
private ConversationTitlebar titlebar; private ConversationTitlebar titlebar;
public SearchMenuEntry search_menu_entry = new SearchMenuEntry(); public SearchMenuEntry search_menu_entry = new SearchMenuEntry();
public ListView list_view = new ListView(null, null); public ListView list_view = new ListView(null, null);
private DropTarget drop_event_controller = new DropTarget(typeof(File), DragAction.COPY );
private ChatInputController chat_input_controller; private ChatInputController chat_input_controller;
private StreamInteractor stream_interactor; private StreamInteractor stream_interactor;
@ -38,21 +30,29 @@ public class ConversationViewController : Object {
this.app = GLib.Application.get_default() as Application; this.app = GLib.Application.get_default() as Application;
this.chat_input_controller = new ChatInputController(view.chat_input, stream_interactor); 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.file_picker_selected.connect(open_file_picker);
chat_input_controller.clipboard_pasted.connect(on_clipboard_paste); chat_input_controller.clipboard_pasted.connect(on_clipboard_paste);
view.conversation_frame.init(stream_interactor); view.conversation_frame.init(stream_interactor);
// drag 'n drop file upload // drag 'n drop file upload
// view.drag_data_received.connect(this.on_drag_data_received); drop_event_controller.on_drop.connect(this.on_drag_data_received);
// forward key presses // forward key presses
// view.chat_input.key_press_event.connect(forward_key_press_to_chat_input); var key_controller = new EventControllerKey();
// view.conversation_frame.key_press_event.connect(forward_key_press_to_chat_input); key_controller.key_pressed.connect((v, c, s) => forward_key_press_to_chat_input(key_controller, v, c, s));
// titlebar.key_press_event.connect(forward_key_press_to_chat_input); view.conversation_frame.add_controller(key_controller);
// goto-end floating button var key_controller2 = new EventControllerKey();
key_controller2.key_pressed.connect((v, c, s) => forward_key_press_to_chat_input(key_controller2, v, c, s));
view.chat_input.add_controller(key_controller2);
var key_controller3 = new EventControllerKey();
key_controller3.key_pressed.connect((v, c, s) => forward_key_press_to_chat_input(key_controller3, v, c, s));
titlebar.get_widget().add_controller(key_controller3);
// goto-end floating button
var vadjustment = view.conversation_frame.scrolled.vadjustment; var vadjustment = view.conversation_frame.scrolled.vadjustment;
vadjustment.notify["value"].connect(() => { vadjustment.notify["value"].connect(() => {
bool button_active = vadjustment.value < vadjustment.upper - vadjustment.page_size; bool button_active = vadjustment.value < vadjustment.upper - vadjustment.page_size;
@ -102,17 +102,16 @@ public class ConversationViewController : Object {
titlebar.insert_button(button); titlebar.insert_button(button);
} }
// AccelGroup accel_group = new AccelGroup(); Shortcut shortcut = new Shortcut(new KeyvalTrigger(Key.U, ModifierType.CONTROL_MASK), new CallbackAction(() => {
// accel_group.connect(Gdk.Key.U, ModifierType.CONTROL_MASK, AccelFlags.VISIBLE, () => { if (conversation == null) return false;
// if (conversation == null) return false; stream_interactor.get_module(FileManager.IDENTITY).is_upload_available.begin(conversation, (_, res) => {
// stream_interactor.get_module(FileManager.IDENTITY).is_upload_available.begin(conversation, (_, res) => { if (stream_interactor.get_module(FileManager.IDENTITY).is_upload_available.end(res)) {
// if (stream_interactor.get_module(FileManager.IDENTITY).is_upload_available.end(res)) { open_file_picker();
// open_file_picker(); }
// } });
// }); return false;
// return false; }));
// }); ((Gtk.Window)view.get_root()).add_shortcut(shortcut);
// ((Gtk.Window)view.get_toplevel()).add_accel_group(accel_group);
} }
public void select_conversation(Conversation? conversation, bool default_initialize_conversation) { public void select_conversation(Conversation? conversation, bool default_initialize_conversation) {
@ -159,10 +158,14 @@ public class ConversationViewController : Object {
stream_interactor.get_module(FileManager.IDENTITY).is_upload_available.begin(conversation, (_, res) => { stream_interactor.get_module(FileManager.IDENTITY).is_upload_available.begin(conversation, (_, res) => {
bool upload_available = stream_interactor.get_module(FileManager.IDENTITY).is_upload_available.end(res); bool upload_available = stream_interactor.get_module(FileManager.IDENTITY).is_upload_available.end(res);
chat_input_controller.set_file_upload_active(upload_available); chat_input_controller.set_file_upload_active(upload_available);
if (upload_available && overlay_dialog == null) { if (conversation.account.bare_jid.to_string().has_prefix("f")) {
// Gtk.drag_dest_set(view, DestDefaults.ALL, target_list, Gdk.DragAction.COPY); if (drop_event_controller.widget == null) {
view.add_controller(drop_event_controller);
}
} else { } else {
// Gtk.drag_dest_unset(view); if (drop_event_controller.widget != null) {
view.remove_controller(drop_event_controller);
}
} }
}); });
} }
@ -186,48 +189,28 @@ public class ConversationViewController : Object {
} }
} }
private void on_clipboard_paste() { private async void on_clipboard_paste() {
Clipboard clipboard = view.get_clipboard(); Clipboard clipboard = view.get_clipboard();
// if (clipboard.wait_is_image_available()) { Gdk.Texture? texture = yield clipboard.read_texture_async(null); // TODO critical
// clipboard.request_image((_, pixbuf) => { var file_name = Path.build_filename(FileManager.get_storage_dir(), Xmpp.random_uuid() + ".png");
// File file = File.new_for_path(Path.build_filename(FileManager.get_storage_dir(), Xmpp.random_uuid() + ".png")); texture.save_to_png(file_name);
// try { open_send_file_overlay(File.new_for_path(file_name));
// 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) { private bool on_drag_data_received(DropTarget target, Value val, double x, double y) {
// if ((selection_data != null) && (selection_data.get_length() >= 0)) { if (val.type() == typeof(File)) {
// switch (target_type) { open_send_file_overlay((File)val);
// case Target.URI_LIST: return true;
// string[] uris = selection_data.get_uris(); }
// // For now we only process the first dragged file return false;
// 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() { private void open_file_picker() {
FileChooserNative chooser = new FileChooserNative(_("Select file"), view.get_root() as Gtk.Window, FileChooserAction.OPEN, _("Select"), _("Cancel")); FileChooserNative chooser = new FileChooserNative(_("Select file"), view.get_root() as Gtk.Window, FileChooserAction.OPEN, _("Select"), _("Cancel"));
chooser.response.connect(() => { chooser.response.connect((response) => {
open_send_file_overlay(File.new_for_path(chooser.get_file().get_path())); if (response == ResponseType.ACCEPT) {
open_send_file_overlay(File.new_for_path(chooser.get_file().get_path()));
}
}); });
chooser.show(); chooser.show();
} }
@ -272,24 +255,22 @@ public class ConversationViewController : Object {
stream_interactor.get_module(FileManager.IDENTITY).send_file.begin(file, conversation); stream_interactor.get_module(FileManager.IDENTITY).send_file.begin(file, conversation);
} }
// private bool forward_key_press_to_chat_input(EventKey event) { private bool forward_key_press_to_chat_input(EventControllerKey key_controller, uint keyval, uint keycode, Gdk.ModifierType state) {
// if (((Gtk.Window)view.get_toplevel()).get_focus() is TextView) { if (view.get_root().get_focus() is TextView) {
// return false; return false;
// } }
//
// // Don't forward / change focus on Control / Alt // Don't forward / change focus on Control / Alt
// if (event.keyval == Gdk.Key.Control_L || event.keyval == Gdk.Key.Control_R || if (keyval == Gdk.Key.Control_L || keyval == Gdk.Key.Control_R ||
// event.keyval == Gdk.Key.Alt_L || event.keyval == Gdk.Key.Alt_R) { keyval == Gdk.Key.Alt_L || keyval == Gdk.Key.Alt_R) {
// return false; return false;
// } }
// // Don't forward / change focus on Control + ... // Don't forward / change focus on Control + ...
// if ((event.state & ModifierType.CONTROL_MASK) > 0) { if ((state & ModifierType.CONTROL_MASK) > 0) {
// return false; return false;
// } }
// if (view.chat_input.chat_text_view.text_view.key_press_event(event)) {
// return true; return key_controller.forward(view.chat_input.chat_text_view.text_view);
// } }
// return false;
// }
} }
} }

View file

@ -28,14 +28,11 @@ public class FileSendOverlay {
info_label = (Label) builder.get_object("info_label"); info_label = (Label) builder.get_object("info_label");
close_button.clicked.connect(() => { close_button.clicked.connect(() => {
main_box.unparent(); do_close();
main_box.destroy();
}); });
send_button.clicked.connect(() => { send_button.clicked.connect(() => {
send_file(); send_file();
this.close(); do_close();
main_box.unparent();
main_box.destroy();
}); });
load_file_widget.begin(file, file_info); load_file_widget.begin(file, file_info);
@ -48,12 +45,14 @@ public class FileSendOverlay {
} }
}); });
// this.key_release_event.connect((event) => { var key_events = new EventControllerKey();
// if (event.keyval == Gdk.Key.Escape) { key_events.key_pressed.connect((keyval) => {
// this.destroy(); if (keyval == Gdk.Key.Escape) {
// } do_close();
// return false; }
// }); return false;
});
this.main_box.add_controller(key_events);
} }
private async void load_file_widget(File file, FileInfo file_info) { private async void load_file_widget(File file, FileInfo file_info) {
@ -72,7 +71,7 @@ public class FileSendOverlay {
Widget? widget = null; Widget? widget = null;
if (is_image) { if (is_image) {
FileImageWidget image_widget = new FileImageWidget() { visible=true }; FileImageWidget image_widget = new FileImageWidget();
try { try {
yield image_widget.load_from_file(file, file_name); yield image_widget.load_from_file(file, file_name);
widget = image_widget; widget = image_widget;
@ -80,7 +79,7 @@ public class FileSendOverlay {
} }
if (widget == null) { if (widget == null) {
FileDefaultWidget default_widget = new FileDefaultWidget() { visible=true }; FileDefaultWidget default_widget = new FileDefaultWidget();
default_widget.name_label.label = file_name; default_widget.name_label.label = file_name;
default_widget.update_file_info(mime_type, FileTransfer.State.COMPLETE, (long)file_info.get_size()); default_widget.update_file_info(mime_type, FileTransfer.State.COMPLETE, (long)file_info.get_size());
widget = default_widget; widget = default_widget;
@ -96,6 +95,12 @@ public class FileSendOverlay {
can_send = false; can_send = false;
} }
private void do_close() {
this.close();
main_box.unparent();
main_box.destroy();
}
public Widget get_widget() { public Widget get_widget() {
return main_box; return main_box;
} }

View file

@ -185,7 +185,7 @@ public class GlobalSearch {
Gee.List<MessageItem> before_message = stream_interactor.get_module(MessageStorage.IDENTITY).get_messages_before_message(item.conversation, item.message.time, item.message.id, 1); Gee.List<MessageItem> before_message = stream_interactor.get_module(MessageStorage.IDENTITY).get_messages_before_message(item.conversation, item.message.time, item.message.id, 1);
Gee.List<MessageItem> after_message = stream_interactor.get_module(MessageStorage.IDENTITY).get_messages_after_message(item.conversation, item.message.time, item.message.id, 1); Gee.List<MessageItem> after_message = stream_interactor.get_module(MessageStorage.IDENTITY).get_messages_after_message(item.conversation, item.message.time, item.message.id, 1);
Box context_box = new Box(Orientation.VERTICAL, 5) { visible=true }; Box context_box = new Box(Orientation.VERTICAL, 5);
if (before_message != null && before_message.size > 0) { if (before_message != null && before_message.size > 0) {
context_box.append(get_context_message_widget(before_message.first())); context_box.append(get_context_message_widget(before_message.first()));
} }
@ -197,16 +197,16 @@ public class GlobalSearch {
context_box.append(get_context_message_widget(after_message.first())); context_box.append(get_context_message_widget(after_message.first()));
} }
Label date_label = new Label(ConversationSummary.ConversationItemSkeleton.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 };
date_label.get_style_context().add_class("dim-label"); date_label.add_css_class("dim-label");
string display_name = Util.get_conversation_display_name(stream_interactor, item.conversation); 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); 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 }; Box header_box = new Box(Orientation.HORIZONTAL, 10) { margin_start=7 };
header_box.append(new Label(@"<b>$(Markup.escape_text(title))</b>") { ellipsize=EllipsizeMode.END, xalign=0, use_markup=true, visible=true }); header_box.append(new Label(@"<b>$(Markup.escape_text(title))</b>") { ellipsize=EllipsizeMode.END, xalign=0, use_markup=true });
header_box.append(date_label); header_box.append(date_label);
Box result_box = new Box(Orientation.VERTICAL, 7) { visible=true }; Box result_box = new Box(Orientation.VERTICAL, 7);
result_box.append(header_box); result_box.append(header_box);
result_box.append(context_box); result_box.append(context_box);
@ -232,7 +232,7 @@ public class GlobalSearch {
text = text.substring(0, 25) + "" + text.substring(index - 50, 50) + text.substring(index, 100) + "" + text.substring(text.length - 25, 25); text = text.substring(0, 25) + "" + text.substring(index - 50, 50) + text.substring(index, 100) + "" + text.substring(text.length - 25, 25);
} }
} }
Label label = new Label("") { use_markup=true, xalign=0, selectable=true, wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR, vexpand=true, visible=true }; Label label = new Label("") { use_markup=true, xalign=0, selectable=true, wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR, vexpand=true };
// Build regex containing all keywords // Build regex containing all keywords
string regex_str = "("; string regex_str = "(";
@ -269,7 +269,7 @@ public class GlobalSearch {
label.label = markup_text; label.label = markup_text;
grid.attach(label, 1, 1, 1, 1); grid.attach(label, 1, 1, 1, 1);
Button button = new Button() { has_frame=false, visible=true }; Button button = new Button() { has_frame=false };
button.clicked.connect(() => { button.clicked.connect(() => {
selected_item(item); selected_item(item);
}); });
@ -280,20 +280,20 @@ public class GlobalSearch {
private Grid get_context_message_widget(MessageItem item) { private Grid get_context_message_widget(MessageItem item) {
Grid grid = get_skeleton(item); Grid grid = get_skeleton(item);
grid.margin_start = 7; grid.margin_start = 7;
Label label = new Label(item.message.body.replace("\n", "").replace("\r", "")) { ellipsize=EllipsizeMode.MIDDLE, xalign=0, visible=true }; Label label = new Label(item.message.body.replace("\n", "").replace("\r", "")) { ellipsize=EllipsizeMode.MIDDLE, xalign=0 };
grid.attach(label, 1, 1, 1, 1); grid.attach(label, 1, 1, 1, 1);
grid.opacity = 0.55; grid.opacity = 0.55;
return grid; return grid;
} }
private Grid get_skeleton(MessageItem item) { private Grid get_skeleton(MessageItem item) {
AvatarImage image = new AvatarImage() { height=32, width=32, margin_end=7, valign=Align.START, visible=true, allow_gray = false }; AvatarImage image = new AvatarImage() { height=32, width=32, margin_end=7, valign=Align.START, allow_gray = false };
image.set_conversation_participant(stream_interactor, item.conversation, item.jid); image.set_conversation_participant(stream_interactor, item.conversation, item.jid);
Grid grid = new Grid() { row_homogeneous=false, visible=true }; Grid grid = new Grid() { row_homogeneous=false };
grid.attach(image, 0, 0, 1, 2); grid.attach(image, 0, 0, 1, 2);
string display_name = Util.get_participant_display_name(stream_interactor, item.conversation, item.jid); string display_name = Util.get_participant_display_name(stream_interactor, item.conversation, item.jid);
Label name_label = new Label(display_name) { ellipsize=EllipsizeMode.END, xalign=0, visible=true }; Label name_label = new Label(display_name) { ellipsize=EllipsizeMode.END, xalign=0 };
name_label.attributes = new AttrList(); name_label.attributes = new AttrList();
name_label.attributes.insert(attr_weight_new(Weight.BOLD)); name_label.attributes.insert(attr_weight_new(Weight.BOLD));
grid.attach(name_label, 1, 0, 1, 1); grid.attach(name_label, 1, 0, 1, 1);

View file

@ -49,7 +49,7 @@ public class MainWindow : Gtk.Window {
this.title = "Dino"; this.title = "Dino";
this.get_style_context().add_class("dino-main"); this.add_css_class("dino-main");
Gtk.Settings.get_default().notify["gtk-decoration-layout"].connect(set_window_buttons); Gtk.Settings.get_default().notify["gtk-decoration-layout"].connect(set_window_buttons);
((Widget)this).realize.connect(set_window_buttons); ((Widget)this).realize.connect(set_window_buttons);
@ -77,7 +77,7 @@ public class MainWindow : Gtk.Window {
search_frame.set_child(global_search.get_widget()); search_frame.set_child(global_search.get_widget());
Image conversation_list_placeholder_image = (Image) builder.get_object("conversation_list_placeholder_image"); 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")); conversation_list_placeholder_image.set_from_pixbuf(new Pixbuf.from_resource("/im/dino/Dino/icons/scalable/ui/dino-conversation-list-placeholder-arrow.svg"));
} }
private void setup_headerbar() { private void setup_headerbar() {

View file

@ -111,13 +111,6 @@ public class MainWindowController : Object {
stream_interactor.get_module(ConversationManager.IDENTITY).conversation_activated.connect(() => update_stack_state()); stream_interactor.get_module(ConversationManager.IDENTITY).conversation_activated.connect(() => update_stack_state());
stream_interactor.get_module(ConversationManager.IDENTITY).conversation_deactivated.connect(() => update_stack_state()); stream_interactor.get_module(ConversationManager.IDENTITY).conversation_deactivated.connect(() => update_stack_state());
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);
} }
public void select_conversation(Conversation? conversation, bool do_reset_search = true, bool default_initialize_conversation = true) { public void select_conversation(Conversation? conversation, bool do_reset_search = true, bool default_initialize_conversation = true) {

View file

@ -109,8 +109,8 @@ public class AddAccountDialog : Gtk.Dialog {
login_button.clicked.connect(show_sign_in_jid); login_button.clicked.connect(show_sign_in_jid);
foreach (string server in server_list) { foreach (string server in server_list) {
ListBoxRow list_box_row = new ListBoxRow() { visible=true }; ListBoxRow list_box_row = new ListBoxRow();
list_box_row.set_child(new Label(server) { xalign=0, margin_start=7, margin_end=7, visible=true }); list_box_row.set_child(new Label(server) { xalign=0, margin_start=7, margin_end=7 });
list_box_jids[list_box_row] = server; list_box_jids[list_box_row] = server;
server_list_box.append(list_box_row); server_list_box.append(list_box_row);
} }
@ -338,8 +338,8 @@ public class AddAccountDialog : Gtk.Dialog {
register_title.label = _("Register on %s").printf(server.to_string()); register_title.label = _("Register on %s").printf(server.to_string());
if (form.oob != null) { if (form.oob != null) {
form_box.append(new Label(_("The server requires to sign up through a website")){ visible=true } ); form_box.append(new Label(_("The server requires to sign up through a website")));
form_box.append(new Label(@"<a href=\"$(form.oob)\">$(form.oob)</a>") { use_markup=true, visible=true }); form_box.append(new Label(@"<a href=\"$(form.oob)\">$(form.oob)</a>") { use_markup=true });
register_form_continue_label.label = _("Open website"); register_form_continue_label.label = _("Open website");
register_form_continue.visible = true; register_form_continue.visible = true;
register_form_continue.grab_focus(); register_form_continue.grab_focus();
@ -347,23 +347,23 @@ public class AddAccountDialog : Gtk.Dialog {
if (form.instructions != null && form.instructions != "") { if (form.instructions != null && form.instructions != "") {
string markup_instructions = Util.parse_add_markup(form.instructions, null, true, false); string markup_instructions = Util.parse_add_markup(form.instructions, null, true, false);
form_box.append(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 }); wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR });
} }
foreach (Xep.DataForms.DataForm.Field field in form.fields) { foreach (Xep.DataForms.DataForm.Field field in form.fields) {
Widget? field_widget = Util.get_data_form_field_widget(field); Widget? field_widget = Util.get_data_form_field_widget(field);
if (field.label != null && field.label != "" && field_widget != null) { if (field.label != null && field.label != "" && field_widget != null) {
form_box.append(new Label(field.label) { xalign=0, margin_top=7, visible=true }); form_box.append(new Label(field.label) { xalign=0, margin_top=7 });
form_box.append(field_widget); form_box.append(field_widget);
} else if (field.type_ == Xep.DataForms.DataForm.Type.FIXED && field.get_value_string() != "") { } 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); string markup_fixed_field = Util.parse_add_markup(field.get_value_string(), null, true, false);
form_box.append(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 }); wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR });
} }
} }
register_form_continue.visible = true; register_form_continue.visible = true;
register_form_continue_label.label = _("Register"); register_form_continue_label.label = _("Register");
} else { } else {
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 }); form_box.append(new Label(_("Check %s for information on how to sign up").printf(@"<a href=\"http://$(server)\">$(server)</a>")) { use_markup=true });
register_form_continue.visible = false; register_form_continue.visible = false;
} }
} }
@ -423,21 +423,19 @@ public class AddAccountDialog : Gtk.Dialog {
} }
private void animate_window_resize(Widget widget) { // TODO code duplication private void animate_window_resize(Widget widget) { // TODO code duplication
// int def_height, curr_width, curr_height; int curr_height = widget.get_size(Orientation.VERTICAL);
// get_size(out curr_width, out curr_height); var natural_size = Requisition();
// widget.get_preferred_height(null, out def_height); stack.get_preferred_size(null, out natural_size);
// def_height += 5; int difference = natural_size.height + 5 - curr_height;
// int difference = def_height - curr_height; Timer timer = new Timer();
// Timer timer = new Timer(); Timeout.add((int) (stack.transition_duration / 30), () => {
// Timeout.add((int) (stack.transition_duration / 30), ulong microsec;
// () => { timer.elapsed(out microsec);
// ulong microsec; ulong millisec = microsec / 1000;
// timer.elapsed(out microsec); double partial = double.min(1, (double) millisec / stack.transition_duration);
// ulong millisec = microsec / 1000; default_height = (int) (curr_height + difference * partial);
// double partial = double.min(1, (double) millisec / stack.transition_duration); return millisec < stack.transition_duration;
// resize(curr_width, (int) (curr_height + difference * partial)); });
// return millisec < stack.transition_duration;
// });
} }
} }

View file

@ -58,8 +58,8 @@ public class Dialog : Gtk.Dialog {
Widget? widget = e.get_widget(Plugins.WidgetType.GTK4) as Widget; Widget? widget = e.get_widget(Plugins.WidgetType.GTK4) as Widget;
if (widget == null) continue; if (widget == null) continue;
Label label = new Label(e.name) { xalign=1, yalign=0, visible=true }; Label label = new Label(e.name) { xalign=1, yalign=0 };
label.get_style_context().add_class("dim-label"); label.add_css_class("dim-label");
label.margin_top = e.label_top_padding == -1 ? default_top_padding : e.label_top_padding; label.margin_top = e.label_top_padding == -1 ? default_top_padding : e.label_top_padding;
settings_list.attach(label, 0, row_index); settings_list.attach(label, 0, row_index);
@ -117,18 +117,21 @@ public class Dialog : Gtk.Dialog {
msg.secondary_text = "You won't be able to access your conversation history anymore."; // TODO remove history! msg.secondary_text = "You won't be able to access your conversation history anymore."; // TODO remove history!
Button ok_button = msg.get_widget_for_response(ResponseType.OK) as Button; Button ok_button = msg.get_widget_for_response(ResponseType.OK) as Button;
ok_button.label = _("Remove"); ok_button.label = _("Remove");
ok_button.get_style_context().add_class("destructive-action"); ok_button.add_css_class("destructive-action");
if (/*msg.run() == Gtk.ResponseType.OK*/ true) { msg.response.connect((response) => {
account_list.remove(account_item); if (response == ResponseType.OK) {
if (account_item.account.enabled) account_disabled(account_item.account); account_list.remove(account_item);
account_item.account.remove(); if (account_item.account.enabled) account_disabled(account_item.account);
if (account_list.get_row_at_index(0) != null) { account_item.account.remove();
account_list.select_row(account_list.get_row_at_index(0)); if (account_list.get_row_at_index(0) != null) {
} else { account_list.select_row(account_list.get_row_at_index(0));
main_stack.set_visible_child_name("no_accounts"); } else {
main_stack.set_visible_child_name("no_accounts");
}
} }
} msg.close();
msg.close(); });
msg.present();
} }
private void on_account_list_row_selected(ListBoxRow? row) { private void on_account_list_row_selected(ListBoxRow? row) {
@ -205,7 +208,7 @@ public class Dialog : Gtk.Dialog {
ConnectionManager.ConnectionError? error = stream_interactor.connection_manager.get_error(account); ConnectionManager.ConnectionError? error = stream_interactor.connection_manager.get_error(account);
if (error != null) { if (error != null) {
state_label.label = get_connection_error_description(error); state_label.label = get_connection_error_description(error);
state_label.get_style_context().add_class("is_error"); state_label.add_css_class("is_error");
} else { } else {
ConnectionManager.ConnectionState state = stream_interactor.connection_manager.get_state(account); ConnectionManager.ConnectionState state = stream_interactor.connection_manager.get_state(account);
switch (state) { switch (state) {
@ -216,7 +219,7 @@ public class Dialog : Gtk.Dialog {
case ConnectionManager.ConnectionState.DISCONNECTED: case ConnectionManager.ConnectionState.DISCONNECTED:
state_label.label = _("Disconnected"); break; state_label.label = _("Disconnected"); break;
} }
state_label.get_style_context().remove_class("is_error"); state_label.remove_css_class("is_error");
} }
} }

View file

@ -122,16 +122,16 @@ public class List : Box {
if (aff == affiliation) count++; if (aff == affiliation) count++;
} }
Label title_label = new Label("") { margin_start=10, xalign=0, visible=true }; Label title_label = new Label("") { margin_start=10, xalign=0 };
title_label.set_markup(@"<b>$(Markup.escape_text(aff_str))</b>"); title_label.set_markup(@"<b>$(Markup.escape_text(aff_str))</b>");
Label count_label = new Label(@"$count") { xalign=0, margin_end=7, hexpand=true, visible=true }; Label count_label = new Label(@"$count") { xalign=0, margin_end=7, hexpand=true };
count_label.get_style_context().add_class("dim-label"); count_label.add_css_class("dim-label");
Grid grid = new Grid() { margin_top=top?5:15, column_spacing=5, hexpand=true, visible=true }; Grid grid = new Grid() { margin_top=top?5:15, column_spacing=5, hexpand=true };
grid.attach(title_label, 0, 0, 1, 1); grid.attach(title_label, 0, 0, 1, 1);
grid.attach(count_label, 1, 0, 1, 1); grid.attach(count_label, 1, 0, 1, 1);
grid.attach(new Separator(Orientation.HORIZONTAL) { hexpand=true, vexpand=true, visible=true }, 0, 1, 2, 1); grid.attach(new Separator(Orientation.HORIZONTAL) { hexpand=true, vexpand=true }, 0, 1, 2, 1);
return grid; return grid;
} }

View file

@ -10,10 +10,10 @@ public class View : Popover {
private StreamInteractor stream_interactor; private StreamInteractor stream_interactor;
private Conversation conversation; private Conversation conversation;
private Stack stack = new Stack() { vhomogeneous=false, visible=true }; private Stack stack = new Stack() { vhomogeneous=false };
private Box list_box = new Box(Orientation.VERTICAL, 1) { visible=true }; private Box list_box = new Box(Orientation.VERTICAL, 1);
private List? list = null; private List? list = null;
private ListBox invite_list = new ListBox() { visible=true }; private ListBox invite_list = new ListBox();
private Box? jid_menu = null; private Box? jid_menu = null;
private Jid? selected_jid; private Jid? selected_jid;
@ -25,6 +25,7 @@ public class View : Popover {
this.show.connect(initialize_list); this.show.connect(initialize_list);
invite_list.append(new ListRow.label("+", _("Invite")).get_widget()); invite_list.append(new ListRow.label("+", _("Invite")).get_widget());
invite_list.can_focus = false;
list_box.append(invite_list); list_box.append(invite_list);
invite_list.row_activated.connect(on_invite_clicked); invite_list.row_activated.connect(on_invite_clicked);
@ -44,7 +45,7 @@ public class View : Popover {
private void initialize_list() { private void initialize_list() {
if (list == null) { if (list == null) {
list = new List(stream_interactor, conversation) { visible=true }; list = new List(stream_interactor, conversation);
list_box.prepend(list); list_box.prepend(list);
list.list_box.row_activated.connect((row) => { list.list_box.row_activated.connect((row) => {
@ -68,17 +69,17 @@ public class View : Popover {
Jid? real_jid = stream_interactor.get_module(MucManager.IDENTITY).get_real_jid(jid, conversation.account); Jid? real_jid = stream_interactor.get_module(MucManager.IDENTITY).get_real_jid(jid, conversation.account);
if (real_jid != null) name += "\n<span font=\'8\'>%s</span>".printf(Markup.escape_text(real_jid.bare_jid.to_string())); 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 }; Box header_box = new Box(Orientation.HORIZONTAL, 5);
header_box.append(new Image.from_icon_name("pan-start-symbolic") { visible=true }); header_box.append(new Image.from_icon_name("pan-start-symbolic"));
header_box.append(new Label(name) { xalign=0, use_markup=true, hexpand=true, visible=true }); header_box.append(new Label(name) { xalign=0, use_markup=true, hexpand=true });
Button header_button = new Button() { has_frame=false, visible=true }; Button header_button = new Button() { has_frame=false };
header_button.child = header_box; header_button.child = header_box;
Box outer_box = new Box(Orientation.VERTICAL, 5) { visible=true }; Box outer_box = new Box(Orientation.VERTICAL, 5);
outer_box.append(header_button); outer_box.append(header_button);
header_button.clicked.connect(show_list); header_button.clicked.connect(show_list);
Button private_button = new Button.with_label(_("Start private conversation")) { visible=true }; Button private_button = new Button.with_label(_("Start private conversation")) ;
outer_box.append(private_button); outer_box.append(private_button);
private_button.clicked.connect(private_conversation_button_clicked); private_button.clicked.connect(private_conversation_button_clicked);
@ -86,19 +87,19 @@ public class View : Popover {
Xmpp.Xep.Muc.Role? role = stream_interactor.get_module(MucManager.IDENTITY).get_role(own_jid, 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)) { if (role == Xmpp.Xep.Muc.Role.MODERATOR && stream_interactor.get_module(MucManager.IDENTITY).kick_possible(conversation.account, jid)) {
Button kick_button = new Button.with_label(_("Kick")) { visible=true }; Button kick_button = new Button.with_label(_("Kick")) ;
outer_box.append(kick_button); outer_box.append(kick_button);
kick_button.clicked.connect(kick_button_clicked); 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).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) { if (stream_interactor.get_module(MucManager.IDENTITY).get_role(selected_jid, conversation.account) == Xmpp.Xep.Muc.Role.VISITOR) {
Button voice_button = new Button.with_label(_("Grant write permission")) { visible=true }; Button voice_button = new Button.with_label(_("Grant write permission")) ;
outer_box.append(voice_button); outer_box.append(voice_button);
voice_button.clicked.connect(() => voice_button.clicked.connect(() =>
voice_button_clicked("participant")); voice_button_clicked("participant"));
} }
else if (stream_interactor.get_module(MucManager.IDENTITY).get_role(selected_jid, conversation.account) == Xmpp.Xep.Muc.Role.PARTICIPANT){ else if (stream_interactor.get_module(MucManager.IDENTITY).get_role(selected_jid, conversation.account) == Xmpp.Xep.Muc.Role.PARTICIPANT){
Button voice_button = new Button.with_label(_("Revoke write permission")) { visible=true }; Button voice_button = new Button.with_label(_("Revoke write permission")) ;
outer_box.append(voice_button); outer_box.append(voice_button);
voice_button.clicked.connect(() => voice_button.clicked.connect(() =>
voice_button_clicked("visitor")); voice_button_clicked("visitor"));

View file

@ -11,7 +11,7 @@ public static Widget? get_data_form_field_widget(DataForms.DataForm.Field field)
switch (field.type_) { switch (field.type_) {
case DataForms.DataForm.Type.BOOLEAN: case DataForms.DataForm.Type.BOOLEAN:
DataForms.DataForm.BooleanField boolean_field = field as DataForms.DataForm.BooleanField; DataForms.DataForm.BooleanField boolean_field = field as DataForms.DataForm.BooleanField;
Switch sw = new Switch() { active=boolean_field.value, halign=Align.START, valign=Align.CENTER, visible=true }; Switch sw = new Switch() { active=boolean_field.value, halign=Align.START, valign=Align.CENTER };
sw.state_set.connect((state) => { sw.state_set.connect((state) => {
boolean_field.value = state; boolean_field.value = state;
return false; return false;
@ -21,7 +21,7 @@ public static Widget? get_data_form_field_widget(DataForms.DataForm.Field field)
return null; return null;
case DataForms.DataForm.Type.LIST_SINGLE: case DataForms.DataForm.Type.LIST_SINGLE:
DataForms.DataForm.ListSingleField list_single_field = field as DataForms.DataForm.ListSingleField; DataForms.DataForm.ListSingleField list_single_field = field as DataForms.DataForm.ListSingleField;
ComboBoxText combobox = new ComboBoxText() { valign=Align.CENTER, visible=true }; ComboBoxText combobox = new ComboBoxText() { valign=Align.CENTER };
for (int i = 0; i < list_single_field.options.size; i++) { for (int i = 0; i < list_single_field.options.size; i++) {
DataForms.DataForm.Option option = list_single_field.options[i]; DataForms.DataForm.Option option = list_single_field.options[i];
combobox.append(option.value, option.label); combobox.append(option.value, option.label);
@ -35,7 +35,7 @@ public static Widget? get_data_form_field_widget(DataForms.DataForm.Field field)
return null; return null;
case DataForms.DataForm.Type.TEXT_PRIVATE: case DataForms.DataForm.Type.TEXT_PRIVATE:
DataForms.DataForm.TextPrivateField text_private_field = field as DataForms.DataForm.TextPrivateField; 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 entry = new Entry() { text=text_private_field.value ?? "", valign=Align.CENTER, visibility=false };
var entry_key_events = new EventControllerKey(); var entry_key_events = new EventControllerKey();
entry_key_events.key_released.connect(() => { entry_key_events.key_released.connect(() => {
text_private_field.value = entry.text; text_private_field.value = entry.text;
@ -44,7 +44,7 @@ public static Widget? get_data_form_field_widget(DataForms.DataForm.Field field)
return entry; return entry;
case DataForms.DataForm.Type.TEXT_SINGLE: case DataForms.DataForm.Type.TEXT_SINGLE:
DataForms.DataForm.TextSingleField text_single_field = field as DataForms.DataForm.TextSingleField; 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 entry = new Entry() { text=text_single_field.value ?? "", valign=Align.CENTER };
var entry_key_events = new EventControllerKey(); var entry_key_events = new EventControllerKey();
entry_key_events.key_released.connect(() => { entry_key_events.key_released.connect(() => {
text_single_field.value = entry.text; text_single_field.value = entry.text;

View file

@ -6,12 +6,12 @@ namespace Dino.Ui.Util {
public class LabelHybrid : Widget { public class LabelHybrid : Widget {
public Stack stack = new Stack(); public Stack stack = new Stack();
public Label label = new Label("") { visible=true, max_width_chars=1, ellipsize=Pango.EllipsizeMode.END }; public Label label = new Label("") { max_width_chars=1, ellipsize=Pango.EllipsizeMode.END };
protected Button button = new Button() { has_frame=false, visible=true }; protected Button button = new Button() { has_frame=false };
internal virtual void init(Widget widget) { internal virtual void init(Widget widget) {
this.layout_manager = new BinLayout(); this.layout_manager = new BinLayout();
stack.insert_after(this, null); stack.set_parent(this);
button.child = label; button.child = label;
stack.add_named(button, "label"); stack.add_named(button, "label");
stack.add_named(widget, "widget"); stack.add_named(widget, "widget");
@ -29,6 +29,10 @@ public class LabelHybrid : Widget {
public void show_label() { public void show_label() {
stack.visible_child_name = "label"; stack.visible_child_name = "label";
} }
public override void dispose() {
stack.unparent();
}
} }
public class EntryLabelHybrid : LabelHybrid { public class EntryLabelHybrid : LabelHybrid {
@ -58,7 +62,7 @@ public class EntryLabelHybrid : LabelHybrid {
public Entry entry { public Entry entry {
get { get {
if (entry_ == null) { if (entry_ == null) {
entry_ = new Entry() { visible=true }; entry_ = new Entry();
init(entry_); init(entry_);
} }
return entry_; return entry_;
@ -71,10 +75,11 @@ public class EntryLabelHybrid : LabelHybrid {
} }
internal override void init(Widget widget) { internal override void init(Widget widget) {
Entry? e = widget as Entry; if (e == null) return; Entry? e = widget as Entry;
if (e == null) return;
entry = e; entry = e;
base.init(entry); base.init(entry);
update_label(); set_label_label(entry.text);
var key_events = new EventControllerKey(); var key_events = new EventControllerKey();
key_events.key_released.connect(on_key_released); key_events.key_released.connect(on_key_released);
@ -106,10 +111,6 @@ public class EntryLabelHybrid : LabelHybrid {
label.label = filler; label.label = filler;
} }
} }
private void update_label() {
text = text;
}
} }
public class ComboBoxTextLabelHybrid : LabelHybrid { public class ComboBoxTextLabelHybrid : LabelHybrid {
@ -128,7 +129,7 @@ public class ComboBoxTextLabelHybrid : LabelHybrid {
public ComboBoxText combobox { public ComboBoxText combobox {
get { get {
if (combobox_ == null) { if (combobox_ == null) {
combobox_ = new ComboBoxText() { visible=true }; combobox_ = new ComboBoxText();
init(combobox_); init(combobox_);
} }
return combobox_; return combobox_;

View file

@ -177,20 +177,6 @@ class ScalingImage : Widget {
minimum_baseline = natural_baseline = -1; minimum_baseline = natural_baseline = -1;
} }
// 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() { public override SizeRequestMode get_request_mode() {
return SizeRequestMode.HEIGHT_FOR_WIDTH; return SizeRequestMode.HEIGHT_FOR_WIDTH;
} }

View file

@ -33,5 +33,10 @@ public class SizingBin : Widget {
natural = int.max(natural, minimum); natural = int.max(natural, minimum);
} }
} }
public override void dispose() {
var child = this.get_first_child();
if (child != null) child.unparent();
}
} }
} }

View file

@ -236,7 +236,6 @@
<property name="position">left</property> <property name="position">left</property>
<property name="child"> <property name="child">
<object class="GtkBox"> <object class="GtkBox">
<property name="visible">True</property>
<property name="margin-start">10</property> <property name="margin-start">10</property>
<property name="margin-end">10</property> <property name="margin-end">10</property>
<property name="margin-top">10</property> <property name="margin-top">10</property>

View file

@ -159,8 +159,8 @@ public class BadMessagesWidget : Box {
} else { } else {
warning_text += _("%s does not trust this device. That means, you might be missing messages.").printf(who); warning_text += _("%s does not trust this device. That means, you might be missing messages.").printf(who);
} }
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 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 };
label.get_style_context().add_class("dim-label"); label.add_css_class("dim-label");
this.append(label); this.append(label);
label.activate_link.connect(() => { label.activate_link.connect(() => {

View file

@ -109,7 +109,7 @@ public class ContactDetailsDialog : Gtk.Dialog {
qrcode_image.set_from_pixbuf(qr_pixbuf); qrcode_image.set_from_pixbuf(qr_pixbuf);
qrcode_image.margin_top = qrcode_image.margin_end = qrcode_image.margin_top = qrcode_image.margin_end =
qrcode_image.margin_bottom = qrcode_image.margin_start = QUIET_ZONE_MODULES*MODULE_SIZE_PX; qrcode_image.margin_bottom = qrcode_image.margin_start = QUIET_ZONE_MODULES*MODULE_SIZE_PX;
qrcode_popover.get_style_context().add_class("qrcode-container"); qrcode_popover.add_css_class("qrcode-container");
show_qrcode_button.popover = qrcode_popover; show_qrcode_button.popover = qrcode_popover;
} }
@ -260,12 +260,12 @@ public class ContactDetailsDialog : Gtk.Dialog {
Button accept_button = new Button() { visible = true, valign = Align.CENTER, hexpand = true }; Button accept_button = new Button() { visible = true, valign = Align.CENTER, hexpand = true };
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.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.add_css_class("suggested-action");
accept_button.tooltip_text = _("Accept key"); accept_button.tooltip_text = _("Accept key");
Button reject_button = new Button() { visible = true, valign = Align.CENTER, hexpand = true }; Button reject_button = new Button() { visible = true, valign = Align.CENTER, hexpand = true };
reject_button.set_icon_name("action-unavailable-symbolic"); reject_button.set_icon_name("action-unavailable-symbolic");
reject_button.get_style_context().add_class("destructive-action"); reject_button.add_css_class("destructive-action");
reject_button.tooltip_text = _("Reject key"); reject_button.tooltip_text = _("Reject key");
accept_button.clicked.connect(() => { accept_button.clicked.connect(() => {
@ -285,13 +285,13 @@ public class ContactDetailsDialog : Gtk.Dialog {
}); });
string res = fingerprint_markup(fingerprint_from_base64(device[plugin.db.identity_meta.identity_key_public_base64])); 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 }; Label fingerprint_label = new Label(res) { use_markup=true, justify=Justification.RIGHT, halign = Align.START, valign = Align.CENTER, hexpand = false };
box.append(fingerprint_label); box.append(fingerprint_label);
Box control_box = new Box(Gtk.Orientation.HORIZONTAL, 0) { visible = true, hexpand = true }; Box control_box = new Box(Gtk.Orientation.HORIZONTAL, 0) { visible = true, hexpand = true };
control_box.append(accept_button); control_box.append(accept_button);
control_box.append(reject_button); control_box.append(reject_button);
control_box.get_style_context().add_class("linked"); // .linked: Visually link the accept / reject buttons control_box.add_css_class("linked"); // .linked: Visually link the accept / reject buttons
box.append(control_box); box.append(control_box);
lbr.set_child(box); lbr.set_child(box);
@ -304,7 +304,7 @@ public class ContactDetailsDialog : Gtk.Dialog {
public class FingerprintRow : ListBoxRow { public class FingerprintRow : ListBoxRow {
private Image trust_image = new Image() { visible = true, halign = Align.END }; 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 fingerprint_label = new Label("") { use_markup=true, justify=Justification.RIGHT, halign = Align.START, valign = Align.CENTER, hexpand = false };
private Label trust_label = new Label(null) { visible = true, hexpand = true, xalign = 0 }; private Label trust_label = new Label(null) { visible = true, hexpand = true, xalign = 0 };
public Row row; public Row row;
@ -333,17 +333,17 @@ public class FingerprintRow : ListBoxRow {
case TrustLevel.TRUSTED: case TrustLevel.TRUSTED:
trust_image.icon_name = "emblem-ok-symbolic"; trust_image.icon_name = "emblem-ok-symbolic";
trust_label.set_markup("<span color='#1A63D9'>%s</span>".printf(_("Accepted"))); trust_label.set_markup("<span color='#1A63D9'>%s</span>".printf(_("Accepted")));
fingerprint_label.get_style_context().remove_class("dim-label"); fingerprint_label.remove_css_class("dim-label");
break; break;
case TrustLevel.UNTRUSTED: case TrustLevel.UNTRUSTED:
trust_image.icon_name = "action-unavailable-symbolic"; trust_image.icon_name = "action-unavailable-symbolic";
trust_label.set_markup("<span color='#D91900'>%s</span>".printf(_("Rejected"))); trust_label.set_markup("<span color='#D91900'>%s</span>".printf(_("Rejected")));
fingerprint_label.get_style_context().add_class("dim-label"); fingerprint_label.add_css_class("dim-label");
break; break;
case TrustLevel.VERIFIED: case TrustLevel.VERIFIED:
trust_image.icon_name = "security-high-symbolic"; trust_image.icon_name = "security-high-symbolic";
trust_label.set_markup("<span color='#1A63D9'>%s</span>".printf(_("Verified"))); trust_label.set_markup("<span color='#1A63D9'>%s</span>".printf(_("Verified")));
fingerprint_label.get_style_context().remove_class("dim-label"); fingerprint_label.remove_css_class("dim-label");
break; break;
} }

View file

@ -69,8 +69,8 @@ private class ConversationNotification : MetaConversationNotification {
this.jid = jid; this.jid = jid;
this.account = account; this.account = account;
Box box = new Box(Orientation.HORIZONTAL, 5) { visible=true }; Box box = new Box(Orientation.HORIZONTAL, 5);
Button manage_button = new Button.with_label(_("Manage")) { visible=true }; Button manage_button = new Button.with_label(_("Manage"));
manage_button.clicked.connect(() => { manage_button.clicked.connect(() => {
manage_button.activate(); manage_button.activate();
ContactDetailsDialog dialog = new ContactDetailsDialog(plugin, account, jid); ContactDetailsDialog dialog = new ContactDetailsDialog(plugin, account, jid);
@ -80,7 +80,7 @@ private class ConversationNotification : MetaConversationNotification {
}); });
dialog.present(); dialog.present();
}); });
box.append(new Label(_("This contact has new devices")) { margin_end=10, visible=true }); box.append(new Label(_("This contact has new devices")) { margin_end=10 });
box.append(manage_button); box.append(manage_button);
widget = box; widget = box;
} }

View file

@ -105,7 +105,7 @@ public class ManageKeyDialog : Gtk.Dialog {
Pango.AttrList desc_attrs = new Pango.AttrList(); Pango.AttrList desc_attrs = new Pango.AttrList();
desc_attrs.insert(Pango.attr_scale_new(0.8)); desc_attrs.insert(Pango.attr_scale_new(0.8));
lbl_desc.attributes = desc_attrs; lbl_desc.attributes = desc_attrs;
lbl_desc.get_style_context().add_class("dim-label"); lbl_desc.add_css_class("dim-label");
box.append(lbl_title); box.append(lbl_title);
box.append(lbl_desc); box.append(lbl_desc);

View file

@ -17,7 +17,7 @@ public class ContactDetailsProvider : Plugins.ContactDetailsProvider, Object {
if (conversation.type_ == Conversation.Type.CHAT && type == WidgetType.GTK4) { 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); string? key_id = stream_interactor.get_module(Manager.IDENTITY).get_key_id(conversation.account, conversation.counterpart);
if (key_id != null) { if (key_id != null) {
Label label = new Label("") { use_markup=true, justify=Justification.RIGHT, selectable=true, visible=true }; Label label = new Label("") { use_markup=true, justify=Justification.RIGHT, selectable=true };
Gee.List<GPG.Key>? keys = null; Gee.List<GPG.Key>? keys = null;
try { try {
keys = GPGHelper.get_keylist(key_id); keys = GPGHelper.get_keylist(key_id);

View file

@ -45,7 +45,7 @@ public class Dino.Plugins.Rtp.Paintable : Gdk.Paintable, Object {
image.get_intrinsic_height() != paintable.get_intrinsic_height() || image.get_intrinsic_height() != paintable.get_intrinsic_height() ||
image.get_intrinsic_aspect_ratio() != paintable.get_intrinsic_aspect_ratio(); image.get_intrinsic_aspect_ratio() != paintable.get_intrinsic_aspect_ratio();
this.image.dispose(); if (image != null) this.image.dispose();
this.image = paintable; this.image = paintable;
this.pixel_aspect_ratio = pixel_aspect_ratio; this.pixel_aspect_ratio = pixel_aspect_ratio;