Saving The Content To A File
In this lesson you will learn how to add a menu entry with a key shortcut, ask the user to select a file for saving the Gtk::TextBuffer contents, and save a file.
Add the “Save As” menu item
- Open the UI definition file for your window and find the
primary_menumenu definition at the bottom of the file - Remove the “Preferences” menu item, as we are not going to need it
- In place of the removed menu item, add the definition of the Save As menu item:
xml
<menu id="primary_menu">
<section>
<item>
<attribute name="label" translatable="yes">_Save as...</attribute>
<attribute name="action">win.save-as</attribute>
</item>
<item>
<attribute name="label" translatable="yes">_Keyboard Shortcuts</attribute>
<attribute name="action">win.show-help-overlay</attribute>
</item>
<item>
<attribute name="label" translatable="yes">_About {{name}}</attribute>
<attribute name="action">app.about</attribute>
</item>
</section>
</menu>The “Save as” menu item is bound to the win.save-as action; this means that activating the menu item will activate the save-as action registered on the Text::Viewer::Window window.
Add the “Save As” action
- Open the
window.crfile, and find the class constructor of theText::Viewer::Windowwidget - Create the
save-asaction, connect a callback to itsactivatesignal, and add the action to the window
crystal
def initialize
super()
@header_bar = Adw::HeaderBar.cast(template_child("header_bar"))
@main_text_view = Gtk::TextView.cast(template_child("main_text_view"))
@open_button = Gtk::Button.cast(template_child("open_button"))
@cursor_pos = Gtk::Label.cast(template_child("cursor_pos"))
open_action = Gio::SimpleAction.new("open", nil)
open_action.activate_signal.connect do
self.open_file_dialog
end
self.add_action open_action
save_action = Gio::SimpleAction.new("save-as", nil)
save_action.activate_signal.connect do
self.save_file_dialog
end
self.add_action save_action
@main_text_view.buffer.notify_signal["cursor-position"].connect do
buffer = @main_text_view.buffer
cursor_position = buffer.cursor_position
iter = buffer.iter_at_offset(cursor_position)
@cursor_pos.label = "Ln #{iter.line}, Col #{iter.line_offset}"
end
endSelect a file
- In the
activatecallback method for thesave-asaction, create a file selection dialog using theGtk::FileChooserAction::Saveaction, and connect to itsresponsesignal
crystal
private def save_file_dialog
filechooser = Gtk::FileChooserNative.new("Save File As", nil, Gtk::FileChooserAction::Save, "_Save", "_Cancel")
filechooser.transient_for = self
filechooser.response_signal.connect do |response|
end
filechooser.show
end- In the method, retrieve the
Gio::Filefor the location selected by the user, and call the save_file method
crystal
private def save_file_dialog
filechooser = Gtk::FileChooserNative.new("Save File As", nil, Gtk::FileChooserAction::Save, "_Save", "_Cancel")
filechooser.transient_for = self
filechooser.response_signal.connect do |response|
if Gtk::ResponseType.from_value(response) == Gtk::ResponseType::Accept
self.save_file(filechooser.file)
end
end
filechooser.show
endSave the contents of the text buffer
In the save_file method, retrieve the contents of the Gtk::TextBuffer using the start and end Gtk::TextIter as the bounds of the buffer, then save it to the Gio::File's Path using Crystal's File IO
crystal
private def save_file(file : Gio::File?)
return if file.nil?
file_path = file.not_nil!.path.not_nil!
File.open(file_path, "w") do |file_io|
buffer = @main_text_view.buffer
# Retrieve the iterator at the start of the buffer
start_iter = buffer.start_iter
# Retrieve the iterator at the end of the buffer
end_iter = buffer.end_iter
# Retrieve all the visible text between the two bounds
text = buffer.text(start_iter, end_iter, false)
return if text.size == 0
file_io.print(text)
end
endAdd a key shortcut for the “Save As” action
- Open the
text-viewer.crsource file and find the constructor for theText::Viewer::Appclass - Add Ctrl + Shift + S as the accelerator shortcut for the
win.save-asaction
crystal
#...
class App < Adw::Application
def initialize
#...
self.set_accels_for_action("app.quit", {"<primary>q"})
self.set_accels_for_action("win.open", {"<Ctrl>o"})
self.set_accels_for_action("win.save-as", {"<Ctrl><Shift>s"})
#...
end
#...Add the “Save As” shortcut to the Keyboard Shortcuts help
- Find the
help-overlay.uifile in the sources directory - Find the
GtkShortcutsGroupdefinition - Add a new
GtkShortcutsShortcutdefinition for thewin.saveaction in the shortcuts group
xml
<object class="GtkShortcutsGroup">
<property name="title" translatable="yes" context="shortcut window">General</property>
<child>
<object class="GtkShortcutsShortcut">
<property name="title" translatable="yes" context="shortcut window">Open</property>
<property name="action-name">win.open</property>
</object>
</child>
<child>
<object class="GtkShortcutsShortcut">
<property name="title" translatable="yes" context="shortcut window">Save As</property>
<property name="action-name">win.save-as</property>
</object>
</child>At the end of this lesson, you should be able to:
- select the “Save As” menu item from the primary menu
- select a file from a dialog
- save the contents of the text viewer in the selected file