Skip to content

Commit 96b40f1

Browse files
authored
Merge pull request #29 from kimkulling/feature/add_standard_dialogs
Featue: Add portable open- and save-file dialog.
2 parents f0fdce0 + ba3a7bf commit 96b40f1

File tree

8 files changed

+149
-32
lines changed

8 files changed

+149
-32
lines changed

samples/demo/main.cpp

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,7 @@ int updateProgressbar(Id, void *instance) {
5757
}
5858

5959
auto *widget = (Widget*) instance;
60-
auto *eventPayload = (EventPayload*) widget->mContent;
61-
if (eventPayload->type == EventDataType::FillState) {
60+
if (auto *eventPayload = (EventPayload *)widget->mContent; eventPayload->type == EventDataType::FillState) {
6261
FilledState *state = (FilledState*) (eventPayload->payload);
6362
uint32_t tick = TinyUi::getTicks();
6463
uint32_t diff = tick - LastTick;
@@ -77,13 +76,12 @@ int updateProgressbar(Id, void *instance) {
7776
}
7877

7978
int main(int argc, char *argv[]) {
80-
Style style = TinyUi::getDefaultStyle();
81-
if (!TinyUi::createContext("Sample-Screen", style)) {
79+
if (Style style = TinyUi::getDefaultStyle(); !TinyUi::createContext("Sample-Screen", style)) {
8280
return -1;
8381
}
8482

8583
if (TinyUi::initScreen(20, 20, 1024, 768) == -1) {
86-
auto &ctx = TinyUi::getContext();
84+
const auto &ctx = TinyUi::getContext();
8785
ctx.mLogger(LogSeverity::Error, "Cannot init screen");
8886
return ErrorCode;
8987
}

samples/hello_world/main.cpp

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@ using namespace tinyui;
2727

2828
static constexpr Id RootPanelId = 1;
2929

30-
static constexpr Id NextPanelId = 100;
31-
3230
int quit(Id, void *instance) {
3331
if (instance == nullptr) {
3432
return ErrorCode;
@@ -41,21 +39,20 @@ int quit(Id, void *instance) {
4139
}
4240

4341
int main(int argc, char *argv[]) {
44-
Style style = TinyUi::getDefaultStyle();
45-
if (!TinyUi::createContext("Sample-Screen", style)) {
42+
if (Style style = TinyUi::getDefaultStyle(); !TinyUi::createContext("Sample-Screen", style)) {
4643
return -1;
4744
}
4845

4946
if (TinyUi::initScreen(20, 20, 1024, 768) == -1) {
50-
auto &ctx = TinyUi::getContext();
47+
const auto &ctx = TinyUi::getContext();
5148
ctx.mLogger(LogSeverity::Error, "Cannot init screen");
5249
return ErrorCode;
5350
}
5451

5552
constexpr int32_t ButtonHeight = 20;
5653
Widgets::panel(RootPanelId, 0, "Sample-Dialog", Rect(90, 5, 220, 60), nullptr);
5754
auto &ctx = TinyUi::getContext();
58-
CallbackI *dynamicQuitCallback = new CallbackI(quit, (void*) &ctx);
55+
auto *dynamicQuitCallback = new CallbackI(quit, (void*) &ctx);
5956

6057
Widgets::label(2, RootPanelId, "Hi, World!", Rect(100, 10, 200, ButtonHeight), Alignment::Center);
6158
Widgets::button(3, RootPanelId, "Quit", Rect(100, 30, 200, ButtonHeight), dynamicQuitCallback);

src/backends/sdl2_renderer.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -186,8 +186,7 @@ ret_code Renderer::releaseRenderer(Context &ctx) {
186186
return ResultOk;
187187
}
188188

189-
SDLContext *sdlCtx = getBackendContext(ctx);
190-
if (sdlCtx->mRenderer != nullptr) {
189+
if (SDLContext *sdlCtx = getBackendContext(ctx); sdlCtx->mRenderer != nullptr) {
191190
SDL_DestroyRenderer(sdlCtx->mRenderer);
192191
sdlCtx->mRenderer = nullptr;
193192
}
@@ -276,6 +275,7 @@ ret_code Renderer::initScreen(Context &ctx, int32_t x, int32_t y, int32_t w, int
276275
SDLContext *sdlCtx = SDLContext::create();
277276
if (sdlCtx->mWindow != nullptr) {
278277
ctx.mLogger(LogSeverity::Error, "Already created.");
278+
sdlCtx->destroy();
279279
return ErrorCode;
280280
}
281281

@@ -448,7 +448,7 @@ ret_code Renderer::createRenderTexture(Context &ctx, int w, int h, SDL_Texture *
448448
return ResultOk;
449449
}
450450

451-
bool Renderer::update(Context &ctx) {
451+
bool Renderer::update(const Context &ctx) {
452452
if (!ctx.mCreated) {
453453
return false;
454454
}
@@ -513,7 +513,7 @@ SurfaceImpl *Renderer::createSurfaceImpl(unsigned char *data, int w, int h, int
513513
std::cerr << "*ERR*: %s\n" << errorMsg << "\n";
514514
return nullptr;
515515
}
516-
SurfaceImpl *surfaceImpl = new SurfaceImpl;
516+
auto *surfaceImpl = new SurfaceImpl;
517517
surfaceImpl->mSurface = surface;
518518

519519
return surfaceImpl;
@@ -527,7 +527,7 @@ void Renderer::releaseSurfaceImpl(SurfaceImpl *surfaceImpl) {
527527
}
528528

529529
ret_code Renderer::getSurfaceInfo(Context &ctx, int32_t &w, int32_t &h) {
530-
SDLContext *sdlCtx = (SDLContext *) ctx.mBackendCtx->mHandle;
530+
const auto *sdlCtx = (const SDLContext *) ctx.mBackendCtx->mHandle;
531531
if (sdlCtx->mSurface == nullptr) {
532532
return ErrorCode;
533533
}

src/backends/sdl2_renderer.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ struct Renderer {
119119
static ret_code endRender(Context &ctx);
120120
static ret_code createRenderTexture(Context &ctx, int w, int h, SDL_Texture **texture);
121121
static ret_code closeScreen(Context &ctx);
122-
static bool update(Context &ctx);
122+
static bool update(const Context &ctx);
123123
static SurfaceImpl *createSurfaceImpl(unsigned char *data, int w, int h, int bytesPerPixel, int pitch);
124124
static void releaseSurfaceImpl(SurfaceImpl *surfaceImpl);
125125
static ret_code getSurfaceInfo(Context &ctx, int32_t &w, int32_t &h);

src/tinyui.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ void log_message(LogSeverity severity, const char *message) {
5757
Context *gCtx = nullptr;
5858

5959
Context *Context::create(const char *title, const Style &style) {
60-
Context *ctx = new Context;
60+
auto *ctx = new Context;
6161
ctx->mLogger = log_message;
6262
ctx->mAppTitle = title;
6363
ctx->mWindowsTitle = title;
@@ -130,7 +130,7 @@ ret_code TinyUi::getSurfaceCenter(int32_t &x, int32_t &y) {
130130
}
131131

132132
bool TinyUi::run() {
133-
auto &ctx = getContext();
133+
const auto &ctx = getContext();
134134
if (!ctx.mUpdateCallbackList.empty()) {
135135
for (auto it = ctx.mUpdateCallbackList.begin(); it != ctx.mUpdateCallbackList.end(); ++it) {
136136
(*it)->mfuncCallback[Events::UpdateEvent](1, (*it)->mInstance);
@@ -150,7 +150,7 @@ ret_code TinyUi::endRender() {
150150
}
151151

152152
void TinyUi::render() {
153-
auto &ctx = getContext();
153+
const auto &ctx = getContext();
154154
beginRender(ctx.mStyle.mClearColor);
155155
Widgets::renderWidgets();
156156
endRender();

src/tinyui.h

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,24 @@ SOFTWARE.
6060
# define _CRT_SECURE_NO_WARNINGS
6161
#endif
6262

63+
#if defined(_WIN32) || defined(_WIN64)
64+
# define TINYUI_WINDOWS
65+
# ifndef WIN32_LEAN_AND_MEAN
66+
# define WIN32_LEAN_AND_MEAN // Minimal windows header
67+
# endif // WIN32_LEAN_AND_MEAN
68+
#elif defined(__gnu_linux__)
69+
# define TINYUI_GNU_LINUX
70+
#elif defined(__APPLE__) || defined(__MACH__)
71+
# error "Currently not supported platform"
72+
#elif defined(__ANDROID__)
73+
# define TINYUIE_ANDROID
74+
#endif
75+
76+
#ifdef TINYUI_WINDOWS
77+
# include <windows.h>
78+
# include <Commdlg.h>
79+
#endif
80+
6381
// Forward declarations -------------------------------------------------------
6482

6583
namespace tinyui {
@@ -78,6 +96,10 @@ using Id = uint64_t;
7896
/// @brief The return code type used in the ui library.
7997
using ret_code = int32_t;
8098

99+
/// @brief The operation was cancelled.
100+
static constexpr ret_code OpCancelled = -5;
101+
/// @brief The context is invalid.
102+
static constexpr ret_code InvalidContext = -4;
81103
/// @brief The invalid render handle return code.
82104
static constexpr ret_code InvalidRenderHandle = -3;
83105
/// @brief The invalid handle return code.
@@ -220,14 +242,12 @@ struct Rect {
220242
top.y = r.top.y;
221243
}
222244

223-
const int x2_ = top.x + r.width;
224-
if (bottom.x < x2_) {
245+
if (const int x2_ = top.x + r.width; bottom.x < x2_) {
225246
bottom.x = x2_;
226247
width = r.width;
227248
}
228249

229-
const int y2_ = bottom.y + r.height;
230-
if (bottom.y < y2_) {
250+
if (const int y2_ = bottom.y + r.height; bottom.y < y2_) {
231251
bottom.y = y2_;
232252
height = r.height;
233253
}

src/widgets.cpp

Lines changed: 96 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ static int inputHandler(Id id, void *instance) {
266266
return ErrorCode;
267267
}
268268

269-
Context *ctx = static_cast<Context *>(instance);
269+
auto *ctx = static_cast<Context *>(instance);
270270
ctx->mFocus = Widgets::findWidget(id, ctx->mRoot);
271271

272272
return ResultOk;
@@ -399,8 +399,7 @@ ret_code Widgets::panel(Id id, Id parentId, const char *title, const Rect &rect,
399399
return InvalidRenderHandle;
400400
}
401401

402-
Widget *child = createWidget(ctx, id, parentId, rect, WidgetType::Panel);
403-
if (child == nullptr) {
402+
if (const Widget *child = createWidget(ctx, id, parentId, rect, WidgetType::Panel); child == nullptr) {
404403
return ErrorCode;
405404
}
406405

@@ -412,14 +411,17 @@ static int onTreeViewItemClicked(Id id, void *data) {
412411
if (treeView == nullptr) {
413412
return ErrorCode;
414413
}
414+
415415
for (size_t i = 0; i < treeView->mChildren.size(); ++i ) {
416416
Widget *child = treeView->mChildren[i];
417417
if (child == nullptr) {
418418
continue;
419419
}
420420
child->mEnabled = !child->mEnabled;
421421
}
422+
422423
std::cout << "TreeView item clicked: " << id << std::endl;
424+
423425
return 0;
424426
}
425427

@@ -432,7 +434,7 @@ ret_code Widgets::treeView(Id id, Id parentId, const char *title, const Rect &re
432434
if (ctx.mRoot == nullptr) {
433435
return InvalidRenderHandle;
434436
}
435-
437+
436438
Widget *widget = createWidget(ctx, id, parentId, rect, WidgetType::TreeView);
437439
if (widget == nullptr) {
438440
return ErrorCode;
@@ -461,12 +463,12 @@ ret_code Widgets::treeItem(Id id, Id parentItemId, const char *text) {
461463
return InvalidRenderHandle;
462464
}
463465

464-
Widget *parentWidget = findWidget(parentItemId, ctx.mRoot);
466+
const Widget *parentWidget = findWidget(parentItemId, ctx.mRoot);
465467
if (parentWidget == nullptr) {
466468
return ErrorCode;
467469
}
468470

469-
auto &parentRect = parentWidget->mRect;
471+
const auto &parentRect = parentWidget->mRect;
470472

471473
const int32_t margin = ctx.mStyle.mMargin;
472474
const int32_t w = parentRect.width;
@@ -579,7 +581,7 @@ static void render(Context &ctx, const Widget *currentWidget) {
579581
if (payload == nullptr) {
580582
break;
581583
}
582-
FilledState *state = reinterpret_cast<FilledState *>(payload->payload);
584+
const auto *state = reinterpret_cast<FilledState *>(payload->payload);
583585
if (state == nullptr) {
584586
break;
585587
}
@@ -771,7 +773,7 @@ void Widgets::setEnableState(Id id, bool enabled) {
771773

772774
bool Widgets::isEnabled(Id id) {
773775
auto &ctx = TinyUi::getContext();
774-
Widget *widget = findWidget(id, ctx.mRoot);
776+
const Widget *widget = findWidget(id, ctx.mRoot);
775777
if (widget != nullptr) {
776778
return widget->isEnabled();
777779
}
@@ -806,4 +808,90 @@ bool Widgets::endChild() {
806808
return true;
807809
}
808810

811+
static constexpr size_t BufferSize = 1024;
812+
813+
ret_code Widgets::getOpenFileDialog(const char *title, const char *extensions, std::string &filename) {
814+
filename.clear();
815+
#ifdef TINYUI_WINDOWS
816+
// Init data
817+
char szFile[BufferSize] = { '\0' };
818+
OPENFILENAME ofn;
819+
memset(&ofn, 0, sizeof(ofn));
820+
ofn.lStructSize = sizeof(ofn);
821+
ofn.lpstrFile = szFile;
822+
823+
// Set lpstrFile[0] to '\0' so that GetOpenFileName does not
824+
// use the contents of szFile to initialize itself.
825+
ofn.lpstrTitle = title;
826+
ofn.lpstrFile[0] = '\0';
827+
ofn.nMaxFile = sizeof(szFile);
828+
ofn.lpstrFilter = extensions;
829+
ofn.nFilterIndex = 1;
830+
ofn.lpstrFileTitle = nullptr;
831+
ofn.nMaxFileTitle = 0;
832+
ofn.lpstrInitialDir = nullptr;
833+
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
834+
835+
// Display the Open dialog box.
836+
if (::GetOpenFileName(&ofn) == TRUE) {
837+
filename = ofn.lpstrFile;
838+
} else {
839+
return OpCancelled;
840+
}
841+
#else
842+
char buffer[BufferSize] = { '\0' };
843+
FILE *f = popen("zenity --file-selection", "r");
844+
if (f == nullptr) {
845+
return OpCancelled;
846+
}
847+
fgets(buffer, BufferSize, f);
848+
filename = buffer;
849+
#endif // TINYUI_WINDOWS
850+
851+
return ResultOk;
852+
}
853+
854+
ret_code Widgets::getSaveFileDialog(const char *title, const char *extensions, std::string &filename) {
855+
filename.clear();
856+
857+
#ifdef TINYUI_WINDOWS
858+
char szFile[BufferSize] = { '\0' };
859+
// Initialize OPENFILENAME
860+
OPENFILENAME ofn;
861+
memset(&ofn, 0, sizeof(ofn));
862+
ofn.lStructSize = sizeof(ofn);
863+
ofn.lpstrFile = szFile;
864+
865+
// Set lpstrFile[0] to '\0' so that GetOpenFileName does not
866+
// use the contents of szFile to initialize itself.
867+
ofn.lpstrTitle = title;
868+
ofn.lpstrFile[0] = '\0';
869+
ofn.nMaxFile = sizeof(szFile);
870+
ofn.lpstrFilter = extensions;
871+
ofn.nFilterIndex = 1;
872+
ofn.lpstrFileTitle = nullptr;
873+
ofn.nMaxFileTitle = 0;
874+
ofn.lpstrInitialDir = nullptr;
875+
ofn.Flags = OFN_PATHMUSTEXIST;
876+
877+
// Display the Open dialog box.
878+
if (TRUE == GetSaveFileName(&ofn)) {
879+
filename = ofn.lpstrFile;
880+
} else {
881+
return OpCancelled;
882+
}
883+
#else
884+
FILE *f = popen("zenity --file-selection", "w");
885+
if (f == nullptr) {
886+
return OpCancelled;
887+
}
888+
char buffer[BufferSize] = { '\0' };
889+
fgets(buffer, BufferSize, f);
890+
891+
filename = buffer;
892+
#endif // TINYUI_WINDOWS
893+
894+
return ResultOk;
895+
}
896+
809897
} // namespace tinyui

src/widgets.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,20 @@ struct Widgets {
337337
/// @brief Will close a child window.
338338
/// @return true if successful.
339339
static bool endChild();
340+
341+
/// @brief Will open a open-file-dialog.
342+
/// @param title The title of the dialog.
343+
/// @param extensions The allowed file extensions (e.g. "txt;pdf;docx").
344+
/// @param filename The selected filename.
345+
/// @return true if a file was selected, false if the dialog was canceled.
346+
static ret_code getOpenFileDialog(const char *title, const char *extensions, std::string &filename);
347+
348+
/// @brief Will open a save-file-dialog.
349+
/// @param title The title of the dialog.
350+
/// @param extensions The allowed file extensions (e.g. "txt;pdf;docx").
351+
/// @param filename The selected filename.
352+
/// @return true if a file was selected, false if the dialog was canceled.
353+
static ret_code getSaveFileDialog(const char *title, const char *extensions, std::string &filename);
340354
};
341355

342356
} // namespace tinyui

0 commit comments

Comments
 (0)