Skip to content
Commits on Source (79)
......@@ -24,7 +24,7 @@ reference:
stage: docs
needs: []
variables:
MESON_FLAGS: "-Dwerror=true --buildtype=release -Dgtk:media-gstreamer=disabled -Dgtk:broadway-backend=false -Dgtk:demos=false -Dgtk:build-examples=false -Dgtk:build-tests=false -Dgtk:werror=false -Dlibsass:werror=false -Dsassc:werror=false -Dlibadwaita:werror=false"
MESON_FLAGS: "-Dc_std=c11 -Dcpp_std=c++11 -Dwerror=true --buildtype=release -Dglib:tests=false -Dgtk:media-gstreamer=disabled -Dgtk:broadway-backend=false -Dgtk:demos=false -Dgtk:build-examples=false -Dgtk:build-tests=false -Dgtk:werror=false -Dlibsass:werror=false -Dsassc:werror=false -Dlibadwaita:werror=false"
script:
- sudo dnf -y install git libpng-devel libjpeg-turbo-devel expat-devel
- mkdir -p pfx/
......
News in 5.6.0, 2022-09-17
-------------------------
This release corresponds to GNOME 43.0 and includes many bug fixes
across the library. It is recommended that distributions update to
this release.
* Updated languages: awk.lang, lean.lang
* Updated style schemes: Adwaita.xml, Adwaita-dark.xml,
solarized-light.xml, solarized-dark.xml, cobalt.xml
* Translation updates
* A bug with input grabs has been fixed when using GtkSourceHover
to create interactive tooltips in a stack or notebook.
* highlight-current-line is only rendered when the toplevel is active.
* Merging of snippet bundles handles tooltips now.
* The snippet manager now delays snippet parsing until needed.
* Completion of snippets is now lazier resulting in less memory and
CPU usage.
* Informative tooltips for snippets are now more aggressively dismissed.
* Scrolling now dismisses interactive hover tooltips.
* Use of Escape now dismisses the completion list and forwards the
event to the GtkSourceView, improving integration of Vim emulation.
* Snippet filtering correctness has been improved when adding/removing
characters from the filter text.
* Assistants such as the completion list defer repositioning to the
next frame tick which improves some situations with widgetry not
having an allocation when snapshotted.
* A new GtkSourceCompletion::provider-model-changed signal has been
added which allows observation of providers by external tooling.
This can be used to implement features like "top matches" from various
completion providers.
* Testsuite improvements
* GtkSourceView now uses GSignalGroup and GBindingGroup from GLib, and
therefore bumps our GLib dependency as it would be necessary with
updated GTK releases anyway.
* GtkSourceHoverDisplay now specifies a CSS element name for use by
applications in styling.
* GtkSourceCompletion now uses PANGO_UNDERLINE_SINGLE_LINE and
PANGO_WEIGHT_BOLD Pango attributes for highlighting fuzzy matches.
This improves visibility when used with certain character sets.
* Splicing of Pango attributes has been improved for completion which
ensures the merging does not result in missing attributes.
* Some methods have been marked as virtual for Vala.
News in 5.5.1, 2022-08-05
-------------------------
......
......@@ -32,6 +32,7 @@
<style id="pattern" name="Pattern" map-to="def:preprocessor"/>
<style id="variable" name="Variable" map-to="def:identifier"/>
<style id="builtin-function" name="Builtin Function" map-to="def:builtin"/>
<style id="regexpr" name="Regular Expression" map-to="def:string"/>
</styles>
<definitions>
......@@ -118,6 +119,10 @@
<match>\$\d+</match>
</context>
<context id="regexpr" style-ref="regexpr">
<match>/.*/</match>
</context>
<context id="awk">
<include>
<context ref="def:shebang"/>
......@@ -129,6 +134,7 @@
<context ref="field-variable"/>
<context ref="built-in-functions"/>
<context ref="arithmetic-functions"/>
<context ref="regexpr"/>
</include>
</context>
......
......@@ -31,14 +31,15 @@
<styles>
<style id="comment" name="Comment" map-to="def:comment"/>
<style id="attribute" name="Attribute" map-to="def:preprocessor"/>
<style id="command" name="Command" map-to="def:preprocessor"/>
<style id="keyword" name="Keyword" map-to="def:keyword"/>
<style id="identifier" name="Identifier"/>
<style id="string" name="String" map-to="def:string"/>
<style id="character" name="Character" map-to="def:character"/>
<style id="escaped-character" name="Escaped Character" map-to="def:special-char"/>
<style id="numeric" name="Numeric" map-to="def:number"/>
<style id="boolean" name="Boolean" map-to="def:boolean"/>
<style id="identifier" name="Identifier"/>
</styles>
<definitions>
......@@ -58,27 +59,38 @@
</include>
</context>
<context id="attribute" style-ref="attribute">
<start>@\[</start>
<end>\]</end>
<include>
<context ref="lean"/>
</include>
</context>
<context id="command" style-ref="command">
<match extended="true">
^\s*\#(
\#(?:
check(_failure)?
| eval
| print
| reduce
)
)\b
</match>
</context>
<context id="keyword" style-ref="keyword">
<keyword>abbrev</keyword>
<keyword>axiom</keyword>
<keyword>break</keyword>
<keyword>class</keyword>
<keyword>continue</keyword>
<keyword>def</keyword>
<keyword>deriving</keyword>
<keyword>do</keyword>
<keyword>else</keyword>
<keyword>end</keyword>
<keyword>example</keyword>
<keyword>extends</keyword>
<keyword>for</keyword>
<keyword>fun</keyword>
<keyword>if</keyword>
......@@ -89,8 +101,10 @@
<keyword>match</keyword>
<keyword>mut</keyword>
<keyword>namespace</keyword>
<keyword>opaque</keyword>
<keyword>open</keyword>
<keyword>partial</keyword>
<keyword>rec</keyword>
<keyword>return</keyword>
<keyword>section</keyword>
<keyword>structure</keyword>
......@@ -103,32 +117,24 @@
<keyword>λ</keyword>
</context>
<context id="identifier" style-ref="identifier">
<context id="escaped-character" style-ref="escaped-character">
<match extended="true">
(?![λΠΣ])[a-zA-Zα-ωΑ-Ωἀ-῾ϊ-ϻ℀-⅏_]
(?:(?![λΠΣ])[a-zA-Zα-ωΑ-Ωἀ-῾ϊ-ϻ℀-⅏_0-9'ⁿ₀-₉ₐ-ₜᵢ-ᵪ])*
| «[^«»\r\n\t]*»
\\(?:
\\
| \"
| \'
| n
| t
| x[0-9a-fA-F]{2}
)
</match>
</context>
<define-regex id="string-escape" extended="true">
\\(
\\
| \"
| \'
| n
| t
| x[0-9a-fA-F]{2}
)
</define-regex>
<context id="string" style-ref="string" end-at-line-end="true" class="string" class-disabled="no-spell-check">
<start>"</start>
<end>"</end>
<include>
<context style-ref="escaped-character">
<match>\%{string-escape}</match>
</context>
<context ref="escaped-character"/>
</include>
</context>
......@@ -136,15 +142,13 @@
<start>'</start>
<end>'</end>
<include>
<context style-ref="escaped-character">
<match>\%{string-escape}</match>
</context>
<context ref="escaped-character"/>
</include>
</context>
<context id="numeric" style-ref="numeric">
<match extended="true">
0[bB][0-1]+
0[bB][01]+
| 0[oO][0-7]+
| 0[xX][0-9a-fA-F]+
| [0-9]+
......@@ -156,17 +160,26 @@
<keyword>false</keyword>
</context>
<context id="identifier" style-ref="identifier">
<match extended="true">
(?![λΠΣ])[a-zA-Zα-ωΑ-Ωἀ-῾ϊ-ϻ℀-⅏_]
(?:(?![λΠΣ])[a-zA-Zα-ωΑ-Ωἀ-῾ϊ-ϻ℀-⅏_0-9'ⁿ₀-₉ₐ-ₜᵢ-ᵪ])*
| «[^«»\r\n\t]*»
</match>
</context>
<context id="lean" class="no-spell-check">
<include>
<context ref="line-comment"/>
<context ref="block-comment"/>
<context ref="attribute"/>
<context ref="command"/>
<context ref="keyword"/>
<context ref="identifier"/>
<context ref="string"/>
<context ref="character"/>
<context ref="numeric"/>
<context ref="boolean"/>
<context ref="identifier"/>
</include>
</context>
......
......@@ -118,6 +118,7 @@
<style name="def:floating-point" foreground="violet_2"/>
<style name="def:function" foreground="blue_2"/>
<style name="def:heading" foreground="teal_3" bold="true"/>
<style name="def:identifier" foreground="chameleon_3"/>
<style name="def:inline-code" foreground="violet_2"/>
<style name="def:keyword" foreground="orange_2" bold="true"/>
<style name="def:link-destination" foreground="blue_2" italic="true" underline="low"/>
......@@ -184,4 +185,4 @@
<style name="xml:namespace" foreground="yellow_4"/>
<style name="xml:processing-instruction" foreground="yellow_4" bold="true"/>
</style-scheme>
\ No newline at end of file
</style-scheme>
......@@ -115,6 +115,7 @@
<style name="def:floating-point" foreground="violet_4"/>
<style name="def:function" foreground="blue_4"/>
<style name="def:heading" foreground="teal_5" bold="true"/>
<style name="def:identifier" foreground="chameleon_3"/>
<style name="def:inline-code" foreground="violet_4"/>
<style name="def:keyword" foreground="orange_5" bold="true"/>
<style name="def:link-destination" foreground="blue_3" italic="true" underline="low"/>
......@@ -181,4 +182,4 @@
<style name="xml:namespace" foreground="yellow_6"/>
<style name="xml:processing-instruction" foreground="yellow_6" bold="true"/>
</style-scheme>
\ No newline at end of file
</style-scheme>
......@@ -118,9 +118,9 @@
<style name="def:list-marker" foreground="bright_orange"/>
<!-- Others -->
<style name="def:error" foreground="white" background="dark_red" bold="true"/>
<style name="def:warning" foreground="white" background="nail_polish_pink"/>
<style name="def:note" foreground="neon_yellow" bold="true"/>
<style name="def:error" underline="error" underline-color="dark_red"/>
<style name="def:warning" underline="error" underline-color="neon_yellow"/>
<style name="def:note" underline="error" underline-color="steel_grey"/>
<style name="def:net-address" foreground="teal_blue" italic="false" underline="single"/>
<style name="def:preprocessor" foreground="light_grey"/>
<style name="def:underlined" underline="single"/>
......
......@@ -70,6 +70,10 @@
<!-- Search Matching -->
<style name="search-match" foreground="base03" background="yellow"/>
<!-- Builder Overrides -->
<style name="-Builder:breakpoint" line-background="base1" foreground="base01"/>
<style name="-Builder:current-breakpoint" line-background="yellow" foreground="base01"/>
<!-- Comments -->
<style name="def:comment" foreground="base01"/>
<style name="def:shebang" foreground="base01" bold="true"/>
......@@ -120,7 +124,7 @@
<!-- Others -->
<style name="def:preprocessor" foreground="violet"/>
<style name="def:error" foreground="red" bold="true"/>
<style name="def:error" underline="error" underline-color="red"/>
<style name="def:note" foreground="magenta" bold="true"/>
<style name="def:net-address" italic="true" underline="single"/>
......
......@@ -121,7 +121,7 @@
<!-- Others -->
<style name="def:preprocessor" foreground="violet"/>
<style name="def:error" foreground="red" bold="true"/>
<style name="def:error" underline="error" underline-color="red"/>
<style name="def:note" foreground="magenta" bold="true"/>
<style name="def:net-address" italic="true" underline="single"/>
......
CompletionProposal
.get_typed_text#virtual_method virtual
CompletionProvider
.get_title#virtual_method virtual
.get_priority#virtual_method virtual
.is_trigger#virtual_method virtual
.key_activates#virtual_method virtual
.list_alternates#virtual_method virtual
\ No newline at end of file
......@@ -32,6 +32,14 @@ GtkSourceAssistant.completion list {
border-top-right-radius: 11px;
margin: 0px;
}
GtkSourceAssistant.completion list separator.horizontal {
margin-top: 2px;
margin-bottom: 2px;
}
GtkSourceAssistant.completion list row:selected:not(:backdrop) {
background: @theme_selected_bg_color;
color: @theme_selected_fg_color;
}
GtkSourceAssistant.completion list,
GtkSourceAssistant.completion list row:not(:selected):not(:hover),
GtkSourceAssistant.completion list row cell {
......
......@@ -346,6 +346,10 @@ gtk_source_completion_snippets_display (GtkSourceCompletionProvider *provider,
{
gtk_source_completion_cell_set_text (cell, p->info.description);
}
else
{
gtk_source_completion_cell_set_text (cell, NULL);
}
}
static void
......@@ -368,13 +372,11 @@ gtk_source_completion_snippets_refilter (GtkSourceCompletionProvider *provider,
old_word = g_steal_pointer (&priv->filter_data->word);
if (old_word && g_str_has_prefix (word, old_word))
{
change = GTK_FILTER_CHANGE_MORE_STRICT;
}
else
{
else if (old_word && g_str_has_prefix (old_word, word))
change = GTK_FILTER_CHANGE_LESS_STRICT;
}
else
change = GTK_FILTER_CHANGE_DIFFERENT;
if (priv->filter_data->filter_all)
{
......
......@@ -23,11 +23,22 @@
#include "gtksourcecompletionsnippetsproposal-private.h"
static char *
gtk_source_completion_snippets_proposal_get_typed_text (GtkSourceCompletionProposal *proposal)
{
return g_strdup (GTK_SOURCE_COMPLETION_SNIPPETS_PROPOSAL (proposal)->info.trigger);
}
static void
proposal_iface_init (GtkSourceCompletionProposalInterface *iface)
{
iface->get_typed_text = gtk_source_completion_snippets_proposal_get_typed_text;
}
G_DEFINE_TYPE_WITH_CODE (GtkSourceCompletionSnippetsProposal,
gtk_source_completion_snippets_proposal,
G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (GTK_SOURCE_TYPE_COMPLETION_PROPOSAL, NULL))
G_IMPLEMENT_INTERFACE (GTK_SOURCE_TYPE_COMPLETION_PROPOSAL, proposal_iface_init))
enum {
PROP_0,
......
......@@ -374,6 +374,10 @@ gtk_source_completion_words_display (GtkSourceCompletionProvider *provider,
{
gtk_source_completion_cell_set_icon_name (cell, "completion-word-symbolic");
}
else
{
gtk_source_completion_cell_set_text (cell, NULL);
}
}
static void
......
......@@ -45,10 +45,22 @@ enum
static GParamSpec *properties[N_PROPS];
static guint signals[N_SIGNALS];
static char *
gtk_source_completion_words_proposal_get_typed_text (GtkSourceCompletionProposal *proposal)
{
return g_strdup (GTK_SOURCE_COMPLETION_WORDS_PROPOSAL (proposal)->word);
}
static void
proposal_iface_init (GtkSourceCompletionProposalInterface *iface)
{
iface->get_typed_text = gtk_source_completion_words_proposal_get_typed_text;
}
G_DEFINE_TYPE_WITH_CODE (GtkSourceCompletionWordsProposal,
gtk_source_completion_words_proposal,
G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (GTK_SOURCE_TYPE_COMPLETION_PROPOSAL, NULL))
G_IMPLEMENT_INTERFACE (GTK_SOURCE_TYPE_COMPLETION_PROPOSAL, proposal_iface_init))
static void
gtk_source_completion_words_proposal_finalize (GObject *object)
......
......@@ -42,19 +42,19 @@ struct _GtkSourceAssistantClass
GdkRectangle *rect);
};
GtkSourceAssistant *_gtk_source_assistant_new (void);
void _gtk_source_assistant_attach (GtkSourceAssistant *assistant,
GtkSourceAssistant *attached_to);
void _gtk_source_assistant_detach (GtkSourceAssistant *assistant);
void _gtk_source_assistant_get_offset (GtkSourceAssistant *assistant,
int *x,
int *y);
GtkTextMark *_gtk_source_assistant_get_mark (GtkSourceAssistant *assistant);
void _gtk_source_assistant_set_mark (GtkSourceAssistant *assistant,
GtkTextMark *mark);
void _gtk_source_assistant_set_child (GtkSourceAssistant *assistant,
GtkWidget *child);
void _gtk_source_assistant_reposition (GtkSourceAssistant *assistant);
void _gtk_source_assistant_destroy (GtkSourceAssistant *assistant);
GtkSourceAssistant *_gtk_source_assistant_new (void);
void _gtk_source_assistant_attach (GtkSourceAssistant *assistant,
GtkSourceAssistant *attached_to);
void _gtk_source_assistant_detach (GtkSourceAssistant *assistant);
void _gtk_source_assistant_get_offset (GtkSourceAssistant *assistant,
int *x,
int *y);
GtkTextMark *_gtk_source_assistant_get_mark (GtkSourceAssistant *assistant);
void _gtk_source_assistant_set_mark (GtkSourceAssistant *assistant,
GtkTextMark *mark);
void _gtk_source_assistant_set_child (GtkSourceAssistant *assistant,
GtkWidget *child);
void _gtk_source_assistant_set_needs_position (GtkSourceAssistant *assistant);
void _gtk_source_assistant_destroy (GtkSourceAssistant *assistant);
G_END_DECLS
......@@ -29,6 +29,7 @@ typedef struct
{
GtkTextMark *mark;
GtkSourceAssistantChild *child;
guint reposition_handler;
} GtkSourceAssistantPrivate;
static void buildable_iface_init (GtkBuildableIface *iface);
......@@ -58,7 +59,7 @@ _gtk_source_assistant_hide_action (GtkWidget *widget,
{
g_assert (GTK_SOURCE_IS_ASSISTANT (widget));
gtk_popover_popdown (GTK_POPOVER (widget));
gtk_widget_hide (widget);
}
static void
......@@ -116,15 +117,12 @@ get_gutter_width (GtkSourceView *view)
return 0;
}
static void
static gboolean
_gtk_source_assistant_update_position (GtkSourceAssistant *assistant)
{
GtkSourceAssistantPrivate *priv = _gtk_source_assistant_get_instance_private (assistant);
const GList *children = NULL;
GtkWidget *parent;
GdkRectangle rect;
int x = 0;
int y = 0;
gboolean changed = FALSE;
g_assert (GTK_SOURCE_IS_ASSISTANT (assistant));
......@@ -133,6 +131,10 @@ _gtk_source_assistant_update_position (GtkSourceAssistant *assistant)
if (GTK_SOURCE_IS_VIEW (parent))
{
GdkRectangle visible_rect;
GdkRectangle old_rect;
GdkRectangle rect;
int old_x, old_y;
int x, y;
gtk_text_view_get_visible_rect (GTK_TEXT_VIEW (parent), &visible_rect);
......@@ -143,30 +145,88 @@ _gtk_source_assistant_update_position (GtkSourceAssistant *assistant)
rect.y -= visible_rect.y;
rect.x += get_gutter_width (GTK_SOURCE_VIEW (parent));
gtk_popover_set_offset (GTK_POPOVER (assistant), x, y);
gtk_popover_set_pointing_to (GTK_POPOVER (assistant), &rect);
gtk_popover_get_offset (GTK_POPOVER (assistant), &old_x, &old_y);
if (old_x != x || old_y != y)
{
gtk_popover_set_offset (GTK_POPOVER (assistant), x, y);
changed = TRUE;
}
if (!gtk_popover_get_pointing_to (GTK_POPOVER (assistant), &old_rect) ||
!gdk_rectangle_equal (&old_rect, &rect))
{
gtk_popover_set_pointing_to (GTK_POPOVER (assistant), &rect);
changed = TRUE;
}
}
if (priv->child != NULL)
{
children = _gtk_source_assistant_child_get_attached (priv->child);
}
const GList *children = _gtk_source_assistant_child_get_attached (priv->child);
for (const GList *iter = children; iter; iter = iter->next)
{
GtkSourceAssistant *child = iter->data;
for (const GList *iter = children; iter; iter = iter->next)
{
GtkSourceAssistant *child = iter->data;
int x, y;
_gtk_source_assistant_get_offset (child, &x, &y);
gtk_popover_set_offset (GTK_POPOVER (child), x, y);
_gtk_source_assistant_get_offset (child, &x, &y);
gtk_popover_set_offset (GTK_POPOVER (child), x, y);
}
}
return changed;
}
static gboolean
gtk_source_assistant_reposition_tick_cb (GtkWidget *widget,
GdkFrameClock *frame_clock,
gpointer user_data)
{
GtkSourceAssistant *self = user_data;
GtkSourceAssistantPrivate *priv = _gtk_source_assistant_get_instance_private (self);
g_assert (GTK_SOURCE_IS_VIEW (widget));
g_assert (GDK_IS_FRAME_CLOCK (frame_clock));
g_assert (GTK_SOURCE_IS_ASSISTANT (self));
priv->reposition_handler = 0;
_gtk_source_assistant_update_position (self);
/* We have to gtk_widget_queue_resize() to ensure that the view will
* continue to have an allocation when the assistant gets snapshotted.
* gtk_widget_queue_allocate() is not enough to make that happen.
* Without this, we often get flickering of the GtkSourceView.
*/
gtk_widget_queue_resize (widget);
return G_SOURCE_REMOVE;
}
void
_gtk_source_assistant_reposition (GtkSourceAssistant *self)
_gtk_source_assistant_set_needs_position (GtkSourceAssistant *self)
{
GtkSourceAssistantPrivate *priv = _gtk_source_assistant_get_instance_private (self);
g_return_if_fail (GTK_SOURCE_IS_ASSISTANT (self));
_gtk_source_assistant_update_position (self);
if (priv->reposition_handler == 0)
{
GtkSourceView *view = _gtk_source_assistant_get_view (self);
if (view == NULL)
{
return;
}
priv->reposition_handler =
gtk_widget_add_tick_callback (GTK_WIDGET (view),
gtk_source_assistant_reposition_tick_cb,
g_object_ref (self),
g_object_unref);
}
}
static void
......@@ -176,7 +236,7 @@ _gtk_source_assistant_show (GtkWidget *widget)
g_assert (GTK_SOURCE_IS_ASSISTANT (assistant));
_gtk_source_assistant_reposition (assistant);
_gtk_source_assistant_update_position (assistant);
GTK_WIDGET_CLASS (_gtk_source_assistant_parent_class)->show (widget);
}
......@@ -220,7 +280,7 @@ _gtk_source_assistant_real_get_offset (GtkSourceAssistant *assistant,
}
else
{
*y = (margin.bottom / 3 * 2) - 1;
*y = margin.bottom - 1;
}
}
......@@ -232,6 +292,19 @@ _gtk_source_assistant_dispose (GObject *object)
g_assert (GTK_SOURCE_IS_ASSISTANT (self));
if (priv->reposition_handler != 0)
{
GtkSourceView *view = _gtk_source_assistant_get_view (self);
if (view != NULL)
{
gtk_widget_remove_tick_callback (GTK_WIDGET (view),
priv->reposition_handler);
}
priv->reposition_handler = 0;
}
_gtk_source_assistant_detach (self);
g_clear_object (&priv->mark);
......@@ -253,7 +326,6 @@ _gtk_source_assistant_class_init (GtkSourceAssistantClass *klass)
klass->get_target_location = _gtk_source_assistant_real_get_target_location;
gtk_widget_class_install_action (widget_class, "assistant.hide", NULL, _gtk_source_assistant_hide_action);
gtk_widget_class_add_binding_action (widget_class, GDK_KEY_Escape, 0, "assistant.hide", NULL);
gtk_widget_class_set_css_name (widget_class, "GtkSourceAssistant");
}
......
/*
* This file is part of GtkSourceView
*
* Copyright 2015 Christian Hergert <christian@hergert.me>
* Copyright 2015 Garrett Regier <garrettregier@gmail.com>
*
* 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, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
#include <gio/gio.h>
G_BEGIN_DECLS
#define GTK_SOURCE_TYPE_BINDING_GROUP (gtk_source_binding_group_get_type())
G_DECLARE_FINAL_TYPE (GtkSourceBindingGroup, gtk_source_binding_group, GTK_SOURCE, BINDING_GROUP, GObject)
GtkSourceBindingGroup *gtk_source_binding_group_new (void);
GObject *gtk_source_binding_group_get_source (GtkSourceBindingGroup *self);
void gtk_source_binding_group_set_source (GtkSourceBindingGroup *self,
gpointer source);
void gtk_source_binding_group_bind (GtkSourceBindingGroup *self,
const gchar *source_property,
gpointer target,
const gchar *target_property,
GBindingFlags flags);
void gtk_source_binding_group_bind_full (GtkSourceBindingGroup *self,
const gchar *source_property,
gpointer target,
const gchar *target_property,
GBindingFlags flags,
GBindingTransformFunc transform_to,
GBindingTransformFunc transform_from,
gpointer user_data,
GDestroyNotify user_data_destroy);
void gtk_source_binding_group_bind_with_closures (GtkSourceBindingGroup *self,
const gchar *source_property,
gpointer target,
const gchar *target_property,
GBindingFlags flags,
GClosure *transform_to,
GClosure *transform_from);
G_END_DECLS
This diff is collapsed.
......@@ -24,14 +24,12 @@
#include "config.h"
#include "gtksourcebindinggroup-private.h"
#include "gtksourcecompletion-private.h"
#include "gtksourcecompletioncontext-private.h"
#include "gtksourcecompletionlist-private.h"
#include "gtksourcecompletionproposal.h"
#include "gtksourcecompletionprovider.h"
#include "gtksourcebuffer.h"
#include "gtksourcesignalgroup-private.h"
#include "gtksourceview-private.h"
/**
......@@ -106,17 +104,17 @@ struct _GtkSourceCompletion
* our current context. That includes handling notification of the first
* result so that we can show the window, etc.
*/
GtkSourceSignalGroup *context_signals;
GSignalGroup *context_signals;
/* Signals to changes in the underlying GtkTextBuffer that we use to
* determine where and how we can do completion.
*/
GtkSourceSignalGroup *buffer_signals;
GSignalGroup *buffer_signals;
/* We need to track various events on the view to ensure that we don't
* activate at incorrect times.
*/
GtkSourceSignalGroup *view_signals;
GSignalGroup *view_signals;
/* The display popover for results */
GtkSourceCompletionList *display;
......@@ -150,6 +148,12 @@ struct _GtkSourceCompletion
*/
guint page_size;
/* Handler for gtk_widget_add_tick_callback() to do delayed calls to
* gtk_widget_hide() (so that we don't potentially flap between
* hide/show while typing.
*/
guint hide_tick_handler;
/* If we're currently being displayed */
guint shown : 1;
......@@ -197,29 +201,69 @@ static GParamSpec *properties [N_PROPS];
static guint signals [N_SIGNALS];
static void
display_show (GtkSourceCompletionList *display)
display_show (GtkSourceCompletion *self)
{
g_assert (GTK_SOURCE_IS_COMPLETION_LIST (display));
g_assert (GTK_SOURCE_IS_COMPLETION (self));
if (self->hide_tick_handler != 0)
{
/* See display_hide() for why a tick handler is used */
gtk_widget_remove_tick_callback (GTK_WIDGET (self->view),
self->hide_tick_handler);
self->hide_tick_handler = 0;
}
gtk_widget_show (GTK_WIDGET (display));
gtk_widget_grab_focus (GTK_WIDGET (display));
gtk_widget_show (GTK_WIDGET (_gtk_source_completion_get_display (self)));
}
static void
display_hide (GtkSourceCompletionList *display)
static gboolean
display_hide_cb (GtkWidget *widget,
GdkFrameClock *frame_clock,
gpointer user_data)
{
GtkWidget *view;
GtkSourceCompletion *self = user_data;
g_assert (GTK_SOURCE_IS_VIEW (widget));
g_assert (GDK_IS_FRAME_CLOCK (frame_clock));
g_assert (GTK_SOURCE_IS_COMPLETION (self));
self->hide_tick_handler = 0;
if (self->display != NULL)
{
gtk_widget_hide (GTK_WIDGET (self->display));
}
g_assert (GTK_SOURCE_IS_COMPLETION_LIST (display));
return G_SOURCE_REMOVE;
}
gtk_widget_hide (GTK_WIDGET (display));
static void
display_hide (GtkSourceCompletion *self)
{
g_assert (GTK_SOURCE_IS_COMPLETION (self));
view = gtk_widget_get_ancestor (GTK_WIDGET (display), GTK_SOURCE_TYPE_VIEW);
/* We don't want to hide immediately because we might get another
* change that causes the assistant to be redisplayed before the
* next frame. Flapping the visibility is really distracting, so we'll
* wait until the next start of the frame clock cycle to hide.
*
* We use the GtkSourceView's frame clock for this instead of the GtkPopover
* because that is the frame clock we need to synchronize with to be sure
* were not invalidating allocations during an active frame cycle.
*/
if (view != NULL)
if (self->display == NULL ||
self->hide_tick_handler != 0 ||
!gtk_widget_get_visible (GTK_WIDGET (self->display)))
{
gtk_widget_grab_focus (view);
return;
}
self->hide_tick_handler =
gtk_widget_add_tick_callback (GTK_WIDGET (self->view),
display_hide_cb,
g_object_ref (self),
g_object_unref);
}
static gboolean
......@@ -302,7 +346,6 @@ gtk_source_completion_complete_cb (GObject *object,
gpointer user_data)
{
GtkSourceCompletionContext *context = (GtkSourceCompletionContext *)object;
GtkSourceCompletionList *list;
GtkSourceCompletion *self = user_data;
GError *error = NULL;
......@@ -335,12 +378,14 @@ gtk_source_completion_complete_cb (GObject *object,
_gtk_source_completion_context_refilter (context);
}
list = _gtk_source_completion_get_display (self);
if (!gtk_source_completion_context_get_empty (context))
display_show (list);
{
display_show (self);
}
else
display_hide (list);
{
display_hide (self);
}
cleanup:
g_clear_error (&error);
......@@ -357,7 +402,7 @@ _gtk_source_completion_set_context (GtkSourceCompletion *self,
if (g_set_object (&self->context, context))
{
g_clear_handle_id (&self->queued_update, g_source_remove);
gtk_source_signal_group_set_target (self->context_signals, context);
g_signal_group_set_target (self->context_signals, context);
}
}
......@@ -471,9 +516,13 @@ gtk_source_completion_start (GtkSourceCompletion *self,
_gtk_source_completion_list_set_context (self->display, context);
if (!gtk_source_completion_context_get_empty (context))
display_show (self->display);
{
display_show (self);
}
else
display_hide (self->display);
{
display_hide (self);
}
}
cleanup:
......@@ -503,8 +552,6 @@ gtk_source_completion_update (GtkSourceCompletion *self,
if (_gtk_source_completion_context_can_refilter (self->context, &begin, &end))
{
GtkSourceCompletionList *display = _gtk_source_completion_get_display (self);
/*
* Make sure we update providers that have already delivered results
* even though some of them won't be ready yet.
......@@ -522,9 +569,13 @@ gtk_source_completion_update (GtkSourceCompletion *self,
}
if (!gtk_source_completion_context_get_empty (self->context))
display_show (display);
{
display_show (self);
}
else
display_hide (display);
{
display_hide (self);
}
return;
}
......@@ -600,9 +651,13 @@ gtk_source_completion_real_show (GtkSourceCompletion *self)
_gtk_source_completion_list_set_context (display, self->context);
if (!gtk_source_completion_context_get_empty (self->context))
display_show (display);
{
display_show (self);
}
else
display_hide (display);
{
display_hide (self);
}
}
static gboolean
......@@ -651,17 +706,23 @@ gtk_source_completion_notify_context_empty_cb (GtkSourceCompletion *self,
g_assert (GTK_SOURCE_IS_COMPLETION_CONTEXT (context));
if (context != self->context)
{
/* Delayed notification from a context we no longer care about.
* Just silently drop the notification.
*/
return;
}
if (gtk_source_completion_context_get_empty (context))
{
if (self->display != NULL)
display_hide (self->display);
{
display_hide (self);
}
}
else
{
GtkSourceCompletionList *display = _gtk_source_completion_get_display (self);
display_show (display);
display_show (self);
}
}
......@@ -834,7 +895,7 @@ gtk_source_completion_set_view (GtkSourceCompletion *self,
if (g_set_weak_pointer (&self->view, view))
{
gtk_source_signal_group_set_target (self->view_signals, view);
g_signal_group_set_target (self->view_signals, view);
g_object_bind_property (view, "buffer",
self->buffer_signals, "target",
G_BINDING_SYNC_CREATE);
......@@ -842,15 +903,15 @@ gtk_source_completion_set_view (GtkSourceCompletion *self,
}
static void
on_buffer_signals_bind (GtkSourceCompletion *self,
GtkSourceBuffer *buffer,
GtkSourceSignalGroup *signals_)
on_buffer_signals_bind (GtkSourceCompletion *self,
GtkSourceBuffer *buffer,
GSignalGroup *signals_)
{
GtkTextIter where;
g_assert (GTK_SOURCE_IS_COMPLETION (self));
g_assert (GTK_SOURCE_IS_BUFFER (buffer));
g_assert (GTK_SOURCE_IS_SIGNAL_GROUP (signals_));
g_assert (G_IS_SIGNAL_GROUP (signals_));
if (self->disposed)
return;
......@@ -877,9 +938,20 @@ gtk_source_completion_dispose (GObject *object)
self->disposed = TRUE;
gtk_source_signal_group_set_target (self->context_signals, NULL);
gtk_source_signal_group_set_target (self->buffer_signals, NULL);
gtk_source_signal_group_set_target (self->view_signals, NULL);
if (self->hide_tick_handler != 0)
{
if (self->view != NULL)
{
gtk_widget_remove_tick_callback (GTK_WIDGET (self->view),
self->hide_tick_handler);
}
self->hide_tick_handler = 0;
}
g_signal_group_set_target (self->context_signals, NULL);
g_signal_group_set_target (self->buffer_signals, NULL);
g_signal_group_set_target (self->view_signals, NULL);
g_clear_pointer ((GtkSourceAssistant **)&self->display, _gtk_source_assistant_destroy);
......@@ -914,6 +986,10 @@ gtk_source_completion_get_property (GObject *object,
switch (prop_id)
{
case PROP_PAGE_SIZE:
g_value_set_uint (value, gtk_source_completion_get_page_size (self));
break;
case PROP_REMEMBER_INFO_VISIBILITY:
g_value_set_boolean (value, self->remember_info_visibility);
break;
......@@ -945,6 +1021,10 @@ gtk_source_completion_set_property (GObject *object,
switch (prop_id)
{
case PROP_PAGE_SIZE:
gtk_source_completion_set_page_size (self, g_value_get_uint (value));
break;
case PROP_REMEMBER_INFO_VISIBILITY:
self->remember_info_visibility = g_value_get_boolean (value);
if (self->display != NULL)
......@@ -1148,9 +1228,9 @@ gtk_source_completion_init (GtkSourceCompletion *self)
{
self->cancellable = g_cancellable_new ();
self->providers = g_ptr_array_new_with_free_func (g_object_unref);
self->buffer_signals = gtk_source_signal_group_new (GTK_TYPE_TEXT_BUFFER);
self->context_signals = gtk_source_signal_group_new (GTK_SOURCE_TYPE_COMPLETION_CONTEXT);
self->view_signals = gtk_source_signal_group_new (GTK_SOURCE_TYPE_VIEW);
self->buffer_signals = g_signal_group_new (GTK_TYPE_TEXT_BUFFER);
self->context_signals = g_signal_group_new (GTK_SOURCE_TYPE_COMPLETION_CONTEXT);
self->view_signals = g_signal_group_new (GTK_SOURCE_TYPE_VIEW);
self->page_size = DEFAULT_PAGE_SIZE;
self->show_icons = TRUE;
......@@ -1159,11 +1239,11 @@ gtk_source_completion_init (GtkSourceCompletion *self)
* having results (or vice-versa, when we've filtered to the point of
* no results).
*/
gtk_source_signal_group_connect_object (self->context_signals,
"notify::empty",
G_CALLBACK (gtk_source_completion_notify_context_empty_cb),
self,
G_CONNECT_SWAPPED);
g_signal_group_connect_object (self->context_signals,
"notify::empty",
G_CALLBACK (gtk_source_completion_notify_context_empty_cb),
self,
G_CONNECT_SWAPPED);
/*
* We need to know when the buffer inserts or deletes text so that we
......@@ -1175,41 +1255,41 @@ gtk_source_completion_init (GtkSourceCompletion *self)
G_CALLBACK (on_buffer_signals_bind),
self,
G_CONNECT_SWAPPED);
gtk_source_signal_group_connect_object (self->buffer_signals,
"delete-range",
G_CALLBACK (gtk_source_completion_buffer_delete_range_after_cb),
self,
G_CONNECT_AFTER | G_CONNECT_SWAPPED);
gtk_source_signal_group_connect_object (self->buffer_signals,
"insert-text",
G_CALLBACK (gtk_source_completion_buffer_insert_text_after_cb),
self,
G_CONNECT_AFTER | G_CONNECT_SWAPPED);
gtk_source_signal_group_connect_object (self->buffer_signals,
"mark-set",
G_CALLBACK (gtk_source_completion_buffer_mark_set_cb),
self,
G_CONNECT_SWAPPED);
g_signal_group_connect_object (self->buffer_signals,
"delete-range",
G_CALLBACK (gtk_source_completion_buffer_delete_range_after_cb),
self,
G_CONNECT_AFTER | G_CONNECT_SWAPPED);
g_signal_group_connect_object (self->buffer_signals,
"insert-text",
G_CALLBACK (gtk_source_completion_buffer_insert_text_after_cb),
self,
G_CONNECT_AFTER | G_CONNECT_SWAPPED);
g_signal_group_connect_object (self->buffer_signals,
"mark-set",
G_CALLBACK (gtk_source_completion_buffer_mark_set_cb),
self,
G_CONNECT_SWAPPED);
/*
* We track some events on the view that owns our GtkSourceCompletion instance so
* that we can hide the window when it definitely should not be displayed.
*/
gtk_source_signal_group_connect_object (self->view_signals,
"move-cursor",
G_CALLBACK (gtk_source_completion_view_move_cursor_cb),
self,
G_CONNECT_AFTER | G_CONNECT_SWAPPED);
gtk_source_signal_group_connect_object (self->view_signals,
"paste-clipboard",
G_CALLBACK (gtk_source_completion_block_interactive),
self,
G_CONNECT_SWAPPED);
gtk_source_signal_group_connect_object (self->view_signals,
"paste-clipboard",
G_CALLBACK (gtk_source_completion_unblock_interactive),
self,
G_CONNECT_AFTER | G_CONNECT_SWAPPED);
g_signal_group_connect_object (self->view_signals,
"move-cursor",
G_CALLBACK (gtk_source_completion_view_move_cursor_cb),
self,
G_CONNECT_AFTER | G_CONNECT_SWAPPED);
g_signal_group_connect_object (self->view_signals,
"paste-clipboard",
G_CALLBACK (gtk_source_completion_block_interactive),
self,
G_CONNECT_SWAPPED);
g_signal_group_connect_object (self->view_signals,
"paste-clipboard",
G_CALLBACK (gtk_source_completion_unblock_interactive),
self,
G_CONNECT_AFTER | G_CONNECT_SWAPPED);
}
/**
......@@ -1424,7 +1504,7 @@ _gtk_source_completion_get_display (GtkSourceCompletion *self)
* @priority: (out) (allow-none): An optional location for the score of the match
*
* This helper function can do a fuzzy match for you giving a haystack and
* casefolded needle.
* casefolded needle.
*
* Casefold your needle using [func@GLib.utf8_casefold] before
* running the query.
......@@ -1514,7 +1594,7 @@ add_attributes (PangoAttrList **attrs,
*attrs = pango_attr_list_new ();
}
attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE);
attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE_LINE);
attr->start_index = begin;
attr->end_index = end;
pango_attr_list_insert (*attrs, g_steal_pointer (&attr));
......