Added save/open file dialogs
This commit is contained in:
parent
8c174fb863
commit
83f4687198
8 changed files with 163 additions and 21 deletions
|
|
@ -160,17 +160,8 @@ window.backgroundColor = rgba(160, 160, 160, 255)
|
||||||
useBackgroundGradient = true
|
useBackgroundGradient = true
|
||||||
padding = rect(15, 8, 15, 8)
|
padding = rect(15, 8, 15, 8)
|
||||||
margin = rect(0, 0, 0, 0)
|
margin = rect(0, 0, 0, 0)
|
||||||
showIcon = true
|
showIcon = false
|
||||||
(icon) {
|
(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) {
|
(button:hover) {
|
||||||
|
|
|
||||||
|
|
@ -41,21 +41,21 @@ Implement following widgets:
|
||||||
***Tab Panel
|
***Tab Panel
|
||||||
***Button
|
***Button
|
||||||
***Image/Icon
|
***Image/Icon
|
||||||
|
***Save/Open File Dialogs (and folder dialogs)
|
||||||
Radio Button
|
Radio Button
|
||||||
Grouping
|
Grouping
|
||||||
Text Input
|
Progressbar
|
||||||
Horizontal Slider
|
Horizontal Slider
|
||||||
Vertical Slider
|
Vertical Slider
|
||||||
ListBox
|
ListBox
|
||||||
ComboBox
|
ComboBox
|
||||||
TreeView
|
Tooltips
|
||||||
Progressbar
|
|
||||||
Context Menu
|
Context Menu
|
||||||
Menu Bar
|
Menu Bar
|
||||||
Save/Open File Dialogs (and folder dialogs)
|
|
||||||
Hex Editor
|
|
||||||
Layouts
|
Layouts
|
||||||
Vertical
|
Vertical
|
||||||
Horizontal
|
Horizontal
|
||||||
Grid
|
Grid
|
||||||
Tooltips
|
Text Input
|
||||||
|
TreeView
|
||||||
|
Hex Editor
|
||||||
|
|
|
||||||
|
|
@ -369,6 +369,105 @@ namespace ogfx
|
||||||
__on_signal(signal);
|
__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)
|
MouseEventData WindowCore::get_mouse_state(SDL_Event& event)
|
||||||
{
|
{
|
||||||
f32 mx = 0, my = 0, _mx = 0, _my = 0;
|
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 };
|
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()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,11 @@ namespace ogfx
|
||||||
{
|
{
|
||||||
class WindowCore : public ostd::BaseObject
|
class WindowCore : public ostd::BaseObject
|
||||||
{
|
{
|
||||||
|
public: struct FileDialogFilter
|
||||||
|
{
|
||||||
|
String description { "" };
|
||||||
|
stdvec<String> extensionList;
|
||||||
|
};
|
||||||
public: enum class eCursor : u8
|
public: enum class eCursor : u8
|
||||||
{
|
{
|
||||||
Default = 0,
|
Default = 0,
|
||||||
|
|
@ -79,6 +84,7 @@ namespace ogfx
|
||||||
bool maximized { false };
|
bool maximized { false };
|
||||||
bool alwaysOnTop { false };
|
bool alwaysOnTop { false };
|
||||||
};
|
};
|
||||||
|
public: using FileDialogFilterList = stdvec<ogfx::WindowCore::FileDialogFilter>;
|
||||||
public:
|
public:
|
||||||
inline WindowCore(void) { }
|
inline WindowCore(void) { }
|
||||||
virtual ~WindowCore(void);
|
virtual ~WindowCore(void);
|
||||||
|
|
@ -99,7 +105,12 @@ namespace ogfx
|
||||||
void setBlockingEventsRefreshFPS(u32 fps);
|
void setBlockingEventsRefreshFPS(u32 fps);
|
||||||
void requestRedraw(void);
|
void requestRedraw(void);
|
||||||
void handleSignal(ostd::Signal& signal) override;
|
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 const ostd::Stylesheet* theme(void) const { return m_guiTheme; }
|
||||||
inline void loadDefaultTHeme(void) { setTheme(DefaultTheme); }
|
inline void loadDefaultTHeme(void) { setTheme(DefaultTheme); }
|
||||||
inline ostd::Stylesheet::VariableList& getDefaultStylesheetVariableList(void) { return m_defaultStylesheetVariables; }
|
inline ostd::Stylesheet::VariableList& getDefaultStylesheetVariableList(void) { return m_defaultStylesheetVariables; }
|
||||||
|
|
@ -142,6 +153,7 @@ namespace ogfx
|
||||||
private:
|
private:
|
||||||
void __handle_event(SDL_Event& event);
|
void __handle_event(SDL_Event& event);
|
||||||
void __load_default_stylesheet_variables(void);
|
void __load_default_stylesheet_variables(void);
|
||||||
|
void __store_file_dialog_filters(const FileDialogFilterList& filters) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
SDL_Window* m_window { nullptr };
|
SDL_Window* m_window { nullptr };
|
||||||
|
|
@ -191,6 +203,9 @@ namespace ogfx
|
||||||
SDL_Cursor* m_cursor_SW_Resize { nullptr };
|
SDL_Cursor* m_cursor_SW_Resize { nullptr };
|
||||||
SDL_Cursor* m_cursor_W_Resize { nullptr };
|
SDL_Cursor* m_cursor_W_Resize { nullptr };
|
||||||
|
|
||||||
|
mutable stdvec<String> m_patternStorage;
|
||||||
|
mutable stdvec<SDL_DialogFileFilter> m_sdlFilters;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
inline static constexpr i32 MaxBlockingEventsFPS { 240 };
|
inline static constexpr i32 MaxBlockingEventsFPS { 240 };
|
||||||
inline static constexpr i32 DefaultBlockingEventsFPS { 30 };
|
inline static constexpr i32 DefaultBlockingEventsFPS { 30 };
|
||||||
|
|
|
||||||
|
|
@ -43,11 +43,10 @@ namespace ogfx
|
||||||
|
|
||||||
void Button::onDraw(ogfx::BasicRenderer2D& gfx)
|
void Button::onDraw(ogfx::BasicRenderer2D& gfx)
|
||||||
{
|
{
|
||||||
// if (m_textChanged)
|
if (m_textChanged)
|
||||||
__update_size();
|
__update_size();
|
||||||
if (isIconEnabled())
|
if (isIconEnabled())
|
||||||
{
|
{
|
||||||
m_realIconSize = getIconSize().propy(getGlobalContentBounds().getSize().y);
|
|
||||||
|
|
||||||
if (isAnimatedEnabled())
|
if (isAnimatedEnabled())
|
||||||
gfx.drawAnimation(m_anim, getGlobalContentPosition(), m_realIconSize);
|
gfx.drawAnimation(m_anim, getGlobalContentPosition(), m_realIconSize);
|
||||||
|
|
@ -88,7 +87,7 @@ namespace ogfx
|
||||||
String filePath = getThemeValue<String>(theme, "icon.path", m_icon.getFilePath());
|
String filePath = getThemeValue<String>(theme, "icon.path", m_icon.getFilePath());
|
||||||
if (filePath != m_icon.getFilePath())
|
if (filePath != m_icon.getFilePath())
|
||||||
setIcon(filePath);
|
setIcon(filePath);
|
||||||
setIconSize(getThemeValue<Vec2>(theme, "icon.size", getSize()));
|
setIconSize(getThemeValue<Vec2>(theme, "icon.size", getIconSize()));
|
||||||
if (isAnimatedEnabled())
|
if (isAnimatedEnabled())
|
||||||
{
|
{
|
||||||
m_anim.create(m_animData);
|
m_anim.create(m_animData);
|
||||||
|
|
@ -99,6 +98,7 @@ namespace ogfx
|
||||||
|
|
||||||
void Button::__update_size(void)
|
void Button::__update_size(void)
|
||||||
{
|
{
|
||||||
|
m_realIconSize = getIconSize().propy(getGlobalContentBounds().getSize().y);
|
||||||
auto size = getWindow().getGFX().getStringDimensions(getText(), getFontSize());
|
auto size = getWindow().getGFX().getStringDimensions(getText(), getFontSize());
|
||||||
size.x += getPadding().left();
|
size.x += getPadding().left();
|
||||||
size.x += getPadding().right();
|
size.x += getPadding().right();
|
||||||
|
|
|
||||||
|
|
@ -41,9 +41,9 @@ namespace ogfx
|
||||||
void setText(const String& text);
|
void setText(const String& text);
|
||||||
void setIcon(const String& filePath);
|
void setIcon(const String& filePath);
|
||||||
void applyTheme(const ostd::Stylesheet& theme) override;
|
void applyTheme(const ostd::Stylesheet& theme) override;
|
||||||
OSTD_BOOL_PARAM_GETSET_E(Animated, m_animated);
|
|
||||||
inline String getText(void) const { return m_text; }
|
inline String getText(void) const { return m_text; }
|
||||||
inline Image& getIcon(void) { return m_icon; }
|
inline Image& getIcon(void) { return m_icon; }
|
||||||
|
OSTD_BOOL_PARAM_GETSET_E(Animated, m_animated);
|
||||||
OSTD_BOOL_PARAM_GETSET_E(Icon, m_showIcon);
|
OSTD_BOOL_PARAM_GETSET_E(Icon, m_showIcon);
|
||||||
OSTD_PARAM_GETSET(ostd::AnimationData, AnimationData, m_animData);
|
OSTD_PARAM_GETSET(ostd::AnimationData, AnimationData, m_animData);
|
||||||
OSTD_PARAM_GETSET(Vec2, IconSize, m_iconSize);
|
OSTD_PARAM_GETSET(Vec2, IconSize, m_iconSize);
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,7 @@ namespace ostd
|
||||||
inline String& clr(void) { m_data = ""; return *this; }
|
inline String& clr(void) { m_data = ""; return *this; }
|
||||||
inline String& set(const cpp_string& str) { m_data = str; 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 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 begin(void) { return m_data.begin(); }
|
||||||
inline auto end(void) { return m_data.end(); }
|
inline auto end(void) { return m_data.end(); }
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,11 @@
|
||||||
#include <ogfx/utils/Keycodes.hpp>
|
#include <ogfx/utils/Keycodes.hpp>
|
||||||
#include <ogfx/ogfx.hpp>
|
#include <ogfx/ogfx.hpp>
|
||||||
|
|
||||||
|
ogfx::WindowCore::FileDialogFilterList filters = {
|
||||||
|
{ "Image files", { "png", "jpg", "jpeg", "bmp" } },
|
||||||
|
{ "All files", { "*" } }
|
||||||
|
};
|
||||||
|
|
||||||
ostd::ConsoleOutputHandler out;
|
ostd::ConsoleOutputHandler out;
|
||||||
using namespace ogfx::gui::widgets;
|
using namespace ogfx::gui::widgets;
|
||||||
|
|
||||||
|
|
@ -69,8 +74,13 @@ class Window : public ogfx::gui::Window
|
||||||
|
|
||||||
m_btn1.setText("BUTTON");
|
m_btn1.setText("BUTTON");
|
||||||
m_btn1.setCallback(ogfx::gui::Widget::eCallback::MousePressed, [&](const ogfx::gui::Event& event) -> void {
|
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_label2.setText("Label2");
|
||||||
m_label3.setText("Label3");
|
m_label3.setText("Label3");
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue