Skip to content
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

Delete button for saveloadwindow, make this window less error prone (and some more) #5674

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions data/lang/core/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,10 @@
"description": "",
"message": "This saved game file could not be written because of a system error."
},
"GAME_SAVE_INVALID_NAME": {
"description": "",
"message": "This saved game file could not be written because of a invalid name."
},
"GENERAL_VIEW_CONTROLS": {
"description": "",
"message": "General View Controls"
Expand Down
24 changes: 24 additions & 0 deletions data/lang/ui-core/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1291,6 +1291,30 @@
"description": "",
"message": "Load"
},
"DELETE": {
"description": "",
"message": "Delete"
},
"DELETE_SAVE_CONFIRMATION": {
"description": "",
"message": "Are you sure you want to delete this save?"
},
"COULD_NOT_DELETE_SAVE": {
"description": "",
"message": "Could not delete save"
},
"SAVE_DELETED_SUCCESSFULLY": {
"description": "",
"message": "Save deleted successfully"
},
"SELECTED_SAVE_DOESNT_EXISTS": {
"description": "",
"message": "Selected save doesn't exists"
},
"SELECTED_SAVE_IS_NOT_A_VALID_SAVE": {
"description": "",
"message": "Selected save is not a valid save"
},
"LOAD_GAME": {
"description": "",
"message": "Load game"
Expand Down
4 changes: 4 additions & 0 deletions data/meta/CoreObject/Game.meta.lua
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ function Game.SaveGameStats(filename) end
---@param filename string
function Game.SaveGame(filename) end

--- Delete savefile with specified filename.
---@param filename string
function Game.DeleteSave(filename) end

--- End the current game and return to the main menu.
function Game.EndGame() end

Expand Down
1 change: 1 addition & 0 deletions data/pigui/libs/forwarded.lua
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ ui.setNextWindowSizeConstraints = pigui.SetNextWindowSizeConstraints ---@type fu
-- Forwarded as-is for use in complicated layout primitives without introducing additional scopes
ui.beginGroup = pigui.BeginGroup
ui.endGroup = pigui.EndGroup
ui.getTime = pigui.GetTime

ui.dummy = pigui.Dummy
ui.newLine = pigui.NewLine
Expand Down
30 changes: 29 additions & 1 deletion data/pigui/libs/message-box.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

local ui = require 'pigui'
local ModalWindow = require 'pigui.libs.modal-win'
local Lang = require 'Lang'
local lui = Lang.GetResource("ui-core")

local OK_BUTTON_WIDTH = 100

Expand All @@ -16,9 +18,35 @@ msgbox.OK = function(msg)
ui.dummy(Vector2((width - OK_BUTTON_WIDTH) / 2, 0))
ui.sameLine()
end
if ui.button("OK", Vector2(OK_BUTTON_WIDTH, 0)) then
if ui.button(lui.OK, Vector2(OK_BUTTON_WIDTH, 0)) then
self:close()
end
end,
function (_, drawPopupFn)
ui.setNextWindowPosCenter('Always')
ui.withStyleColors({ PopupBg = ui.theme.colors.modalBackground }, drawPopupFn)
end):open()
end

msgbox.OK_CANCEL = function(msg, callback)
ModalWindow.New('PopupMessageBox', function(self)
ui.text(msg)
local width = ui.getContentRegion().x
local okButton = ui.button(lui.OK, Vector2(OK_BUTTON_WIDTH, 0))
if okButton then
if callback then
callback(okButton)
end
self:close()
end
ui.sameLine(width - OK_BUTTON_WIDTH, 0)
if ui.button(lui.CANCEL, Vector2(OK_BUTTON_WIDTH, 0)) then
self:close()
end
end,
function (_, drawPopupFn)
ui.setNextWindowPosCenter('Always')
ui.withStyleColors({ PopupBg = ui.theme.colors.modalBackground }, drawPopupFn)
end):open()
end

Expand Down
38 changes: 38 additions & 0 deletions data/pigui/libs/ui-timer.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
-- Copyright © 2008-2023 Pioneer Developers. See AUTHORS.txt for details
-- Licensed under the terms of the GPL v3. See licenses/GPL-3.txt

local ui = require 'pigui'
Max5377 marked this conversation as resolved.
Show resolved Hide resolved

local timers = {}

function ui.createTimer(name, endTime, callback)
timers[name] = {}
local timer = timers[name]
timer.endTime = ui.getTime() + endTime
timer.callback = callback
end

function ui.deleteTimer(name)
local timer = timers[name]
if not timer then return end
if timer.callback then
timer.callback()
end
timers[name] = nil
end

local function updateTimers()
if next(timers) then
for name, timer in pairs(timers) do
if ui.getTime() > timer.endTime then
ui.deleteTimer(name)
end
end
end
end

