@@ -25,10 +25,48 @@ START_NAMESPACE_DGL | |||||
class App; | class App; | ||||
class Widget; | class Widget; | ||||
class StandaloneWindow; | |||||
class Window | class Window | ||||
{ | { | ||||
public: | public: | ||||
/** | |||||
File browser options. | |||||
*/ | |||||
struct FileBrowserOptions { | |||||
const char* startDir; | |||||
const char* title; | |||||
uint width; | |||||
uint height; | |||||
/** | |||||
File browser buttons. | |||||
0 means hidden. | |||||
1 means visible and unchecked. | |||||
2 means visible and checked. | |||||
*/ | |||||
struct Buttons { | |||||
uint listAllFiles; | |||||
uint showHidden; | |||||
uint showPlaces; | |||||
/** Constuctor for default values */ | |||||
Buttons() | |||||
: listAllFiles(2), | |||||
showHidden(1), | |||||
showPlaces(1) {} | |||||
} buttons; | |||||
/** Constuctor for default values */ | |||||
FileBrowserOptions() | |||||
: startDir(nullptr), | |||||
title(nullptr), | |||||
width(0), | |||||
height(0), | |||||
buttons() {} | |||||
}; | |||||
explicit Window(App& app); | explicit Window(App& app); | ||||
explicit Window(App& app, Window& parent); | explicit Window(App& app, Window& parent); | ||||
explicit Window(App& app, intptr_t parentId); | explicit Window(App& app, intptr_t parentId); | ||||
@@ -42,6 +80,8 @@ public: | |||||
void focus(); | void focus(); | ||||
void repaint() noexcept; | void repaint() noexcept; | ||||
bool openFileBrowser(const FileBrowserOptions& options); | |||||
bool isVisible() const noexcept; | bool isVisible() const noexcept; | ||||
void setVisible(bool yesNo); | void setVisible(bool yesNo); | ||||
@@ -54,6 +94,7 @@ public: | |||||
void setSize(uint width, uint height); | void setSize(uint width, uint height); | ||||
void setSize(Size<uint> size); | void setSize(Size<uint> size); | ||||
const char* getTitle() const noexcept; | |||||
void setTitle(const char* title); | void setTitle(const char* title); | ||||
void setTransientWinId(uintptr_t winId); | void setTransientWinId(uintptr_t winId); | ||||
@@ -70,6 +111,8 @@ protected: | |||||
virtual void onReshape(uint width, uint height); | virtual void onReshape(uint width, uint height); | ||||
virtual void onClose(); | virtual void onClose(); | ||||
virtual void fileBrowserSelected(const char* filename); | |||||
private: | private: | ||||
struct PrivateData; | struct PrivateData; | ||||
PrivateData* const pData; | PrivateData* const pData; | ||||
@@ -20,6 +20,7 @@ | |||||
#include "AppPrivateData.hpp" | #include "AppPrivateData.hpp" | ||||
#include "../Widget.hpp" | #include "../Widget.hpp" | ||||
#include "../Window.hpp" | #include "../Window.hpp" | ||||
#include "../../distrho/extra/d_string.hpp" | |||||
#include "pugl/pugl.h" | #include "pugl/pugl.h" | ||||
@@ -69,6 +70,7 @@ struct Window::PrivateData { | |||||
fUsingEmbed(false), | fUsingEmbed(false), | ||||
fWidth(1), | fWidth(1), | ||||
fHeight(1), | fHeight(1), | ||||
fTitle(nullptr), | |||||
fWidgets(), | fWidgets(), | ||||
fModal(), | fModal(), | ||||
#if defined(DISTRHO_OS_WINDOWS) | #if defined(DISTRHO_OS_WINDOWS) | ||||
@@ -97,6 +99,7 @@ struct Window::PrivateData { | |||||
fUsingEmbed(false), | fUsingEmbed(false), | ||||
fWidth(1), | fWidth(1), | ||||
fHeight(1), | fHeight(1), | ||||
fTitle(nullptr), | |||||
fWidgets(), | fWidgets(), | ||||
fModal(parent.pData), | fModal(parent.pData), | ||||
#if defined(DISTRHO_OS_WINDOWS) | #if defined(DISTRHO_OS_WINDOWS) | ||||
@@ -135,6 +138,7 @@ struct Window::PrivateData { | |||||
fUsingEmbed(parentId != 0), | fUsingEmbed(parentId != 0), | ||||
fWidth(1), | fWidth(1), | ||||
fHeight(1), | fHeight(1), | ||||
fTitle(nullptr), | |||||
fWidgets(), | fWidgets(), | ||||
fModal(), | fModal(), | ||||
#if defined(DISTRHO_OS_WINDOWS) | #if defined(DISTRHO_OS_WINDOWS) | ||||
@@ -190,6 +194,7 @@ struct Window::PrivateData { | |||||
puglSetSpecialFunc(fView, onSpecialCallback); | puglSetSpecialFunc(fView, onSpecialCallback); | ||||
puglSetReshapeFunc(fView, onReshapeCallback); | puglSetReshapeFunc(fView, onReshapeCallback); | ||||
puglSetCloseFunc(fView, onCloseCallback); | puglSetCloseFunc(fView, onCloseCallback); | ||||
puglSetFileSelectedFunc(fView, fileBrowserSelectedCallback); | |||||
puglCreateWindow(fView, nullptr); | puglCreateWindow(fView, nullptr); | ||||
@@ -535,10 +540,22 @@ struct Window::PrivateData { | |||||
// ------------------------------------------------------------------- | // ------------------------------------------------------------------- | ||||
const char* getTitle() const noexcept | |||||
{ | |||||
static const char* const kFallback = ""; | |||||
return fTitle != nullptr ? fTitle : kFallback; | |||||
} | |||||
void setTitle(const char* const title) | void setTitle(const char* const title) | ||||
{ | { | ||||
DBGp("Window setTitle \"%s\"\n", title); | DBGp("Window setTitle \"%s\"\n", title); | ||||
if (fTitle != nullptr) | |||||
std::free(fTitle); | |||||
fTitle = strdup(title); | |||||
#if defined(DISTRHO_OS_WINDOWS) | #if defined(DISTRHO_OS_WINDOWS) | ||||
SetWindowTextA(hwnd, title); | SetWindowTextA(hwnd, title); | ||||
#elif defined(DISTRHO_OS_MAC) | #elif defined(DISTRHO_OS_MAC) | ||||
@@ -840,6 +857,7 @@ struct Window::PrivateData { | |||||
bool fUsingEmbed; | bool fUsingEmbed; | ||||
uint fWidth; | uint fWidth; | ||||
uint fHeight; | uint fHeight; | ||||
char* fTitle; | |||||
std::list<Widget*> fWidgets; | std::list<Widget*> fWidgets; | ||||
struct Modal { | struct Modal { | ||||
@@ -922,6 +940,11 @@ struct Window::PrivateData { | |||||
handlePtr->onPuglClose(); | handlePtr->onPuglClose(); | ||||
} | } | ||||
static void fileBrowserSelectedCallback(PuglView* view, const char* filename) | |||||
{ | |||||
handlePtr->fSelf->fileBrowserSelected(filename); | |||||
} | |||||
#undef handlePtr | #undef handlePtr | ||||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData) | DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData) | ||||
@@ -977,6 +1000,73 @@ void Window::repaint() noexcept | |||||
puglPostRedisplay(pData->fView); | puglPostRedisplay(pData->fView); | ||||
} | } | ||||
// static int fib_filter_filename_filter(const char* const name) | |||||
// { | |||||
// return 1; | |||||
// (void)name; | |||||
// } | |||||
bool Window::openFileBrowser(const FileBrowserOptions& options) | |||||
{ | |||||
using DISTRHO_NAMESPACE::d_string; | |||||
// -------------------------------------------------------------------------- | |||||
// configure start dir | |||||
// TODO: get abspath if needed | |||||
// TODO: cross-platform | |||||
d_string startDir(options.startDir); | |||||
if (startDir.isEmpty()) | |||||
{ | |||||
if (char* const dir_name = get_current_dir_name()) | |||||
{ | |||||
startDir = dir_name; | |||||
std::free(dir_name); | |||||
} | |||||
} | |||||
DISTRHO_SAFE_ASSERT_RETURN(startDir.isNotEmpty(), false); | |||||
if (! startDir.endsWith('/')) | |||||
startDir += "/"; | |||||
DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(0, startDir) == 0, false); | |||||
// -------------------------------------------------------------------------- | |||||
// configure title | |||||
d_string title(options.title); | |||||
if (title.isEmpty()) | |||||
{ | |||||
title = pData->getTitle(); | |||||
if (title.isEmpty()) | |||||
title = "FileBrowser"; | |||||
} | |||||
DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(1, title) == 0, false); | |||||
// -------------------------------------------------------------------------- | |||||
// configure filters | |||||
x_fib_cfg_filter_callback(nullptr); //fib_filter_filename_filter); | |||||
// -------------------------------------------------------------------------- | |||||
// configure buttons | |||||
x_fib_cfg_buttons(3, options.buttons.listAllFiles-1); | |||||
x_fib_cfg_buttons(1, options.buttons.showHidden-1); | |||||
x_fib_cfg_buttons(2, options.buttons.showPlaces-1); | |||||
// -------------------------------------------------------------------------- | |||||
// show | |||||
return (x_fib_show(pData->xDisplay, pData->xWindow, /*options.width*/0, /*options.height*/0) == 0); | |||||
} | |||||
bool Window::isVisible() const noexcept | bool Window::isVisible() const noexcept | ||||
{ | { | ||||
return pData->fVisible; | return pData->fVisible; | ||||
@@ -1022,6 +1112,11 @@ void Window::setSize(Size<uint> size) | |||||
pData->setSize(size.getWidth(), size.getHeight()); | pData->setSize(size.getWidth(), size.getHeight()); | ||||
} | } | ||||
const char* Window::getTitle() const noexcept | |||||
{ | |||||
return pData->getTitle(); | |||||
} | |||||
void Window::setTitle(const char* title) | void Window::setTitle(const char* title) | ||||
{ | { | ||||
pData->setTitle(title); | pData->setTitle(title); | ||||
@@ -1101,6 +1196,10 @@ void Window::onClose() | |||||
{ | { | ||||
} | } | ||||
void Window::fileBrowserSelected(const char*) | |||||
{ | |||||
} | |||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
END_NAMESPACE_DGL | END_NAMESPACE_DGL | ||||
@@ -219,6 +219,14 @@ typedef void (*PuglScrollFunc)(PuglView* view, int x, int y, float dx, float dy) | |||||
*/ | */ | ||||
typedef void (*PuglSpecialFunc)(PuglView* view, bool press, PuglKey key); | typedef void (*PuglSpecialFunc)(PuglView* view, bool press, PuglKey key); | ||||
/** | |||||
A function called when a filename is selected via file-browser. | |||||
@param view The view the event occured in. | |||||
@param filename The selected file name or NULL if the dialog was canceled. | |||||
*/ | |||||
typedef void (*PuglFileSelectedFunc)(PuglView* view, const char* filename); | |||||
/** | /** | ||||
Create a Pugl context. | Create a Pugl context. | ||||
@@ -352,6 +360,12 @@ puglSetSpecialFunc(PuglView* view, PuglSpecialFunc specialFunc); | |||||
PUGL_API void | PUGL_API void | ||||
puglSetReshapeFunc(PuglView* view, PuglReshapeFunc reshapeFunc); | puglSetReshapeFunc(PuglView* view, PuglReshapeFunc reshapeFunc); | ||||
/** | |||||
Set the function to call on file-browser selections. | |||||
*/ | |||||
PUGL_API void | |||||
puglSetFileSelectedFunc(PuglView* view, PuglFileSelectedFunc fileSelectedFunc); | |||||
/** | /** | ||||
Return the native window handle. | Return the native window handle. | ||||
*/ | */ | ||||
@@ -51,6 +51,7 @@ struct PuglViewImpl { | |||||
PuglReshapeFunc reshapeFunc; | PuglReshapeFunc reshapeFunc; | ||||
PuglScrollFunc scrollFunc; | PuglScrollFunc scrollFunc; | ||||
PuglSpecialFunc specialFunc; | PuglSpecialFunc specialFunc; | ||||
PuglFileSelectedFunc fileSelectedFunc; | |||||
PuglInternals* impl; | PuglInternals* impl; | ||||
PuglNativeWindow parent; | PuglNativeWindow parent; | ||||
@@ -200,3 +201,9 @@ puglSetSpecialFunc(PuglView* view, PuglSpecialFunc specialFunc) | |||||
{ | { | ||||
view->specialFunc = specialFunc; | view->specialFunc = specialFunc; | ||||
} | } | ||||
void | |||||
puglSetFileSelectedFunc(PuglView* view, PuglFileSelectedFunc fileSelectedFunc) | |||||
{ | |||||
view->fileSelectedFunc = fileSelectedFunc; | |||||
} |
@@ -32,6 +32,10 @@ | |||||
#include "pugl_internal.h" | #include "pugl_internal.h" | ||||
#define SOFD_HAVE_X11 | |||||
#include "../sofd/libsofd.h" | |||||
#include "../sofd/libsofd.c" | |||||
struct PuglInternalsImpl { | struct PuglInternalsImpl { | ||||
Display* display; | Display* display; | ||||
int screen; | int screen; | ||||
@@ -96,7 +100,7 @@ puglCreateWindow(PuglView* view, const char* title) | |||||
{ | { | ||||
PuglInternals* impl = view->impl; | PuglInternals* impl = view->impl; | ||||
impl->display = XOpenDisplay(0); | |||||
impl->display = XOpenDisplay(NULL); | |||||
impl->screen = DefaultScreen(impl->display); | impl->screen = DefaultScreen(impl->display); | ||||
impl->doubleBuffered = True; | impl->doubleBuffered = True; | ||||
@@ -322,6 +326,26 @@ puglProcessEvents(PuglView* view) | |||||
XEvent event; | XEvent event; | ||||
while (XPending(view->impl->display) > 0) { | while (XPending(view->impl->display) > 0) { | ||||
XNextEvent(view->impl->display, &event); | XNextEvent(view->impl->display, &event); | ||||
if (x_fib_handle_events(view->impl->display, &event)) { | |||||
const int status = x_fib_status(); | |||||
if (status > 0) { | |||||
char* const filename = x_fib_filename(); | |||||
x_fib_close(view->impl->display); | |||||
if (view->fileSelectedFunc) { | |||||
view->fileSelectedFunc(view, filename); | |||||
} | |||||
free(filename); | |||||
} else if (status < 0) { | |||||
x_fib_close(view->impl->display); | |||||
if (view->fileSelectedFunc) { | |||||
view->fileSelectedFunc(view, NULL); | |||||
} | |||||
} | |||||
break; | |||||
} | |||||
switch (event.type) { | switch (event.type) { | ||||
case MapNotify: | case MapNotify: | ||||
puglReshape(view, view->width, view->height); | puglReshape(view, view->width, view->height); | ||||