10 Commits

Author SHA1 Message Date
deepend-tildeclub
78219bf9e1 Merge pull request #176 from ZoiteChat/fix-font-consistency
Apply pref font to topic/tree/userlist/input
2026-03-30 11:07:59 -06:00
deepend-tildeclub
1c30cbfab1 Merge pull request #175 from ZoiteChat/fix-menu-hover-highlight
Fix fallback GTK menu highlight states
2026-03-30 11:07:43 -06:00
ac33dc5250 Apply pref font to topic/tree/userlist/input 2026-03-30 10:33:43 -06:00
8de5db18d6 Fix fallback GTK menu highlight states 2026-03-30 10:29:05 -06:00
deepend-tildeclub
913aa49df4 Merge pull request #169 from ZoiteChat/tab-close-button
UI: add close button to tabs
2026-03-30 09:44:01 -06:00
5e4633ee53 UI: Adjust channel tab height 2026-03-30 09:31:25 -06:00
deepend-tildeclub
30e573d87a Merge pull request #174 from ZoiteChat/mutli-line-topic-fix
Wrap topic bar in scroller, bound height
2026-03-30 09:20:37 -06:00
9e91d60bb1 Wrap topic bar in scroller, bound height 2026-03-29 17:48:56 -06:00
f75c27c843 Make tab close button smaller + flat 2026-03-28 22:20:21 -06:00
687b5fb40d UI: add close button to tabs 2026-03-28 11:33:03 -06:00
5 changed files with 204 additions and 25 deletions

View File