ui.registerModule('ui-timer', function()
updateTimers()
end)

return ui
112 changes: 93 additions & 19 deletions data/pigui/modules/saveloadgame.lua
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,26 @@ local Color = _G.Color

local ui = require 'pigui'
local ModalWindow = require 'pigui.libs.modal-win'
local MessageBox = require 'pigui.libs.message-box'
local UITimer = require 'pigui.libs.ui-timer'

local optionButtonSize = ui.rescaleUI(Vector2(100, 32))
local winSize = Vector2(ui.screenWidth * 0.4, ui.screenHeight * 0.6)
local pionillium = ui.fonts.pionillium

local searchText = lc.SEARCH .. ':'
local saveText = lui.SAVE .. ':'
local errText = lui.ERROR .. ': '
local caseSensitiveText = lui.CASE_SENSITIVE

local saveFileCache = {}
local selectedSave
local saveIsValid = true
local saveInList
local showDeleteResult = false
local successDeleteSaveResult = false
local timerName = "displaySuccessfulResult"
local dissapearTime = 3.0

local minSearchTextLength = 1
local searchSave = ""
Expand All @@ -35,11 +43,13 @@ local caseSensitive = false
local function optionTextButton(label, enabled, callback)
local variant = not enabled and ui.theme.buttonColors.disabled or nil
local button
ui.withFont(pionillium.medium.name, pionillium.medium.size, function()
ui.withFont(pionillium.medium, function()
button = ui.button(label, optionButtonSize, variant)
end)
if button then
callback(button)
if enabled and callback then
callback(button)
end
end
end

Expand Down Expand Up @@ -94,17 +104,34 @@ local function closeAndClearCache()
ui.saveLoadWindow:close()
ui.saveLoadWindow.mode = nil
saveFileCache = {}
popupOpened = false
saveInList = false
selectedSave = ""
searchSave = ""
UITimer.deleteTimer(timerName)
showDeleteResult = false
end

local function closeAndLoadOrSave()
if selectedSave ~= nil and selectedSave ~= '' then
if ui.saveLoadWindow.mode == "LOAD" and saveIsValid then
Game.LoadGame(selectedSave)
closeAndClearCache()
local success, err
if ui.saveLoadWindow.mode == "LOAD" then
if saveIsValid then
success, err = pcall(Game.LoadGame, selectedSave)
else
MessageBox.OK(lui.SELECTED_SAVE_IS_NOT_A_VALID_SAVE)
end
elseif ui.saveLoadWindow.mode == "SAVE" then
Game.SaveGame(selectedSave)
closeAndClearCache()
success, err = pcall(Game.SaveGame, selectedSave)
else
logWarning("Unknown saveLoadWindow mode: " .. ui.saveLoadWindow.mode)
end
if success ~= nil then
if not success then
MessageBox.OK(errText .. err)
else
closeAndClearCache()
end
end
end
end
Expand All @@ -113,17 +140,16 @@ end
local function displaySave(f)
if ui.selectable(f.name, f.name == selectedSave, {"SpanAllColumns", "DontClosePopups", "AllowDoubleClick"}) then
selectedSave = f.name
saveIsValid = pcall(Game.SaveGameStats, f.name)
if ui.isMouseDoubleClicked(0) then
closeAndLoadOrSave()
saveIsValid = pcall(Game.SaveGameStats, f.name)
if ui.isMouseDoubleClicked(0) then
closeAndLoadOrSave()
end
end

if ui.isItemHovered("ForTooltip") then
ui.setTooltip(getSaveTooltip(f.name))
end


ui.nextColumn()
ui.text(Format.Date(f.mtime.timestamp))
ui.nextColumn()
Expand All @@ -138,17 +164,57 @@ local function showSaveFiles()
else
table.sort(files, function(a,b) return (a.mtime.timestamp > b.mtime.timestamp) end)
ui.columns(2,"##saved_games",true)
local wasInList = false
for _,f in pairs(files) do
if(shouldDisplayThisSave(f)) then
displaySave(f)
if not wasInList and (f.name == selectedSave) then
wasInList = true
end
end
end
ui.columns(1,"",false)
saveInList = wasInList
end
end

local function deleteSave()
successDeleteSaveResult = Game.DeleteSave(selectedSave)
showDeleteResult = true
if successDeleteSaveResult then
UITimer.createTimer(timerName, dissapearTime, function()
showDeleteResult = false
end)
else
return
end
selectedSave = ''
end

local function showDeleteSaveResult(saving)
if successDeleteSaveResult then
local textSize = ui.calcTextSize(lui.SAVE_DELETED_SUCCESSFULLY, pionillium.small)
if saving then
ui.sameLine(ui.getContentRegion().x - textSize.x)
else
local txt_hshift = math.max(0, (optionButtonSize.y - ui.getFrameHeight() + textSize.y) / 2)
ui.sameLine(ui.getContentRegion().x - textSize.x - (ui.getItemSpacing().x + ui.getWindowPadding().x + optionButtonSize.x * 3.2))
ui.addCursorPos(Vector2(0, txt_hshift))
end
ui.withFont(pionillium.small, function()
ui.text(lui.SAVE_DELETED_SUCCESSFULLY)
end)
else
MessageBox.OK(lui.COULD_NOT_DELETE_SAVE)
showDeleteResult = false
end
end

local function showDeleteConfirmation()
MessageBox.OK_CANCEL(lui.DELETE_SAVE_CONFIRMATION, deleteSave)
end

local function drawSearchHeader(txt_width)
ui.withFont(pionillium.medium.name, pionillium.medium.size, function()
ui.withFont(pionillium.medium, function()
ui.text(searchText)
ui.nextItemWidth(txt_width, 0)
searchSave, _ = ui.inputText("##searchSave", searchSave, {})
Expand All @@ -161,13 +227,15 @@ local function drawSearchHeader(txt_width)
end

local function drawOptionButtons(txt_width, saving)

-- for vertical center alignment
local txt_hshift = math.max(0, (optionButtonSize.y - ui.getFrameHeight()) / 2)
local mode = saving and lui.SAVE or lui.LOAD
ui.sameLine(txt_width + ui.getWindowPadding().x + ui.getItemSpacing().x)
ui.addCursorPos(Vector2(0, saving and -txt_hshift or txt_hshift))
optionTextButton(mode, selectedSave ~= nil and selectedSave ~= '' and saveIsValid, closeAndLoadOrSave)
optionTextButton(mode, ((saving and (selectedSave ~= nil and selectedSave ~= '')) or (not saving and saveInList)), closeAndLoadOrSave)
ui.sameLine()
ui.addCursorPos(Vector2(0, saving and -txt_hshift or txt_hshift))
optionTextButton(lui.DELETE, saveInList, showDeleteConfirmation)
ui.sameLine()
ui.addCursorPos(Vector2(0, saving and -txt_hshift or txt_hshift))
optionTextButton(lui.CANCEL, true, closeAndClearCache)
Expand All @@ -194,17 +262,23 @@ ui.saveLoadWindow = ModalWindow.New("LoadGame", function()

ui.separator()

-- a little padding just before the window border, so that the cancel button will not be cut out
txt_width = txt_width / 1.03
-- a padding just before the window border, so that the cancel button will not be cut out
txt_width = txt_width / 1.38
if saving then
ui.withFont(pionillium.medium.name, pionillium.medium.size, function()
ui.withFont(pionillium.medium, function()
ui.text(saveText)
if showDeleteResult then
showDeleteSaveResult(saving)
end
ui.nextItemWidth(txt_width, 0)
selectedSave = ui.inputText("##saveFileName", selectedSave or "", {})
end)
else
if showDeleteResult then
showDeleteSaveResult(saving)
end
end
drawOptionButtons(txt_width, saving)

end, function (_, drawPopupFn)
ui.setNextWindowSize(winSize, "Always")
ui.setNextWindowPosCenter('Always')
Expand Down
1 change: 1 addition & 0 deletions data/pigui/views/game.lua
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@ ui.registerHandler('game', function(delta_t)
end

callModules('modal')
callModules('ui-timer')

if ui.ctrlHeld() and ui.isKeyReleased(ui.keys.delete) then
gameView.debugReload()
Expand Down
1 change: 1 addition & 0 deletions data/pigui/views/mainmenu.lua
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ local function showMainMenu()

callModules('mainMenu')
callModules('modal')
callModules('ui-timer')
end -- showMainMenu

ui.registerHandler('mainMenu', showMainMenu)
4 changes: 3 additions & 1 deletion src/FileSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ namespace FileSystem {

std::string GetUserDir();
std::string GetDataDir();
bool IsValidFilename(const std::string &fileName);

/// Makes a string safe for use as a file name
/// warning: this mapping is non-injective, that is,
Expand Down Expand Up @@ -194,7 +195,6 @@ namespace FileSystem {
virtual ~FileDataMalloc() { std::free(m_data); }
};


class FileEnumerator {
public:
enum Flags {
Expand Down Expand Up @@ -304,6 +304,8 @@ namespace FileSystem {
FILE *OpenReadStream(const std::string &path);
// similar to fopen(path, "wb")
FILE *OpenWriteStream(const std::string &path, int flags = 0);
bool RemoveFile(const std::string &relativePath);
bool IsChildOfRoot(const std::string &path);
};

class FileSourceUnion : public FileSource {
Expand Down
Loading