/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*- */ /* gtksourcecompletioncontainer.c * This file is part of GtkSourceView * * Copyright (C) 2013, 2014, 2016 - Sébastien Wilmet * * GtkSourceView is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * GtkSourceView is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * Custom sizing of the scrolled window containing the GtkTreeView containing * the completion proposals. If the GtkTreeView is small enough, the scrolled * window returns the natural size of the GtkTreeView. If it exceeds a certain * size, the scrolled window returns a smaller size, with the height at a row * boundary of the GtkTreeView. * * The purpose is to have a compact completion window, with a certain size * limit. */ #include "gtksourcecompletioncontainer.h" #define UNREALIZED_WIDTH 350 #define MAX_HEIGHT 180 G_DEFINE_TYPE (GtkSourceCompletionContainer, _gtk_source_completion_container, GTK_TYPE_SCROLLED_WINDOW); static gint get_max_width (GtkSourceCompletionContainer *container) { if (gtk_widget_get_realized (GTK_WIDGET (container))) { GtkWidget *toplevel; GdkWindow *window; GdkScreen *screen; gint max_width; gint xorigin; toplevel = gtk_widget_get_toplevel (GTK_WIDGET (container)); window = gtk_widget_get_window (toplevel); screen = gdk_window_get_screen (window); gdk_window_get_origin (window, &xorigin, NULL); max_width = gdk_screen_get_width (screen) - xorigin; return MAX (max_width, UNREALIZED_WIDTH); } return UNREALIZED_WIDTH; } static void _gtk_source_completion_container_get_preferred_width (GtkWidget *widget, gint *min_width, gint *nat_width) { GtkSourceCompletionContainer *container = GTK_SOURCE_COMPLETION_CONTAINER (widget); GtkWidget *child; GtkRequisition nat_size; gint width; child = gtk_bin_get_child (GTK_BIN (container)); gtk_widget_get_preferred_size (child, NULL, &nat_size); width = MIN (nat_size.width, get_max_width (container)); if (GTK_WIDGET_CLASS (_gtk_source_completion_container_parent_class)->get_preferred_width != NULL) { gint min_width_parent = 0; GTK_WIDGET_CLASS (_gtk_source_completion_container_parent_class)->get_preferred_width (widget, &min_width_parent, NULL); width = MAX (width, min_width_parent); } if (min_width != NULL) { *min_width = width; } if (nat_width != NULL) { *nat_width = width; } g_return_if_fail (width >= 0); } static gint get_row_height (GtkSourceCompletionContainer *container, gint tree_view_height) { GtkWidget *tree_view; GtkTreeModel *model; gint nb_rows; /* For another possible implementation, see gtkentrycompletion.c in the * GTK+ source code (the _gtk_entry_completion_resize_popup() function). * It uses gtk_tree_view_column_cell_get_size() for retrieving the * height, plus gtk_widget_style_get() to retrieve the * "vertical-separator" height (note that the vertical separator must * probably be counted one less time than the number or rows). * But using that technique is buggy, it returns a smaller height (it's * maybe a bug in GtkTreeView, or there are other missing parameters). * * Note that the following implementation doesn't take into account * "vertical-separator". If there are some sizing bugs, it's maybe the * source of the problem. (note that on my system the separator size was * 0). */ tree_view = gtk_bin_get_child (GTK_BIN (container)); model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view)); if (model == NULL) { return 0; } nb_rows = gtk_tree_model_iter_n_children (model, NULL); if (nb_rows == 0) { return 0; } return tree_view_height / nb_rows; } /* Return a height at a row boundary of the GtkTreeView. */ static void _gtk_source_completion_container_get_preferred_height (GtkWidget *widget, gint *min_height, gint *nat_height) { GtkSourceCompletionContainer *container = GTK_SOURCE_COMPLETION_CONTAINER (widget); GtkWidget *child; GtkRequisition nat_size; gint height; child = gtk_bin_get_child (GTK_BIN (container)); gtk_widget_get_preferred_size (child, NULL, &nat_size); if (nat_size.height <= MAX_HEIGHT) { height = nat_size.height; } else { gint row_height = get_row_height (container, nat_size.height); gint n_rows_allowed = row_height != 0 ? MAX_HEIGHT / row_height : 0; height = n_rows_allowed * row_height; } if (GTK_WIDGET_CLASS (_gtk_source_completion_container_parent_class)->get_preferred_height != NULL) { gint min_height_parent = 0; GTK_WIDGET_CLASS (_gtk_source_completion_container_parent_class)->get_preferred_height (widget, &min_height_parent, NULL); height = MAX (height, min_height_parent); } if (min_height != NULL) { *min_height = height; } if (nat_height != NULL) { *nat_height = height; } g_return_if_fail (height >= 0); } static void _gtk_source_completion_container_class_init (GtkSourceCompletionContainerClass *klass) { GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); widget_class->get_preferred_width = _gtk_source_completion_container_get_preferred_width; widget_class->get_preferred_height = _gtk_source_completion_container_get_preferred_height; } static void _gtk_source_completion_container_init (GtkSourceCompletionContainer *container) { } GtkSourceCompletionContainer * _gtk_source_completion_container_new (void) { return g_object_new (GTK_SOURCE_TYPE_COMPLETION_CONTAINER, NULL); }