/* -*- 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 <swilmet@gnome.org>
*
* 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);
}