1
0
Fork 0
mirror of https://github.com/Orama-Interactive/Pixelorama.git synced 2025-01-18 17:19:50 +00:00

Add autosave feature with backup of unsaved new projects (#221)

* Add autosave feature with backup of unsaved new projects.

* Fix wrong indentation on line 205.

* Store backup for every opened file in user://. Some other improvements.

* Remove unnecessary variable.

* Update Translations.pot

Co-authored-by: Manolis Papadeas <35376950+OverloadedOrama@users.noreply.github.com>
This commit is contained in:
Martin Novák 2020-04-30 19:33:24 +02:00 committed by GitHub
parent c82c54d096
commit 82fe186b65
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 343 additions and 110 deletions

View file

@ -1676,6 +1676,14 @@ visible = false
[node name="RightCursor" type="Sprite" parent="."]
visible = false
[node name="BackupConfirmation" type="ConfirmationDialog" parent="."]
margin_right = 349.0
margin_bottom = 88.0
rect_min_size = Vector2( 250, 87.5 )
popup_exclusive = true
dialog_text = "Autosaved backup for %s was found.
Do you want to reload it?"
[connection signal="mouse_entered" from="MenuAndUI/UI/CanvasAndTimeline/HViewportContainer/ViewportAndRulers/HorizontalRuler" to="MenuAndUI/UI/CanvasAndTimeline/HViewportContainer/ViewportAndRulers/HorizontalRuler" method="_on_HorizontalRuler_mouse_entered"]
[connection signal="pressed" from="MenuAndUI/UI/CanvasAndTimeline/HViewportContainer/ViewportAndRulers/HorizontalRuler" to="MenuAndUI/UI/CanvasAndTimeline/HViewportContainer/ViewportAndRulers/HorizontalRuler" method="_on_HorizontalRuler_pressed"]
[connection signal="pressed" from="MenuAndUI/UI/CanvasAndTimeline/HViewportContainer/ViewportAndRulers/HSplitContainer/ViewportandVerticalRuler/VerticalRuler" to="MenuAndUI/UI/CanvasAndTimeline/HViewportContainer/ViewportAndRulers/HSplitContainer/ViewportandVerticalRuler/VerticalRuler" method="_on_VerticalRuler_pressed"]
@ -1762,6 +1770,7 @@ visible = false
[connection signal="file_selected" from="PaletteImportFileDialog" to="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/PaletteVBoxContainer/ScrollPalette/CenterPalette/PaletteContainer" method="on_palette_import_file_selected"]
[connection signal="popup_hide" from="PaletteImportFileDialog" to="MenuAndUI/UI/RightPanel/PreviewAndPalettes/ToolAndPaletteVSplit/PaletteVBoxContainer/ScrollPalette/CenterPalette/PaletteContainer" method="_on_NewPaletteDialog_popup_hide"]
[connection signal="popup_hide" from="HSVDialog" to="." method="_can_draw_true"]
[connection signal="popup_hide" from="BackupConfirmation" to="." method="_can_draw_true"]
[editable path="BrushesPopup/TabContainer/File/FileBrushContainer/CircleBrushButton"]

View file

@ -49,39 +49,30 @@ size_flags_horizontal = 3
[node name="VBoxContainer" type="VBoxContainer" parent="HSplitContainer/ScrollContainer"]
margin_right = 308.0
margin_bottom = 1186.0
margin_bottom = 1230.0
size_flags_horizontal = 3
[node name="General" type="VBoxContainer" parent="HSplitContainer/ScrollContainer/VBoxContainer"]
margin_right = 306.0
margin_bottom = 116.0
[node name="OpenLastProject" type="CheckBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/General"]
margin_right = 306.0
margin_bottom = 24.0
hint_tooltip = "Opens last opened project on startup"
mouse_default_cursor_shape = 2
pressed = true
text = "Open last project on startup"
margin_bottom = 180.0
[node name="SmoothZoom" type="CheckBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/General"]
margin_top = 28.0
margin_right = 306.0
margin_bottom = 52.0
margin_bottom = 24.0
hint_tooltip = "Adds a smoother transition when zooming in or out"
mouse_default_cursor_shape = 2
pressed = true
text = "Smooth Zoom"
[node name="HSeparator2" type="HSeparator" parent="HSplitContainer/ScrollContainer/VBoxContainer/General"]
margin_top = 56.0
margin_top = 28.0
margin_right = 306.0
margin_bottom = 60.0
margin_bottom = 32.0
[node name="GridContainer" type="GridContainer" parent="HSplitContainer/ScrollContainer/VBoxContainer/General"]
margin_top = 64.0
margin_top = 36.0
margin_right = 306.0
margin_bottom = 116.0
margin_bottom = 88.0
custom_constants/vseparation = 4
custom_constants/hseparation = 4
columns = 2
@ -125,6 +116,11 @@ size_flags_horizontal = 3
pressed = true
text = "Show right tool icon"
[node name="HSeparator3" type="HSeparator" parent="HSplitContainer/ScrollContainer/VBoxContainer/General"]
margin_top = 92.0
margin_right = 306.0
margin_bottom = 96.0
[node name="PressureSentivity" type="HBoxContainer" parent="HSplitContainer/ScrollContainer/VBoxContainer/General"]
visible = false
margin_top = 116.0
@ -145,10 +141,56 @@ text = "Affect Brush's Alpha"
items = [ "None", null, false, 0, null, "Affect Brush's Alpha", null, false, 1, null ]
selected = 1
[node name="Languages" type="VBoxContainer" parent="HSplitContainer/ScrollContainer/VBoxContainer"]
margin_top = 120.0
[node name="OpenLastProject" type="CheckBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/General"]
margin_top = 100.0
margin_right = 306.0
margin_bottom = 512.0
margin_bottom = 124.0
hint_tooltip = "Opens last opened project on startup"
mouse_default_cursor_shape = 2
pressed = true
text = "Open last project on startup"
[node name="EnableAutosave" type="CheckBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/General"]
margin_top = 128.0
margin_right = 306.0
margin_bottom = 152.0
text = "Enable autosave"
__meta__ = {
"_edit_use_anchors_": false
}
[node name="AutosaveInterval" type="HBoxContainer" parent="HSplitContainer/ScrollContainer/VBoxContainer/General"]
margin_top = 156.0
margin_right = 306.0
margin_bottom = 180.0
__meta__ = {
"_edit_use_anchors_": false
}
[node name="AutosaveIntervalLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/General/AutosaveInterval"]
margin_top = 5.0
margin_right = 115.0
margin_bottom = 19.0
text = "Autosave interval:"
[node name="AutosaveInterval" type="SpinBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/General/AutosaveInterval"]
margin_left = 119.0
margin_right = 306.0
margin_bottom = 24.0
size_flags_horizontal = 3
min_value = 1.0
max_value = 30.0
value = 1.0
align = 2
suffix = "minute(s)"
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Languages" type="VBoxContainer" parent="HSplitContainer/ScrollContainer/VBoxContainer"]
margin_top = 184.0
margin_right = 306.0
margin_bottom = 576.0
[node name="System Language" type="CheckBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/Languages"]
margin_right = 306.0
@ -252,9 +294,9 @@ custom_fonts/font = ExtResource( 3 )
text = "繁體中文 [zh_TW]"
[node name="Themes" type="VBoxContainer" parent="HSplitContainer/ScrollContainer/VBoxContainer"]
margin_top = 516.0
margin_top = 580.0
margin_right = 306.0
margin_bottom = 652.0
margin_bottom = 716.0
[node name="Dark Theme" type="CheckBox" parent="HSplitContainer/ScrollContainer/VBoxContainer/Themes"]
margin_right = 306.0
@ -291,9 +333,9 @@ mouse_default_cursor_shape = 2
text = "Light"
[node name="Canvas" type="VBoxContainer" parent="HSplitContainer/ScrollContainer/VBoxContainer"]
margin_top = 656.0
margin_top = 720.0
margin_right = 306.0
margin_bottom = 848.0
margin_bottom = 912.0
[node name="GuideOptions" type="GridContainer" parent="HSplitContainer/ScrollContainer/VBoxContainer/Canvas"]
margin_right = 306.0
@ -465,9 +507,9 @@ mouse_default_cursor_shape = 2
color = Color( 1, 1, 1, 1 )
[node name="Image" type="VBoxContainer" parent="HSplitContainer/ScrollContainer/VBoxContainer"]
margin_top = 852.0
margin_top = 916.0
margin_right = 306.0
margin_bottom = 928.0
margin_bottom = 992.0
[node name="ImageOptions" type="GridContainer" parent="HSplitContainer/ScrollContainer/VBoxContainer/Image"]
margin_right = 306.0
@ -538,9 +580,9 @@ mouse_default_cursor_shape = 2
color = Color( 0, 0, 0, 0 )
[node name="Shortcuts" type="VBoxContainer" parent="HSplitContainer/ScrollContainer/VBoxContainer"]
margin_top = 932.0
margin_top = 996.0
margin_right = 306.0
margin_bottom = 1186.0
margin_bottom = 1230.0
[node name="HBoxContainer" type="HBoxContainer" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts"]
margin_right = 306.0
@ -571,7 +613,8 @@ margin_bottom = 28.0
[node name="Shortcuts" type="GridContainer" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts"]
margin_top = 32.0
margin_right = 306.0
margin_bottom = 254.0
margin_bottom = 234.0
custom_constants/vseparation = 2
custom_constants/hseparation = 5
columns = 3
@ -598,9 +641,9 @@ text = "Right Tool:"
align = 1
[node name="Empty2" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
margin_top = 18.0
margin_top = 16.0
margin_right = 137.0
margin_bottom = 22.0
margin_bottom = 20.0
[node name="HSeparator" type="HSeparator" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
visible = false
@ -610,174 +653,174 @@ margin_bottom = 22.0
[node name="HSeparator2" type="HSeparator" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
margin_left = 142.0
margin_top = 18.0
margin_top = 16.0
margin_right = 221.0
margin_bottom = 22.0
margin_bottom = 20.0
[node name="HSeparator3" type="HSeparator" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
margin_left = 226.0
margin_top = 18.0
margin_top = 16.0
margin_right = 305.0
margin_bottom = 22.0
margin_bottom = 20.0
[node name="RectSelectLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
margin_top = 29.0
margin_top = 25.0
margin_right = 137.0
margin_bottom = 43.0
margin_bottom = 39.0
text = "Rectangular Selection"
[node name="left_rectangle_select_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
margin_left = 142.0
margin_top = 26.0
margin_top = 22.0
margin_right = 221.0
margin_bottom = 46.0
margin_bottom = 42.0
size_flags_horizontal = 3
[node name="right_rectangle_select_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
margin_left = 226.0
margin_top = 26.0
margin_top = 22.0
margin_right = 305.0
margin_bottom = 46.0
margin_bottom = 42.0
size_flags_horizontal = 3
[node name="ZoomLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
margin_top = 53.0
margin_top = 47.0
margin_right = 137.0
margin_bottom = 67.0
margin_bottom = 61.0
text = "Zoom"
[node name="left_zoom_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
margin_left = 142.0
margin_top = 50.0
margin_top = 44.0
margin_right = 221.0
margin_bottom = 70.0
margin_bottom = 64.0
size_flags_horizontal = 3
[node name="right_zoom_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
margin_left = 226.0
margin_top = 50.0
margin_top = 44.0
margin_right = 305.0
margin_bottom = 70.0
margin_bottom = 64.0
size_flags_horizontal = 3
[node name="ColorPickerLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
margin_top = 77.0
margin_top = 69.0
margin_right = 137.0
margin_bottom = 91.0
margin_bottom = 83.0
text = "Color Picker"
[node name="left_colorpicker_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
margin_left = 142.0
margin_top = 74.0
margin_top = 66.0
margin_right = 221.0
margin_bottom = 94.0
margin_bottom = 86.0
[node name="right_colorpicker_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
margin_left = 226.0
margin_top = 74.0
margin_top = 66.0
margin_right = 305.0
margin_bottom = 94.0
margin_bottom = 86.0
[node name="PencilLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
margin_top = 101.0
margin_top = 91.0
margin_right = 137.0
margin_bottom = 115.0
margin_bottom = 105.0
text = "Pencil"
[node name="left_pencil_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
margin_left = 142.0
margin_top = 98.0
margin_top = 88.0
margin_right = 221.0
margin_bottom = 118.0
margin_bottom = 108.0
[node name="right_pencil_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
margin_left = 226.0
margin_top = 98.0
margin_top = 88.0
margin_right = 305.0
margin_bottom = 118.0
margin_bottom = 108.0
[node name="EraserLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
margin_top = 125.0
margin_top = 113.0
margin_right = 137.0
margin_bottom = 139.0
margin_bottom = 127.0
text = "Eraser"
[node name="left_eraser_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
margin_left = 142.0
margin_top = 122.0
margin_top = 110.0
margin_right = 221.0
margin_bottom = 142.0
margin_bottom = 130.0
[node name="right_eraser_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
margin_left = 226.0
margin_top = 122.0
margin_top = 110.0
margin_right = 305.0
margin_bottom = 142.0
margin_bottom = 130.0
[node name="BucketLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
margin_top = 149.0
margin_top = 135.0
margin_right = 137.0
margin_bottom = 163.0
margin_bottom = 149.0
text = "Bucket"
[node name="left_fill_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
margin_left = 142.0
margin_top = 146.0
margin_top = 132.0
margin_right = 221.0
margin_bottom = 166.0
margin_bottom = 152.0
[node name="right_fill_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
margin_left = 226.0
margin_top = 146.0
margin_top = 132.0
margin_right = 305.0
margin_bottom = 166.0
margin_bottom = 152.0
[node name="LightenDarkenLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
margin_top = 173.0
margin_top = 157.0
margin_right = 137.0
margin_bottom = 187.0
margin_bottom = 171.0
text = "Lighten/Darken"
[node name="left_lightdark_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
margin_left = 142.0
margin_top = 170.0
margin_top = 154.0
margin_right = 221.0
margin_bottom = 190.0
margin_bottom = 174.0
[node name="right_lightdark_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
margin_left = 226.0
margin_top = 170.0
margin_top = 154.0
margin_right = 305.0
margin_bottom = 190.0
margin_bottom = 174.0
[node name="HSeparator4" type="HSeparator" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
margin_top = 194.0
margin_top = 176.0
margin_right = 137.0
margin_bottom = 198.0
margin_bottom = 180.0
[node name="HSeparator5" type="HSeparator" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
margin_left = 142.0
margin_top = 194.0
margin_top = 176.0
margin_right = 221.0
margin_bottom = 198.0
margin_bottom = 180.0
[node name="HSeparator6" type="HSeparator" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
margin_left = 226.0
margin_top = 194.0
margin_top = 176.0
margin_right = 305.0
margin_bottom = 198.0
margin_bottom = 180.0
[node name="Switch Colors" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
margin_top = 205.0
margin_top = 185.0
margin_right = 137.0
margin_bottom = 219.0
margin_bottom = 199.0
text = "Switch Colors"
[node name="switch_colors" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
margin_left = 142.0
margin_top = 202.0
margin_top = 182.0
margin_right = 221.0
margin_bottom = 222.0
margin_bottom = 202.0
[node name="Popups" type="Node" parent="."]
@ -805,13 +848,15 @@ __meta__ = {
[connection signal="about_to_show" from="." to="." method="_on_PreferencesDialog_about_to_show"]
[connection signal="popup_hide" from="." to="." method="_on_PreferencesDialog_popup_hide"]
[connection signal="item_selected" from="HSplitContainer/Tree" to="." method="_on_Tree_item_selected"]
[connection signal="pressed" from="HSplitContainer/ScrollContainer/VBoxContainer/General/OpenLastProject" to="." method="_on_OpenLastProject_pressed"]
[connection signal="pressed" from="HSplitContainer/ScrollContainer/VBoxContainer/General/SmoothZoom" to="." method="_on_SmoothZoom_pressed"]
[connection signal="toggled" from="HSplitContainer/ScrollContainer/VBoxContainer/General/GridContainer/LeftIndicatorCheckbox" to="." method="_on_LeftIndicatorCheckbox_toggled"]
[connection signal="toggled" from="HSplitContainer/ScrollContainer/VBoxContainer/General/GridContainer/RightIndicatorCheckbox" to="." method="_on_RightIndicatorCheckbox_toggled"]
[connection signal="toggled" from="HSplitContainer/ScrollContainer/VBoxContainer/General/GridContainer/LeftToolIconCheckbox" to="." method="_on_LeftToolIconCheckbox_toggled"]
[connection signal="toggled" from="HSplitContainer/ScrollContainer/VBoxContainer/General/GridContainer/RightToolIconCheckbox" to="." method="_on_RightToolIconCheckbox_toggled"]
[connection signal="item_selected" from="HSplitContainer/ScrollContainer/VBoxContainer/General/PressureSentivity/PressureSensitivityOptionButton" to="." method="_on_PressureSensitivityOptionButton_item_selected"]
[connection signal="pressed" from="HSplitContainer/ScrollContainer/VBoxContainer/General/OpenLastProject" to="." method="_on_OpenLastProject_pressed"]
[connection signal="toggled" from="HSplitContainer/ScrollContainer/VBoxContainer/General/EnableAutosave" to="." method="_on_EnableAutosave_toggled"]
[connection signal="value_changed" from="HSplitContainer/ScrollContainer/VBoxContainer/General/AutosaveInterval/AutosaveInterval" to="." method="_on_AutosaveInterval_value_changed"]
[connection signal="color_changed" from="HSplitContainer/ScrollContainer/VBoxContainer/Canvas/GuideOptions/GuideColor" to="." method="_on_GuideColor_color_changed"]
[connection signal="value_changed" from="HSplitContainer/ScrollContainer/VBoxContainer/Canvas/GridOptions/GridWidthValue" to="." method="_on_GridWidthValue_value_changed"]
[connection signal="value_changed" from="HSplitContainer/ScrollContainer/VBoxContainer/Canvas/GridOptions/GridHeightValue" to="." method="_on_GridHeightValue_value_changed"]

View file

@ -41,6 +41,9 @@ func _ready() -> void:
# Disable input until the shortcut selector is displayed
set_process_input(false)
# Replace OK with Close since preference changes are being applied immediately, not after OK confirmation
get_ok().text = tr("Close")
for child in languages.get_children():
if child is Button:
child.connect("pressed", self, "_on_Language_pressed", [child])
@ -76,6 +79,16 @@ func _ready() -> void:
Global.show_right_tool_icon = Global.config_cache.get_value("preferences", "show_right_tool_icon")
right_tool_icon.pressed = Global.show_right_tool_icon
# Get autosave settings
if Global.config_cache.has_section_key("preferences", "autosave_interval"):
var autosave_interval = Global.config_cache.get_value("preferences", "autosave_interval")
OpenSave.set_autosave_interval(autosave_interval)
general.get_node("AutosaveInterval/AutosaveInterval").value = autosave_interval
if Global.config_cache.has_section_key("preferences", "enable_autosave"):
var enable_autosave = Global.config_cache.get_value("preferences", "enable_autosave")
OpenSave.toggle_autosave(enable_autosave)
general.get_node("EnableAutosave").pressed = enable_autosave
# Set default values for Canvas options
if Global.config_cache.has_section_key("preferences", "grid_size"):
var grid_size = Global.config_cache.get_value("preferences", "grid_size")
@ -527,3 +540,15 @@ func _on_OpenLastProject_pressed():
Global.open_last_project = !Global.open_last_project
Global.config_cache.set_value("preferences", "open_last_project", Global.open_last_project)
Global.config_cache.save("user://cache.ini")
func _on_EnableAutosave_toggled(button_pressed : bool) -> void:
OpenSave.toggle_autosave(button_pressed)
Global.config_cache.set_value("preferences", "enable_autosave", button_pressed)
Global.config_cache.save("user://cache.ini")
func _on_AutosaveInterval_value_changed(value : float) -> void:
OpenSave.set_autosave_interval(value)
Global.config_cache.set_value("preferences", "autosave_interval", value)
Global.config_cache.save("user://cache.ini")

View file

@ -193,8 +193,28 @@ func _ready() -> void:
Global.config_cache.set_value("preferences", "open_last_project", true)
if Global.config_cache.get_value("preferences", "open_last_project"):
Global.open_last_project = Global.config_cache.get_value("preferences", "open_last_project")
# If backup file exists then Pixelorama was not closed properly (probably crashed) - reopen backup
$BackupConfirmation.get_cancel().text = tr("Delete")
if Global.config_cache.has_section("backups"):
var project_paths = Global.config_cache.get_section_keys("backups")
if project_paths.size() > 0:
# Get backup path
var backup_path = Global.config_cache.get_value("backups", project_paths[0])
# Temporatily stop autosave until user confirms backup
OpenSave.autosave_timer.stop()
Global.can_draw = false
# For it's only possible to reload the first found backup
$BackupConfirmation.dialog_text = $BackupConfirmation.dialog_text % project_paths[0]
$BackupConfirmation.connect("confirmed", self, "_on_BackupConfirmation_confirmed", [project_paths[0], backup_path])
$BackupConfirmation.get_cancel().connect("pressed", self, "_on_BackupConfirmation_delete", [project_paths[0], backup_path])
$BackupConfirmation.popup_centered()
else:
load_last_project()
else:
load_last_project()
func _input(event : InputEvent) -> void:
Global.left_cursor.position = get_global_mouse_position() + Vector2(-32, 32)
Global.left_cursor.texture = Global.left_cursor_tool_texture
@ -420,16 +440,17 @@ func help_menu_id_pressed(id : int) -> void:
Global.can_draw = false
func load_last_project():
# Check if any project was saved or opened last time
if Global.config_cache.has_section_key("preferences", "last_project_path"):
# Check if file still exists on disk
var file_path = Global.config_cache.get_value("preferences", "last_project_path")
var file_check := File.new()
if file_check.file_exists(file_path): # If yes then load the file
_on_OpenSprite_file_selected(file_path)
else:
# If file doesn't exist on disk then warn user about this
$OpenLastProjectAlertDialog.popup_centered()
if Global.open_last_project:
# Check if any project was saved or opened last time
if Global.config_cache.has_section_key("preferences", "last_project_path"):
# Check if file still exists on disk
var file_path = Global.config_cache.get_value("preferences", "last_project_path")
var file_check := File.new()
if file_check.file_exists(file_path): # If yes then load the file
_on_OpenSprite_file_selected(file_path)
else:
# If file doesn't exist on disk then warn user about this
$OpenLastProjectAlertDialog.popup_centered()
func _on_UnsavedCanvasDialog_confirmed() -> void:
@ -451,7 +472,7 @@ func _on_OpenSprite_file_selected(path : String) -> void:
func _on_SaveSprite_file_selected(path : String) -> void:
OpenSave.save_pxo_file(path)
OpenSave.save_pxo_file(path, false)
# Set last opened project path and save
Global.config_cache.set_value("preferences", "last_project_path", path)
@ -782,16 +803,34 @@ func _on_QuitAndSaveDialog_custom_action(action : String) -> void:
$SaveSprite.popup_centered()
$QuitDialog.hide()
Global.can_draw = false
OpenSave.remove_backup()
func _on_QuitDialog_confirmed() -> void:
# Darken the UI to denote that the application is currently exiting
# (it won't respond to user input in this state).
modulate = Color(0.5, 0.5, 0.5)
OpenSave.remove_backup()
get_tree().quit()
func _on_BackupConfirmation_confirmed(project_path : String, backup_path : String) -> void:
OpenSave.reload_backup_file(project_path, backup_path)
OpenSave.autosave_timer.start()
$ExportDialog.file_name = OpenSave.current_save_path.get_file().trim_suffix(".pxo")
$ExportDialog.directory_path = OpenSave.current_save_path.get_base_dir()
$ExportDialog.was_exported = false
file_menu.set_item_text(3, tr("Save") + " %s" % OpenSave.current_save_path.get_file())
file_menu.set_item_text(6, tr("Export"))
func _on_BackupConfirmation_delete(project_path : String, backup_path : String) -> void:
OpenSave.remove_backup_by_path(project_path, backup_path)
OpenSave.autosave_timer.start()
# Reopen last project
load_last_project()
func _on_LeftPixelPerfectMode_toggled(button_pressed) -> void:
Global.left_pixel_perfect = button_pressed

View file

@ -1,17 +1,31 @@
extends Node
var current_save_path := ""
# Stores a filename of a backup file in user:// until user saves manually
var backup_save_path = ""
onready var autosave_timer : Timer
var default_autosave_interval := 5 # Minutes
func _ready():
autosave_timer = Timer.new()
autosave_timer.one_shot = false
autosave_timer.process_mode = Timer.TIMER_PROCESS_IDLE
autosave_timer.connect("timeout", self, "_on_Autosave_timeout")
add_child(autosave_timer)
set_autosave_interval(default_autosave_interval)
toggle_autosave(false) # Gets started from preferences dialog
func open_pxo_file(path : String) -> void:
func open_pxo_file(path : String, untitled_backup : bool = false) -> void:
var file := File.new()
var err := file.open_compressed(path, File.READ, File.COMPRESSION_ZSTD)
if err == ERR_FILE_UNRECOGNIZED:
err = file.open(path, File.READ) # If the file is not compressed open it raw (pre-v0.7)
if err != OK:
Global.notification_label("File failed to open")
file.close()
OS.alert("Can't load file")
return
var file_version := file.get_line() # Example, "v0.6"
@ -133,13 +147,13 @@ func open_pxo_file(path : String) -> void:
file.close()
current_save_path = path
Global.window_title = path.get_file() + " - Pixelorama"
if not untitled_backup:
# Untitled backup should not change window title and save path
current_save_path = path
Global.window_title = path.get_file() + " - Pixelorama"
func save_pxo_file(path : String) -> void:
current_save_path = path
func save_pxo_file(path : String, autosave : bool) -> void:
var file := File.new()
var err := file.open_compressed(path, File.WRITE, File.COMPRESSION_ZSTD)
if err == OK:
@ -209,10 +223,89 @@ func save_pxo_file(path : String) -> void:
file.store_8(tag[3]) # Tag "to", the last frame
file.store_line("END_FRAME_TAGS")
if !Global.saved:
file.close()
if !Global.saved and not autosave:
Global.saved = true
Global.window_title = current_save_path.get_file() + " - Pixelorama"
Global.notification_label("File saved")
file.close()
if autosave:
Global.notification_label("File autosaved")
else:
# First remove backup then set current save path
remove_backup()
current_save_path = path
Global.notification_label("File saved")
if backup_save_path == "":
Global.window_title = path.get_file() + " - Pixelorama"
else:
Global.notification_label("File failed to save")
func toggle_autosave(enable : bool) -> void:
if enable:
autosave_timer.start()
else:
autosave_timer.stop()
func set_autosave_interval(interval : float) -> void:
autosave_timer.wait_time = interval * 60 # Interval parameter is in minutes, wait_time is seconds
autosave_timer.start()
func _on_Autosave_timeout() -> void:
if backup_save_path == "":
# Create a new backup file if it doesn't exist yet
backup_save_path = "user://backup-" + String(OS.get_unix_time())
store_backup_path()
save_pxo_file(backup_save_path, true)
# Backup paths are stored in two ways:
# 1) User already manually saved and defined a save path -> {current_save_path, backup_save_path}
# 2) User didn't manually saved, "untitled" backup is stored -> {backup_save_path, backup_save_path}
func store_backup_path() -> void:
if current_save_path != "":
# Remove "untitled" backup if it existed on this project instance
if Global.config_cache.has_section_key("backups", backup_save_path):
Global.config_cache.erase_section_key("backups", backup_save_path)
Global.config_cache.set_value("backups", current_save_path, backup_save_path)
else:
Global.config_cache.set_value("backups", backup_save_path, backup_save_path)
Global.config_cache.save("user://cache.ini")
func remove_backup() -> void:
# Remove backup file
if backup_save_path != "":
if current_save_path != "":
remove_backup_by_path(current_save_path, backup_save_path)
else:
# If manual save was not yet done - remove "untitled" backup
remove_backup_by_path(backup_save_path, backup_save_path)
backup_save_path = ""
func remove_backup_by_path(project_path : String, backup_path : String) -> void:
Directory.new().remove(backup_path)
Global.config_cache.erase_section_key("backups", project_path)
Global.config_cache.save("user://cache.ini")
func reload_backup_file(project_path : String, backup_path : String) -> void:
# If project path is the same as backup save path -> the backup was untitled
open_pxo_file(backup_path, project_path == backup_path)
backup_save_path = backup_path
if project_path != backup_path:
current_save_path = project_path
Global.window_title = project_path.get_file() + " - Pixelorama(*)"
Global.saved = false
Global.notification_label("Backup reloaded")

View file

@ -909,6 +909,15 @@ msgstr ""
msgid "File saved"
msgstr ""
msgid "File autosaved"
msgstr ""
msgid "File failed to open"
msgstr ""
msgid "File failed to save"
msgstr ""
msgid "File exported"
msgstr ""
@ -1067,3 +1076,16 @@ msgstr ""
msgid "Creates multiple files but every file is stored in different directory that corresponds to its frame tag"
msgstr ""
msgid "Close"
msgstr ""
msgid "Delete"
msgstr ""
msgid "Autosaved backup for %s was found."
"Do you want to reload it?"
msgstr ""
msgid "Backup reloaded"
msgstr ""