Added save/open file dialogs

This commit is contained in:
OmniaX-Dev 2026-04-21 15:28:39 +02:00
parent 8c174fb863
commit 83f4687198
8 changed files with 163 additions and 21 deletions

View file

@ -160,17 +160,8 @@ window.backgroundColor = rgba(160, 160, 160, 255)
useBackgroundGradient = true
padding = rect(15, 8, 15, 8)
margin = rect(0, 0, 0, 0)
showIcon = true
showIcon = false
(icon) {
path = file("img.png")
animation = anim(frameCount: 36, \
fps: 60, \
columns: 9, \
rows: 4, \
frameWidth: 256, \
frameHeight: 256)
size = vec2(32, 32)
animated = true
}
}
(button:hover) {

View file

@ -41,21 +41,21 @@ Implement following widgets:
***Tab Panel
***Button
***Image/Icon
***Save/Open File Dialogs (and folder dialogs)
Radio Button
Grouping
Text Input
Progressbar
Horizontal Slider
Vertical Slider
ListBox
ComboBox
TreeView
Progressbar
Tooltips
Context Menu
Menu Bar
Save/Open File Dialogs (and folder dialogs)
Hex Editor
Layouts
Vertical
Horizontal
Grid
Tooltips
Text Input
TreeView
Hex Editor

View file

@ -369,6 +369,105 @@ namespace ogfx
__on_signal(signal);
}
stdvec<String> WindowCore::showOpenFileDialog(const stdvec<FileDialogFilter>& filterList, bool multiselect, const String& defaultPath) const
{
struct DialogResult {
stdvec<String> paths;
bool completed { false };
};
DialogResult result;
__store_file_dialog_filters(filterList);
SDL_ShowOpenFileDialog(
[](void* userdata, const char* const* filelist, int filter) {
auto* res = static_cast<DialogResult*>(userdata);
if (filelist) {
for (const char* const* f = filelist; *f; f++)
res->paths.push_back(String(*f));
}
res->completed = true;
},
&result,
m_window,
m_sdlFilters.empty() ? nullptr : m_sdlFilters.data(),
m_sdlFilters.size(),
defaultPath.empty() ? nullptr : defaultPath.c_str(),
multiselect
);
while (!result.completed) {
SDL_PumpEvents();
SDL_Delay(10);
}
return result.paths;
}
stdvec<String> WindowCore::showOpenFolderDialog(bool multiselect, const String& defaultPath) const
{
struct DialogResult {
stdvec<String> paths;
bool completed { false };
};
DialogResult result;
SDL_ShowOpenFolderDialog(
[](void* userdata, const char* const* filelist, int filter) {
auto* res = static_cast<DialogResult*>(userdata);
if (filelist) {
for (const char* const* f = filelist; *f; f++)
res->paths.push_back(String(*f));
}
res->completed = true;
},
&result,
m_window,
defaultPath.empty() ? nullptr : defaultPath.c_str(),
multiselect
);
while (!result.completed) {
SDL_PumpEvents();
SDL_Delay(10);
}
return result.paths;
}
String WindowCore::showSaveFileDialog(const stdvec<FileDialogFilter>& filterList, const String& defaultPath) const
{
struct DialogResult {
String path;
bool completed { false };
};
DialogResult result;
__store_file_dialog_filters(filterList);
SDL_ShowSaveFileDialog(
[](void* userdata, const char* const* filelist, int filter) {
auto* res = static_cast<DialogResult*>(userdata);
if (filelist && *filelist) {
res->path = String(*filelist);
}
res->completed = true;
},
&result,
m_window,
m_sdlFilters.empty() ? nullptr : m_sdlFilters.data(),
m_sdlFilters.size(),
defaultPath.empty() ? nullptr : defaultPath.c_str()
);
while (!result.completed) {
SDL_PumpEvents();
SDL_Delay(10);
}
return result.path;
}
MouseEventData WindowCore::get_mouse_state(SDL_Event& event)
{
f32 mx = 0, my = 0, _mx = 0, _my = 0;
@ -637,6 +736,32 @@ namespace ogfx
m_defaultStylesheetVariables["text_left"] = { String("").add(cast<i32>(eTextAlign::Left)), true };
}
void WindowCore::__store_file_dialog_filters(const stdvec<FileDialogFilter>& filters) const
{
m_patternStorage.clear();
m_sdlFilters.clear();
m_patternStorage.reserve(filters.size());
m_sdlFilters.reserve(filters.size());
for (const auto& f : filters)
{
// Join extensions with semicolons: {"png", "jpg"} -> "png;jpg"
String pattern;
for (size_t i = 0; i < f.extensionList.size(); i++)
{
if (i > 0) pattern.add(";");
pattern.add(f.extensionList[i]);
}
m_patternStorage.push_back(pattern);
// SDL filter points at strings that must stay alive
m_sdlFilters.push_back({
f.description.c_str(),
m_patternStorage.back().c_str()
});
}
}

View file