@@ -27,8 +27,6 @@ typedef struct
GtkWidget *b2; /* button2 */ GtkWidget *b2; /* button2 */
} tabview; } tabview;
#define ICON_CHANVIEW_CLOSE "gtk-close"
static void chanview_populate (chanview *cv); static void chanview_populate (chanview *cv);
/* ignore "toggled" signal? */ /* ignore "toggled" signal? */
@@ -334,12 +332,6 @@ tab_scroll_cb (GtkWidget *widget, GdkEventScroll *event, gpointer cv)
return FALSE; return FALSE;
} }
static void
cv_tabs_xclick_cb (GtkWidget *button, chanview *cv)
{
cv->cb_xbutton (cv, cv->focused, cv->focused->tag, cv->focused->userdata);
}
/* make a Scroll (arrow) button */ /* make a Scroll (arrow) button */
static GtkWidget * static GtkWidget *
@@ -384,7 +376,6 @@ cv_tabs_init (chanview *cv)
GtkWidget *box, *hbox = NULL; GtkWidget *box, *hbox = NULL;
GtkWidget *viewport; GtkWidget *viewport;
GtkWidget *outer; GtkWidget *outer;
GtkWidget *button;
if (cv->vertical) if (cv->vertical)
{ {
@@ -451,11 +442,6 @@ cv_tabs_init (chanview *cv)
gtk_box_pack_start (GTK_BOX (outer), ((tabview *)cv)->b1, 0, 0, 0); gtk_box_pack_start (GTK_BOX (outer), ((tabview *)cv)->b1, 0, 0, 0);
} }
button = gtkutil_button (outer, ICON_CHANVIEW_CLOSE, NULL, cv_tabs_xclick_cb,
cv, 0);
gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
gtk_widget_set_can_focus (button, FALSE);
gtk_container_add (GTK_CONTAINER (cv->box), outer); gtk_container_add (GTK_CONTAINER (cv->box), outer);
} }
@@ -659,20 +645,129 @@ tab_toggled_cb (GtkToggleButton *tab, chan *ch)
static gboolean static gboolean
tab_click_cb (GtkWidget *wid, GdkEventButton *event, chan *ch) tab_click_cb (GtkWidget *wid, GdkEventButton *event, chan *ch)
{ {
GtkWidget *close_button;
gint close_x;
gint close_y;
GtkAllocation close_alloc;
if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
{
close_button = g_object_get_data (G_OBJECT (wid), "tab-close-button");
if (close_button &&
gtk_widget_translate_coordinates (close_button, wid, 0, 0, &close_x, &close_y))
{
gtk_widget_get_allocation (close_button, &close_alloc);
if (event->x >= close_x && event->x < close_x + close_alloc.width &&
event->y >= close_y && event->y < close_y + close_alloc.height)
{
ch->cv->cb_xbutton (ch->cv, ch, ch->tag, ch->userdata);
return TRUE;
}
}
}
return ch->cv->cb_contextmenu (ch->cv, ch, ch->tag, ch->userdata, event); return ch->cv->cb_contextmenu (ch->cv, ch, ch->tag, ch->userdata, event);
} }
static gboolean
tab_close_motion_cb (GtkWidget *wid, GdkEventMotion *event, chan *ch)
{
GtkWidget *close_button;
gint close_x;
gint close_y;
GtkAllocation close_alloc;
gboolean hover = FALSE;
close_button = g_object_get_data (G_OBJECT (wid), "tab-close-button");
if (close_button &&
gtk_widget_translate_coordinates (close_button, wid, 0, 0, &close_x, &close_y))
{
gtk_widget_get_allocation (close_button, &close_alloc);
hover = event->x >= close_x && event->x < close_x + close_alloc.width &&
event->y >= close_y && event->y < close_y + close_alloc.height;
}
if (hover)
{
GdkCursor *cursor;
gtk_widget_set_state_flags (close_button, GTK_STATE_FLAG_PRELIGHT, TRUE);
if (gtk_widget_get_window (wid))
{
cursor = gdk_cursor_new_for_display (gtk_widget_get_display (wid), GDK_HAND2);
gdk_window_set_cursor (gtk_widget_get_window (wid), cursor);
g_object_unref (cursor);
}
}
else
{
gtk_widget_unset_state_flags (close_button, GTK_STATE_FLAG_PRELIGHT);
if (gtk_widget_get_window (wid))
gdk_window_set_cursor (gtk_widget_get_window (wid), NULL);
}
return FALSE;
}
static gboolean
tab_close_leave_cb (GtkWidget *wid, GdkEventCrossing *event, chan *ch)
{
GtkWidget *close_button;
close_button = g_object_get_data (G_OBJECT (wid), "tab-close-button");
if (close_button)
gtk_widget_unset_state_flags (close_button, GTK_STATE_FLAG_PRELIGHT);
if (gtk_widget_get_window (wid))
gdk_window_set_cursor (gtk_widget_get_window (wid), NULL);
return FALSE;
}
static GtkWidget *
tab_get_label (GtkWidget *tab)
{
GtkWidget *label;
label = g_object_get_data (G_OBJECT (tab), "tab-label");
if (label)
return label;
return gtk_bin_get_child (GTK_BIN (tab));
}
static void * static void *
cv_tabs_add (chanview *cv, chan *ch, char *name, GtkTreeIter *parent) cv_tabs_add (chanview *cv, chan *ch, char *name, GtkTreeIter *parent)
{ {
GtkWidget *but; GtkWidget *but;
GtkWidget *hbox;
GtkWidget *label;
GtkWidget *close_button;
GtkWidget *close_icon;
but = gtk_toggle_button_new_with_label (name); but = gtk_toggle_button_new ();
gtk_widget_set_name (but, "zoitechat-tab"); gtk_widget_set_name (but, "zoitechat-tab");
gtk_widget_set_size_request (but, -1, 18);
gtk_widget_add_events (but, GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
label = gtk_label_new (name);
close_button = gtk_button_new ();
gtk_style_context_add_class (gtk_widget_get_style_context (close_button), "flat");
close_icon = gtk_image_new_from_icon_name ("window-close-symbolic", GTK_ICON_SIZE_MENU);
gtk_image_set_pixel_size (GTK_IMAGE (close_icon), 8);
gtk_button_set_always_show_image (GTK_BUTTON (close_button), TRUE);
gtk_widget_set_can_focus (close_button, FALSE);
gtk_container_add (GTK_CONTAINER (close_button), close_icon);
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
gtk_box_pack_start (GTK_BOX (hbox), close_button, FALSE, FALSE, 0);
gtk_container_add (GTK_CONTAINER (but), hbox);
g_object_set_data (G_OBJECT (but), "tab-label", label);
g_object_set_data (G_OBJECT (but), "tab-close-button", close_button);
g_object_set_data (G_OBJECT (but), "c", ch); g_object_set_data (G_OBJECT (but), "c", ch);
/* used to trap right-clicks */ /* used to trap right-clicks */
g_signal_connect (G_OBJECT (but), "button-press-event", g_signal_connect (G_OBJECT (but), "button-press-event",
G_CALLBACK (tab_click_cb), ch); G_CALLBACK (tab_click_cb), ch);
g_signal_connect (G_OBJECT (but), "motion-notify-event",
G_CALLBACK (tab_close_motion_cb), ch);
g_signal_connect (G_OBJECT (but), "leave-notify-event",
G_CALLBACK (tab_close_leave_cb), ch);
/* avoid prelights */ /* avoid prelights */
g_signal_connect (G_OBJECT (but), "enter-notify-event", g_signal_connect (G_OBJECT (but), "enter-notify-event",
G_CALLBACK (tab_ignore_cb), NULL); G_CALLBACK (tab_ignore_cb), NULL);
@@ -684,6 +779,7 @@ cv_tabs_add (chanview *cv, chan *ch, char *name, GtkTreeIter *parent)
g_signal_connect (G_OBJECT (but), "toggled", g_signal_connect (G_OBJECT (but), "toggled",
G_CALLBACK (tab_toggled_cb), ch); G_CALLBACK (tab_toggled_cb), ch);
g_object_set_data (G_OBJECT (but), "u", ch->userdata); g_object_set_data (G_OBJECT (but), "u", ch->userdata);
gtk_widget_show_all (hbox);
tab_add_real (cv, but, ch); tab_add_real (cv, but, ch);
@@ -892,7 +988,7 @@ cv_tabs_cleanup (chanview *cv)
static void static void
cv_tabs_set_color (chan *ch, PangoAttrList *list) cv_tabs_set_color (chan *ch, PangoAttrList *list)
{ {
gtk_label_set_attributes (GTK_LABEL (gtk_bin_get_child (GTK_BIN (ch->impl))), list); gtk_label_set_attributes (GTK_LABEL (tab_get_label (ch->impl)), list);
} }
static void static void
@@ -901,16 +997,16 @@ cv_tabs_rename (chan *ch, char *name)
PangoAttrList *attr; PangoAttrList *attr;
GtkWidget *tab = ch->impl; GtkWidget *tab = ch->impl;
attr = gtk_label_get_attributes (GTK_LABEL (gtk_bin_get_child (GTK_BIN (tab)))); attr = gtk_label_get_attributes (GTK_LABEL (tab_get_label (tab)));
if (attr) if (attr)
pango_attr_list_ref (attr); pango_attr_list_ref (attr);
gtk_button_set_label (GTK_BUTTON (tab), name); gtk_label_set_text (GTK_LABEL (tab_get_label (tab)), name);
gtk_widget_queue_resize (gtk_widget_get_parent(gtk_widget_get_parent(gtk_widget_get_parent(tab)))); gtk_widget_queue_resize (gtk_widget_get_parent(gtk_widget_get_parent(gtk_widget_get_parent(tab))));
if (attr) if (attr)
{ {
gtk_label_set_attributes (GTK_LABEL (gtk_bin_get_child (GTK_BIN (tab))), attr); gtk_label_set_attributes (GTK_LABEL (tab_get_label (tab)), attr);
pango_attr_list_unref (attr); pango_attr_list_unref (attr);
} }
} }

View File

@@ -2968,10 +2968,73 @@ mg_create_dialogbuttons (GtkWidget *box)
} }
} }
static void
mg_configure_topic_scroller (GtkWidget *scroller, GtkWidget *topic)
{
PangoContext *context;
PangoFontMetrics *metrics;
int line_height;
int min_height;
int max_height;
context = gtk_widget_get_pango_context (topic);
metrics = pango_context_get_metrics (context,
pango_context_get_font_description (context),
pango_context_get_language (context));
line_height = PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) +
pango_font_metrics_get_descent (metrics));
pango_font_metrics_unref (metrics);
if (line_height <= 0)
line_height = 16;
min_height = line_height + 8;
max_height = line_height * 4 + 8;
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroller),
GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroller), GTK_SHADOW_NONE);
gtk_scrolled_window_set_propagate_natural_height (GTK_SCROLLED_WINDOW (scroller), TRUE);
gtk_scrolled_window_set_min_content_height (GTK_SCROLLED_WINDOW (scroller), min_height);
gtk_scrolled_window_set_max_content_height (GTK_SCROLLED_WINDOW (scroller), max_height);
}
void
mg_apply_session_font_prefs (session_gui *gui)
{
const PangoFontDescription *font = NULL;
GtkWidget *topic_scroller;
if (!gui)
return;
if (input_style)
font = input_style->font_desc;
if (gui->topic_entry)
{
theme_manager_apply_entry_palette (gui->topic_entry, font);
topic_scroller = gtk_widget_get_parent (gui->topic_entry);
if (topic_scroller && GTK_IS_SCROLLED_WINDOW (topic_scroller))
mg_configure_topic_scroller (topic_scroller, gui->topic_entry);
}
if (gui->input_box && prefs.hex_gui_input_style)
theme_manager_apply_entry_palette (gui->input_box, font);
if (gui->chanview)
chanview_apply_theme (gui->chanview);
if (gui->user_tree)
theme_manager_apply_userlist_style (gui->user_tree,
theme_manager_get_userlist_palette_behavior (font));
}
static void static void
mg_create_topicbar (session *sess, GtkWidget *box) mg_create_topicbar (session *sess, GtkWidget *box)
{ {
GtkWidget *vbox, *hbox, *mode_hbox, *topic, *bbox; GtkWidget *vbox, *hbox, *mode_hbox, *topic, *bbox, *topic_scroller;
session_gui *gui = sess->gui; session_gui *gui = sess->gui;
gui->topic_bar = vbox = mg_box_new (GTK_ORIENTATION_VERTICAL, FALSE, 0); gui->topic_bar = vbox = mg_box_new (GTK_ORIENTATION_VERTICAL, FALSE, 0);
@@ -2984,12 +3047,15 @@ mg_create_topicbar (session *sess, GtkWidget *box)
sess->res->tab = NULL; sess->res->tab = NULL;
gui->topic_entry = topic = gtk_text_view_new (); gui->topic_entry = topic = gtk_text_view_new ();
gtk_widget_set_name (topic, "zoitechat-inputbox"); gtk_widget_set_name (topic, "zoitechat-topicbox");
gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (topic), GTK_WRAP_WORD_CHAR); gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (topic), GTK_WRAP_WORD);
gtk_text_view_set_left_margin (GTK_TEXT_VIEW (topic), 4); gtk_text_view_set_left_margin (GTK_TEXT_VIEW (topic), 4);
gtk_text_view_set_right_margin (GTK_TEXT_VIEW (topic), 4); gtk_text_view_set_right_margin (GTK_TEXT_VIEW (topic), 4);
gtk_box_pack_start (GTK_BOX (hbox), topic, TRUE, TRUE, 0); topic_scroller = gtk_scrolled_window_new (NULL, NULL);
mg_apply_emoji_fallback_widget (topic); gtk_container_add (GTK_CONTAINER (topic_scroller), topic);
theme_manager_apply_entry_palette (topic, input_style ? input_style->font_desc : NULL);
mg_configure_topic_scroller (topic_scroller, topic);
gtk_box_pack_start (GTK_BOX (hbox), topic_scroller, TRUE, TRUE, 0);
gtk_widget_add_events (topic, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | gtk_widget_add_events (topic, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK); GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
g_signal_connect (G_OBJECT (topic), "key-press-event", g_signal_connect (G_OBJECT (topic), "key-press-event",

View File

@@ -37,6 +37,7 @@ GtkWidget *mg_create_generic_tab (char *name, char *title, int force_toplevel, i
void mg_set_title (GtkWidget *button, char *title); void mg_set_title (GtkWidget *button, char *title);
void mg_set_access_icon (session_gui *gui, GdkPixbuf *pix, gboolean away); void mg_set_access_icon (session_gui *gui, GdkPixbuf *pix, gboolean away);
void mg_apply_setup (void); void mg_apply_setup (void);
void mg_apply_session_font_prefs (session_gui *gui);
void mg_close_sess (session *); void mg_close_sess (session *);
void mg_tab_close (session *sess); void mg_tab_close (session *sess);
void mg_detach (session *sess, int mode); void mg_detach (session *sess, int mode);

View File

@@ -2066,7 +2066,7 @@ setup_apply_to_sess (session_gui *gui)
{ {
mg_update_xtext (gui->xtext); mg_update_xtext (gui->xtext);
theme_preferences_apply_to_session (gui, input_style); mg_apply_session_font_prefs (gui);
if (prefs.hex_gui_ulist_buttons) if (prefs.hex_gui_ulist_buttons)
gtk_widget_show (gui->button_box); gtk_widget_show (gui->button_box);

View File

@@ -367,6 +367,12 @@ theme_css_build_toplevel_classes (void)
"color: @theme_fg_color;" "color: @theme_fg_color;"
"border-color: @theme_bg_color;" "border-color: @theme_bg_color;"
"}" "}"
"window.%s menuitem:hover, window.%s menuitem:selected {"
"background-color: @theme_selected_bg_color;"
"background-image: none;"
"color: @theme_selected_fg_color;"
"border-color: @theme_selected_bg_color;"
"}"
"window.%s, window.%s:backdrop, .%s {" "window.%s, window.%s:backdrop, .%s {"
"background-color: #f6f6f6;" "background-color: #f6f6f6;"
"color: #101010;" "color: #101010;"
@@ -377,6 +383,12 @@ theme_css_build_toplevel_classes (void)
"background-image: none;" "background-image: none;"
"color: @theme_fg_color;" "color: @theme_fg_color;"
"border-color: @theme_bg_color;" "border-color: @theme_bg_color;"
"}"
"window.%s menuitem:hover, window.%s menuitem:selected {"
"background-color: @theme_selected_bg_color;"
"background-image: none;"
"color: @theme_selected_fg_color;"
"border-color: @theme_selected_bg_color;"
"}", "}",
theme_css_selector_dark_class, theme_css_selector_dark_class,
theme_css_selector_dark_class, theme_css_selector_dark_class,
@@ -387,6 +399,10 @@ theme_css_build_toplevel_classes (void)
theme_css_selector_dark_class, theme_css_selector_dark_class,
theme_css_selector_dark_class, theme_css_selector_dark_class,
theme_css_selector_dark_class, theme_css_selector_dark_class,
theme_css_selector_dark_class,
theme_css_selector_dark_class,
theme_css_selector_light_class,
theme_css_selector_light_class,
theme_css_selector_light_class, theme_css_selector_light_class,
theme_css_selector_light_class, theme_css_selector_light_class,
theme_css_selector_light_class, theme_css_selector_light_class,