Skip to content

Navigation Menu

Sign in
Sign up

Jeremypw/format bar/use language action #1796

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
jeremypw wants to merge 16 commits into master from jeremypw/FormatBar/use-language-action
Closed
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
e8ed527
FormatBar: use Action for lang state
danirabbit Jul 21, 2026
737e82b
Merge branch 'master' into danirabbit/formatbar-langaction
jeremypw Jul 22, 2026
ad0b305
Merge branch 'master' into danirabbit/formatbar-langaction
jeremypw Aug 13, 2026
904313b
Change a confusing name
jeremypw Aug 13, 2026
067356f
Fix set document on start up
jeremypw Aug 13, 2026
6e6dbb0
Remove unnecessary, duplicate handler
jeremypw Aug 13, 2026
6a880eb
Reorder public before private functions
jeremypw Aug 13, 2026
7bf3ca0
Fix long lines
jeremypw Aug 13, 2026
bf8c02a
Cleanup update current lang
jeremypw Aug 13, 2026
7efa64f
Use CheckButton
jeremypw Aug 13, 2026
54e26f7
Popdown list after choice
jeremypw Aug 13, 2026
498db2c
Do not popdown - like master
jeremypw Aug 13, 2026
8a0685d
Do not set document language to same language
jeremypw Aug 13, 2026
0a0d2c4
Do not call format_tab_header_from_global_settings too early
jeremypw Aug 13, 2026
4b0beb7
Ensure signal handlers connect and disconnect once per doc
jeremypw Aug 13, 2026
b75732d
Move handler ids to start
jeremypw Aug 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
212 changes: 98 additions & 114 deletions src/Widgets/FormatBar.vala
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,19 @@ public class Code.FormatBar : Gtk.Box {
public bool tab_width_set_by_editor_config { get; set; default = false; }

private FormatBox line_formatbox;
private Gtk.MenuButton line_menubutton;
private FormatBox lang_formatbox;
private FormatBox tab_formatbox;
private Granite.SwitchModelButton space_tab_modelbutton;
private Gtk.Entry goto_entry;
private Gtk.InfoBar editorconfig_infobar;
private Gtk.ListBox lang_selection_listbox;
private Gtk.MenuButton line_menubutton;
private Gtk.SourceLanguageManager manager;
private Gtk.SpinButton width_spinbutton;
private LangEntry normal_entry;
private SimpleAction language_action;
private ulong cursor_handler = 0;
private ulong language_handler = 0;

private unowned Scratch.Services.Document? doc = null;
private unowned Scratch.Services.Document? current_doc = null;

construct {
get_style_context ().add_class (Gtk.STYLE_CLASS_LINKED);
Expand Down Expand Up @@ -97,7 +98,7 @@ public class Code.FormatBar : Gtk.Box {
placeholder_text = _("Filter languages")
};

lang_selection_listbox = new Gtk.ListBox () {
var lang_selection_listbox = new Gtk.ListBox () {
selection_mode = SINGLE
};
lang_selection_listbox.set_sort_func ((row1, row2) => {
Expand All @@ -108,16 +109,13 @@ public class Code.FormatBar : Gtk.Box {
return (((LangEntry) row).lang_name.down ().contains (lang_selection_filter.text.down ().strip ()));
});

unowned SList<Gtk.RadioButton> group = null;
foreach (unowned string id in manager.get_language_ids ()) {
weak Gtk.SourceLanguage lang = manager.get_language (id);
var entry = new LangEntry (id, lang.name, group);
group = entry.get_radio_group ();
var entry = new LangEntry (id, lang.name);
lang_selection_listbox.add (entry);
}

normal_entry = new LangEntry (null, _("Plain Text"), group);

var normal_entry = new LangEntry ("", _("Plain Text"));
lang_selection_listbox.add (normal_entry);

var lang_scrolled = new Gtk.ScrolledWindow (null, null) {
Expand Down Expand Up @@ -187,16 +185,32 @@ public class Code.FormatBar : Gtk.Box {
add (lang_menubutton);
add (line_menubutton);

lang_selection_listbox.row_activated.connect ((row) => {
var lang_entry = ((LangEntry) row);
select_language (lang_entry);
language_action = new SimpleAction.stateful ("language", VariantType.STRING, new Variant.string (""));
language_action.change_state.connect ((parameter) => {
language_action.set_state (parameter);
var lang_id = parameter.get_string ();

if (current_doc.source_view.language.id != lang_id) { // Avoids loop
current_doc.source_view.language = lang_id != "" ? manager.get_language (lang_id) : null;
}

if (lang_id != "") {
unowned var lang = manager.get_language (lang_id);
lang_formatbox.text = lang.name;
} else {
lang_formatbox.text = _("Plain Text");
current_doc.source_view.language = null;
}
});

var action_group = new SimpleActionGroup ();
action_group.add_action (language_action);
insert_action_group ("format", action_group);

lang_selection_filter.changed.connect (() => {
lang_selection_listbox.invalidate_filter ();
});

format_tab_header_from_global_settings ();
width_spinbutton.value_changed.connect (() => {
if (!tab_width_set_by_editor_config) {
Scratch.settings.set_int (
Expand All @@ -215,14 +229,16 @@ public class Code.FormatBar : Gtk.Box {
}
});

// We need to connect_after because otherwise, the text isn't parsed into the "value" property and we only get the previous value
// We need to connect_after because otherwise, the text isn't parsed into the "value" property
// and we only get the previous value
goto_entry.activate.connect_after (() => {
int line, column;
goto_entry.text = goto_entry.text.replace (":", ".");
goto_entry.text.scanf ("%i.%i", out line, out column);
doc.source_view.go_to_line (line, column - 1);
// Focuses parent to the source view, so that the cursor, which indicates line and column is actually visible.
doc.source_view.grab_focus ();
current_doc.source_view.go_to_line (line, column - 1);
// Focuses parent to the source view, so that the cursor, which indicates line and column
// is actually visible.
current_doc.source_view.grab_focus ();
});

Scratch.settings.changed["indent-width"].connect (format_tab_header_from_global_settings);
Expand All @@ -236,32 +252,60 @@ public class Code.FormatBar : Gtk.Box {
line_menubutton.active = true;
}

private void select_language (LangEntry lang, bool update_source_view = true) {
lang_selection_listbox.select_row (lang);
lang_formatbox.text = lang.lang_name;
if (update_source_view) {
lang.active = true;
doc.source_view.language = lang.lang_id != null ? manager.get_language (lang.lang_id) : null;
public void set_document (Scratch.Services.Document doc) requires (doc != null) {
if (current_doc != null) {
SignalHandler.disconnect (current_doc, cursor_handler);
SignalHandler.disconnect (current_doc, language_handler);
}

current_doc = doc;
if (doc.loading) {
Timeout.add (200, () => {
if (doc.loading) {
return Source.CONTINUE;
} else {
update_widgets ();
return Source.REMOVE;
}
});
} else {
lang.selected = true;
update_widgets ();
}
}

private void format_tab_header_from_global_settings () {
if (!tab_style_set_by_editor_config) {
set_insert_spaces_instead_of_tabs (Scratch.settings.get_boolean ("spaces-instead-of-tabs"));
public void set_insert_spaces_instead_of_tabs (bool use_spaces) requires (current_doc != null) {
space_tab_modelbutton.active = use_spaces;
current_doc.source_view.insert_spaces_instead_of_tabs = use_spaces;
}

public void set_tab_width (int indent_width) requires (current_doc != null) {
width_spinbutton.@value = indent_width;
if (space_tab_modelbutton.active) {
tab_formatbox.text = ngettext ("%d Space", "%d Spaces", indent_width).printf (indent_width);
} else {
tab_formatbox.text = ngettext ("%d Tab", "%d Tabs", indent_width).printf (indent_width);
}

if (!tab_width_set_by_editor_config) {
set_tab_width (Scratch.settings.get_int ("indent-width"));
current_doc.source_view.indent_width = indent_width;
current_doc.source_view.tab_width = indent_width;
}

private void update_widgets () requires (current_doc != null) {
format_tab_header_from_global_settings ();
update_current_lang ();
format_tab_header_from_global_settings ();
format_line_header ();
if (cursor_handler == 0) {
cursor_handler = current_doc.source_view.buffer.notify["cursor-position"].connect (format_line_header);
}

editorconfig_infobar.revealed = tab_style_set_by_editor_config || tab_width_set_by_editor_config;
space_tab_modelbutton.sensitive = !tab_style_set_by_editor_config;
if (language_handler == 0) {
language_handler = current_doc.source_view.notify["language"].connect (update_current_lang);
}
}

private void format_line_header () {
var buffer = doc.source_view.buffer;
private void format_line_header () requires (current_doc != null) {
var buffer = current_doc.source_view.buffer;
var position = buffer.cursor_position;
Gtk.TextIter iter;
buffer.get_iter_at_offset (out iter, position);
Expand All @@ -270,52 +314,24 @@ public class Code.FormatBar : Gtk.Box {
goto_entry.text = "%d.%d".printf (line, iter.get_line_offset () + 1);
}

public void set_document (Scratch.Services.Document doc) {
if (this.doc != null) {
this.doc.source_view.buffer.notify["cursor-position"].disconnect (format_line_header);
}

this.doc = doc;
update_current_lang ();
format_tab_header_from_global_settings ();
format_line_header ();
this.doc.source_view.buffer.notify["cursor-position"].connect (format_line_header);
private void update_current_lang () requires (current_doc != null) {
var language = current_doc.source_view.language;
var lang_id = language != null ? language.id : "";
// Should not directly modify action state
language_action.change_state (new Variant.string (lang_id));
}

public void set_insert_spaces_instead_of_tabs (bool use_spaces) {
space_tab_modelbutton.active = use_spaces;
if (doc != null) {
doc.source_view.insert_spaces_instead_of_tabs = use_spaces;
}
}

public void set_tab_width (int indent_width) {
width_spinbutton.@value = indent_width;
if (space_tab_modelbutton.active) {
tab_formatbox.text = ngettext ("%d Space", "%d Spaces", indent_width).printf (indent_width);
} else {
tab_formatbox.text = ngettext ("%d Tab", "%d Tabs", indent_width).printf (indent_width);
private void format_tab_header_from_global_settings () {
if (!tab_style_set_by_editor_config) {
set_insert_spaces_instead_of_tabs (Scratch.settings.get_boolean ("spaces-instead-of-tabs"));
}

if (doc != null) {
doc.source_view.indent_width = indent_width;
doc.source_view.tab_width = indent_width;
if (!tab_width_set_by_editor_config) {
set_tab_width (Scratch.settings.get_int ("indent-width"));
}
}

private void update_current_lang () {
var language = doc.source_view.language;
if (language != null) {
var lang_id = language.id;
lang_selection_listbox.get_children ().foreach ((child) => {
var lang_entry = ((LangEntry) child);
if (lang_entry.lang_id == lang_id) {
select_language (lang_entry, false);
}
});
} else {
select_language (normal_entry, false);
}
editorconfig_infobar.revealed = tab_style_set_by_editor_config || tab_width_set_by_editor_config;
space_tab_modelbutton.sensitive = !tab_style_set_by_editor_config;
}

private class FormatBox : Gtk.Box {
Expand Down Expand Up @@ -348,56 +364,24 @@ public class Code.FormatBar : Gtk.Box {
}

private class LangEntry : Gtk.ListBoxRow {
public string? lang_id { get; construct; }
public string lang_id { get; construct; }
public string lang_name { get; construct; }
public unowned SList<Gtk.RadioButton> group { get; construct; }

public bool active {
get {
return lang_radio.active;
}

set {
lang_radio.active = value;
}
}

public bool selected {
get {
return lang_radio.active;
}

set {
lang_radio.toggled.disconnect (radio_toggled);
lang_radio.active = value;
lang_radio.toggled.connect (radio_toggled);
}
}

private Gtk.RadioButton lang_radio;
public LangEntry (string? lang_id, string lang_name, SList<Gtk.RadioButton> group) {
Object (group: group, lang_id: lang_id, lang_name: lang_name);
public LangEntry (string lang_id, string lang_name) {
Object (lang_id: lang_id, lang_name: lang_name);
}

class construct {
set_css_name (Gtk.STYLE_CLASS_MENUITEM);
}

construct {
lang_radio = new Gtk.RadioButton.with_label (group, lang_name);

add (lang_radio);
lang_radio.toggled.connect (radio_toggled);
}

private void radio_toggled () {
if (lang_radio.active) {
activate ();
}
}
var lang_checkbutton = new Gtk.CheckButton.with_label (lang_name) {
action_name = "format.language",
action_target = new Variant.string (lang_id)
};

public unowned SList<Gtk.RadioButton> get_radio_group () {
return lang_radio.get_group ();
child = lang_checkbutton;
}
}
}
Loading

AltStyle によって変換されたページ (->オリジナル) /