@ -34,6 +34,11 @@ namespace ogfx
{
class WindowCore : public ostd::BaseObject
{
public: struct FileDialogFilter
{
String description { "" };
stdvec<String> extensionList;
};
public: enum class eCursor : u8
{
Default = 0,
@ -79,6 +84,7 @@ namespace ogfx
bool maximized { false };
bool alwaysOnTop { false };
};
public: using FileDialogFilterList = stdvec<ogfx::WindowCore::FileDialogFilter>;
public:
inline WindowCore(void) { }
virtual ~WindowCore(void);
@ -99,7 +105,12 @@ namespace ogfx
void setBlockingEventsRefreshFPS(u32 fps);
void requestRedraw(void);
void handleSignal(ostd::Signal& signal) override;
stdvec<String> showOpenFileDialog(const FileDialogFilterList& filterList, bool multiselect, const String& defaultPath = "") const;
stdvec<String> showOpenFolderDialog(bool multiselect, const String& defaultPath = "") const;
String showSaveFileDialog(const FileDialogFilterList& filterList, const String& defaultPath = "") const;
inline String showOpenFileDialog(const FileDialogFilterList& filterList) const { auto list = showOpenFileDialog(filterList, false); return (list.size() == 0 ? "" : list[0]); }
inline String showOpenFolderDialog(void) const { auto list = showOpenFolderDialog(false); return (list.size() == 0 ? "" : list[0]); }
inline const ostd::Stylesheet* theme(void) const { return m_guiTheme; }
inline void loadDefaultTHeme(void) { setTheme(DefaultTheme); }
inline ostd::Stylesheet::VariableList& getDefaultStylesheetVariableList(void) { return m_defaultStylesheetVariables; }
@ -142,6 +153,7 @@ namespace ogfx
private:
void __handle_event(SDL_Event& event);
void __load_default_stylesheet_variables(void);
void __store_file_dialog_filters(const FileDialogFilterList& filters) const;
protected:
SDL_Window* m_window { nullptr };
@ -191,6 +203,9 @@ namespace ogfx
SDL_Cursor* m_cursor_SW_Resize { nullptr };
SDL_Cursor* m_cursor_W_Resize { nullptr };
mutable stdvec<String> m_patternStorage;
mutable stdvec<SDL_DialogFileFilter> m_sdlFilters;
public:
inline static constexpr i32 MaxBlockingEventsFPS { 240 };
inline static constexpr i32 DefaultBlockingEventsFPS { 30 };

View file

@ -43,11 +43,10 @@ namespace ogfx
void Button::onDraw(ogfx::BasicRenderer2D& gfx)
{
// if (m_textChanged)
if (m_textChanged)
__update_size();
if (isIconEnabled())
{
m_realIconSize = getIconSize().propy(getGlobalContentBounds().getSize().y);
if (isAnimatedEnabled())
gfx.drawAnimation(m_anim, getGlobalContentPosition(), m_realIconSize);
@ -88,7 +87,7 @@ namespace ogfx
String filePath = getThemeValue<String>(theme, "icon.path", m_icon.getFilePath());
if (filePath != m_icon.getFilePath())
setIcon(filePath);
setIconSize(getThemeValue<Vec2>(theme, "icon.size", getSize()));
setIconSize(getThemeValue<Vec2>(theme, "icon.size", getIconSize()));
if (isAnimatedEnabled())
{
m_anim.create(m_animData);
@ -99,6 +98,7 @@ namespace ogfx
void Button::__update_size(void)
{
m_realIconSize = getIconSize().propy(getGlobalContentBounds().getSize().y);
auto size = getWindow().getGFX().getStringDimensions(getText(), getFontSize());
size.x += getPadding().left();
size.x += getPadding().right();

View file

@ -41,9 +41,9 @@ namespace ogfx
void setText(const String& text);
void setIcon(const String& filePath);
void applyTheme(const ostd::Stylesheet& theme) override;
OSTD_BOOL_PARAM_GETSET_E(Animated, m_animated);
inline String getText(void) const { return m_text; }
inline Image& getIcon(void) { return m_icon; }
OSTD_BOOL_PARAM_GETSET_E(Animated, m_animated);
OSTD_BOOL_PARAM_GETSET_E(Icon, m_showIcon);
OSTD_PARAM_GETSET(ostd::AnimationData, AnimationData, m_animData);
OSTD_PARAM_GETSET(Vec2, IconSize, m_iconSize);

View file

@ -84,6 +84,7 @@ namespace ostd
inline String& clr(void) { m_data = ""; return *this; }
inline String& set(const cpp_string& str) { m_data = str; return *this; }
inline stdvec<u32> getUTF8Codepoints(void) const { return decodeUTF8(m_data); }
inline bool empty(void) const { return m_data.empty(); }
inline auto begin(void) { return m_data.begin(); }
inline auto end(void) { return m_data.end(); }

View file

@ -23,6 +23,11 @@
#include <ogfx/utils/Keycodes.hpp>
#include <ogfx/ogfx.hpp>
ogfx::WindowCore::FileDialogFilterList filters = {
{ "Image files", { "png", "jpg", "jpeg", "bmp" } },
{ "All files", { "*" } }
};
ostd::ConsoleOutputHandler out;
using namespace ogfx::gui::widgets;
@ -69,8 +74,13 @@ class Window : public ogfx::gui::Window
m_btn1.setText("BUTTON");
m_btn1.setCallback(ogfx::gui::Widget::eCallback::MousePressed, [&](const ogfx::gui::Event& event) -> void {
std::cout << showOpenFileDialog(filters) << "\n";
});
m_btn1.addThemeOverride("@.button.showIcon", true);
m_btn1.setAnimationData(ad);
m_btn1.setIcon("./img.png");
m_btn1.setIconSize({ 64, 64 });
m_btn1.enableAnimated();
m_label2.setText("Label2");
m_label3.setText("Label3");