diff --git a/src/ui/assets/icons/folder-16.png b/src/ui/assets/icons/folder-16.png new file mode 100644 index 00000000..64422d9d Binary files /dev/null and b/src/ui/assets/icons/folder-16.png differ diff --git a/src/ui/assets/icons/folder-24.png b/src/ui/assets/icons/folder-24.png new file mode 100644 index 00000000..facfb117 Binary files /dev/null and b/src/ui/assets/icons/folder-24.png differ diff --git a/src/ui/forms/fields/path.lua b/src/ui/forms/fields/path.lua index 9bd2e1e1..8ddb1c8c 100644 --- a/src/ui/forms/fields/path.lua +++ b/src/ui/forms/fields/path.lua @@ -1,12 +1,54 @@ +local ui = require("ui") +local uiElements = require("ui.elements") +local uiUtils = require("ui.utils") + local utils = require("utils") local mods = require("mods") local loadedState = require("loaded_state") local stringField = require("ui.forms.fields.string") +local iconUtils = require("ui.utils.icons") +local fileLocations = require("file_locations") local pathField = {} pathField.fieldType = "path" +local function openFileDialog(textField, options) + local relativeToMod = options.relativeToMod + local allowedExtensions = options.allowedExtensions + + local filter + local startingPath = fileLocations.getCelesteDir() + + if allowedExtensions then + filter = table.concat(allowedExtensions, ",") + end + + if relativeToMod ~= false then + local modPath = mods.getFilenameModPath(loadedState.filename) + + if not modPath then + return false + end + + startingPath = modPath + end + + utils.openDialog(startingPath, filter, function(filename) + if relativeToMod ~= false then + local modPath = mods.getFilenameModPath(filename) + + if not modPath then + return false + end + + filename = string.sub(filename, #modPath + 2) + end + + textField:setText(filename) + end) +end + function pathField.getElement(name, value, options) -- Add extra options and pass it onto string field @@ -63,7 +105,34 @@ function pathField.getElement(name, value, options) return true end - return stringField.getElement(name, value, options) + local stringElement = stringField.getElement(name, value, options) + local textfield = stringElement.field + + if textfield.height == -1 then + textfield:layout() + end + + local iconMaxSize = textfield.label.height + local parentHeight = textfield.height + local folderIcon, iconSize = iconUtils.getIcon("folder", iconMaxSize) + + if folderIcon then + local centerOffset = math.floor((parentHeight - iconSize) / 2) + local folderImage = uiElements.image(folderIcon):with(uiUtils.rightbound(0)):with(uiUtils.at(0, centerOffset)) + + folderImage.interactive = 1 + folderImage:hook({ + onClick = function(orig, self) + orig(self) + + openFileDialog(textfield, options) + end + }) + + textfield:addChild(folderImage) + end + + return stringElement end return pathField \ No newline at end of file diff --git a/src/ui/themes.lua b/src/ui/themes.lua index 7209f37c..26ec3f57 100644 --- a/src/ui/themes.lua +++ b/src/ui/themes.lua @@ -8,6 +8,7 @@ local themes = {} themes.themes = {} themes.currentTheme = nil +themes.defaultThemeName = "dark" function themes.unloadThemes() themes.themes = {} @@ -75,7 +76,7 @@ end function themes.useTheme(name) if name and themes.themes[name] then - themes.currentTheme = name + themes.currentTheme = themes.themes[name] themer.apply(themes.themes[name]) diff --git a/src/ui/ui_device.lua b/src/ui/ui_device.lua index 6e93b048..9b5b34ec 100644 --- a/src/ui/ui_device.lua +++ b/src/ui/ui_device.lua @@ -35,7 +35,7 @@ function debugUtils.reloadUI() themes.unloadThemes() themes.loadInternalThemes() themes.loadExternalThemes() - themes.useTheme(themes.currentTheme) + themes.useTheme(themes.currentTheme.name) uiRoot.updateWindows(windows.getLoadedWindows()) @@ -62,7 +62,7 @@ function ui.initializeDevice() local appliedTheme = themes.useTheme(configTheme) if not appliedTheme then - themes.useTheme("dark") + themes.useTheme(themes.defaultThemeName) end end diff --git a/src/ui/utils/icons.lua b/src/ui/utils/icons.lua new file mode 100644 index 00000000..5a0b58c1 --- /dev/null +++ b/src/ui/utils/icons.lua @@ -0,0 +1,93 @@ +local themes = require("ui.themes") +local utils = require("utils") + +local iconUtils = {} + +local iconSizes = {64, 32, 24, 16} +local iconCache = {} + +local previousThemeName = themes.defaultThemeName + +function iconUtils.clearCache(name) + if name then + for _, size in ipairs(iconSizes) do + local cacheKey = string.format("%s-%s", name, size) + + iconCache[cacheKey] = nil + end + + else + iconCache = {} + end +end + +function iconUtils.getBestSize(maxSize) + for _, size in ipairs(iconSizes) do + if size <= maxSize then + return size + end + end +end + +function iconUtils.getIcon(name, maxSize, allowCustom) + local currentTheme = themes.currentTheme + + -- Invalidate all icons if theme has changed + if currentTheme.name ~= previousThemeName then + iconUtils.clearCache() + + previousThemeName = currentTheme.name + end + + local targetSize = iconUtils.getBestSize(maxSize) + local cacheKey = string.format("%s-%s", name, targetSize) + + if iconCache[cacheKey] then + return unpack(iconCache[cacheKey]) + end + + local iconsPath = "ui/assets/icons/" + + if currentTheme and currentTheme.iconsPath then + if allowCustom ~= false then + iconsPath = currentTheme.iconsPath + end + end + + -- Add image to cache for all sizes that fit + local image + local actualSize + local relevantSizes = {} + + for _, size in ipairs(iconSizes) do + if size <= maxSize then + table.insert(relevantSizes, size) + + local path = utils.joinpath(iconsPath, string.format("%s-%s.png", name, size)) + local fileInfo = love.filesystem.getInfo(path) or utils.pathAttributes(path) + + if fileInfo then + image = love.graphics.newImage(path) + actualSize = size + + break + end + end + end + + if image then + for _, size in ipairs(relevantSizes) do + cacheKey = string.format("%s-%s", name, size) + iconCache[cacheKey] = {image, actualSize} + end + + return image, actualSize + end + + -- Fallback to default theme + if allowCustom ~= false then + return iconUtils.getIcon(name, maxSize, false) + end +end + +return iconUtils \ No newline at end of file