├── .clang_complete ├── .gitignore ├── .gitmodules ├── LICENSE.txt ├── README.md ├── premake5.lua ├── screenshot ├── gtk_color_picker.png ├── gtk_file_open.png ├── gtk_file_save.png ├── gtk_message_0.png ├── gtk_message_1.png ├── osx_color_picker.png ├── osx_file_open.png ├── osx_file_save.png ├── osx_message_0.png ├── osx_message_1.png ├── osx_with_glfw.png ├── win_color_picker.png ├── win_file_open.png ├── win_file_open_dir.png ├── win_file_save.png ├── win_message_0.png └── win_message_1.png ├── src ├── ColorPickerDialog.h ├── FileDialog.h ├── MessageDialog.h ├── NativeDialog.cpp ├── NativeDialog.h ├── gtk │ ├── ColorPickerDialog-GTK.cpp │ ├── FileDialog-GTK.cpp │ └── MessageDialog-GTK.cpp ├── osx │ ├── ColorPickerDialog-OSX.mm │ ├── FileDialog-OSX.mm │ └── MessageDialog-OSX.mm └── win │ ├── ColorPickerDialog.cpp │ ├── FileDialog-Windows.cpp │ └── MessageDialog.cpp ├── test.cc └── test.osx.mm /.clang_complete: -------------------------------------------------------------------------------- 1 | --std=c++11 2 | -Isrc 3 | -I/usr/include/gtk-3.0 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .build 2 | *.obj 3 | *.o 4 | *.a 5 | *.lib 6 | *.dll 7 | *.so 8 | *.os 9 | *.dylib -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "src/win/bubble"] 2 | path = src/win/bubble 3 | url = git@github.com:r-lyeh/bubble.git 4 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Geequlim 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Native Dialogs 2 | ----------------- 3 | **Use native dialogs with same code on Windows,Mac OSX and Linux(GTK3 required).** 4 | 5 | ## Features: 6 | * C++11 support and required 7 | * Easy to use 8 | * Native user interface 9 | * Native message dialog 10 | * Native file chooser dialog 11 | * Native color picker dialog 12 | 13 | ## Integrate to your project 14 | * Add all files under src to your project 15 | * inclue NativeDialog.h file to your code 16 | * Remove code under osx if it's not a OSX application 17 | * Remove code under win if it's not a Windows application 18 | 19 | ## Usage: 20 | 21 | ### Message Dialog 22 | 23 | * Show native notice dialog with one line 24 | 25 | ```c++ 26 | MessageDialog("Notice","Hello World!",{"Close"}).show(); 27 | ``` 28 | 29 | Platform|Screenshot 30 | ---|--- 31 | Windows|![](screenshot/win_message_0.png) 32 | GTK|![](screenshot/gtk_message_0.png) 33 | OS X|![](screenshot/osx_message_0.png) 34 | 35 | * Deal events as you like 36 | 37 | ```c++ 38 | MessageDialog mdlg("Notice","Do you want to quit?",{"Yes","No"}); 39 | mdlg.setDecideHandler([](const Dialog& dlg){ 40 | auto mdlg = dynamic_cast(dlg); 41 | if( mdlg.responseButtonTitle() == "Yes" ){ 42 | // He want to quit 43 | } 44 | else if(mdlg.responseButtonIndex() == 1) { 45 | // He is not sure to quit 46 | } 47 | }) 48 | .show(); 49 | ``` 50 | Platform|Screenshot 51 | ---|--- 52 | Windows|![](screenshot/win_message_1.png) 53 | GTK|![](screenshot/gtk_message_1.png) 54 | OS X|![](screenshot/osx_message_1.png) 55 | 56 | ### File Chooser Dialog 57 | 58 | * File selection 59 | * Directory selection 60 | * Multiselection 61 | * Save file mode 62 | * Filters are easy to set 63 | 64 | ```c++ 65 | FileDialog fdlg("Open some text files", 66 | FileDialog::SELECT_FILE|FileDialog::MULTI_SELECT); 67 | fdlg.setDefaultPath("..") 68 | .addFilter("Text Files","txt") 69 | .addFilter("Code files","c;cpp;h;hpp") 70 | .addFilter("All files","*") 71 | //.setSaveMode(true) // Set save mode 72 | .setDecideHandler([](const Dialog& dlg){ 73 | auto fdlg = dynamic_cast(dlg); 74 | const std::vector& fileList = fdlg.selectedPathes(); 75 | // All pathes are in absolute format 76 | }) 77 | .setCancelHandler( [](const Dialog& dlg){ 78 | // Nothing selected as it was canceled 79 | } 80 | ) 81 | .show(); 82 | ``` 83 | Platform|Screenshot 84 | ---|--- 85 | Windows|![](screenshot/win_file_open.png) 86 | GTK|![](screenshot/gtk_file_open.png) 87 | OS X|![](screenshot/osx_file_open.png) 88 | 89 | ### Color Picker Dialog 90 | 91 | No more thing but you want 92 | 93 | ```c++ 94 | ColorPickerDialog cdlg("Pick a color you like"); 95 | cdlg.setColor({1,0,1,1}) // Set default selected color 96 | .setDecideHandler( [](const Dialog& dlg){ 97 | auto colorDlg = dynamic_cast(dlg); 98 | auto color = colorDlg.color(); 99 | // Get color value with color.r,color.g,color.b and color.a 100 | }) 101 | .show(); 102 | ``` 103 | 104 | Platform|Screenshot 105 | ---|--- 106 | Windows|![](screenshot/win_color_picker.png) 107 | GTK|![](screenshot/gtk_color_picker.png) 108 | OS X|![](screenshot/osx_color_picker.png) 109 | 110 | #### More about usage : [test.cc](test.cc) 111 | 112 | ### Notice for platform specifications 113 | 114 | * Color Picker Dialog won't pause your thread on Mac OSX 115 | * File Dialogs support set host window with a `NSWindow*` value by `setHostWindow` method on Mac OSX 116 | * Filters of File Dialog are invisible but it works on Mac OSX 117 | * Select Multi-Directory is not allowed on Windows and GTK 118 | * Windows has a diffrent UI for directory selection like this 119 | 120 | ![](screenshot/win_file_open_dir.png) 121 | 122 | 123 | --- 124 | 125 | Here is a demo that use a glfw window as host window for Mac OSX : [test.osx.mm](test.osx.mm) 126 | 127 | ![](screenshot/osx_with_glfw.png) 128 | 129 | #### TODO: 130 | 131 | * Native font select dialog 132 | 133 | ## LICENSE 134 | Under [MIT LICENSE](LICENSE.txt) . 135 | -------------------------------------------------------------------------------- /premake5.lua: -------------------------------------------------------------------------------- 1 | solution 'NativeDialog' 2 | location ( '.build' ) 3 | flags {'C++11'} 4 | configurations { 'Debug', 'Release' } 5 | filter 'configurations:Debug' 6 | defines { 'DEBUG' } 7 | flags { 'Symbols' } 8 | filter 'configurations:Release' 9 | defines { 'NDEBUG' } 10 | optimize 'On' 11 | filter {} 12 | 13 | project 'Test' 14 | kind 'ConsoleApp' 15 | language 'C++' 16 | targetdir('.build/%{cfg.buildcfg}') 17 | includedirs {'src'} 18 | files { 'test.cc','src/**' } 19 | removefiles {"src/**.mm"} 20 | removefiles {'src/win/bubble/*'} 21 | if os.get() == 'linux' then 22 | buildoptions {'`pkg-config --cflags gtk+-3.0 glib-2.0`'} 23 | links {'gtk-3','glib-2.0','gobject-2.0'} 24 | elseif os.get() == 'macosx' then 25 | links {'Cocoa.framework'} 26 | files { 'src/osx/**' } 27 | elseif os.get() == 'windows' then 28 | files {'src/win/bubble/bubble.*pp'} 29 | end 30 | -------------------------------------------------------------------------------- /screenshot/gtk_color_picker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Geequlim/NativeDialogs/ca0cb49ce4d08f20cb6ff72c691cf427d655e0aa/screenshot/gtk_color_picker.png -------------------------------------------------------------------------------- /screenshot/gtk_file_open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Geequlim/NativeDialogs/ca0cb49ce4d08f20cb6ff72c691cf427d655e0aa/screenshot/gtk_file_open.png -------------------------------------------------------------------------------- /screenshot/gtk_file_save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Geequlim/NativeDialogs/ca0cb49ce4d08f20cb6ff72c691cf427d655e0aa/screenshot/gtk_file_save.png -------------------------------------------------------------------------------- /screenshot/gtk_message_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Geequlim/NativeDialogs/ca0cb49ce4d08f20cb6ff72c691cf427d655e0aa/screenshot/gtk_message_0.png -------------------------------------------------------------------------------- /screenshot/gtk_message_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Geequlim/NativeDialogs/ca0cb49ce4d08f20cb6ff72c691cf427d655e0aa/screenshot/gtk_message_1.png -------------------------------------------------------------------------------- /screenshot/osx_color_picker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Geequlim/NativeDialogs/ca0cb49ce4d08f20cb6ff72c691cf427d655e0aa/screenshot/osx_color_picker.png -------------------------------------------------------------------------------- /screenshot/osx_file_open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Geequlim/NativeDialogs/ca0cb49ce4d08f20cb6ff72c691cf427d655e0aa/screenshot/osx_file_open.png -------------------------------------------------------------------------------- /screenshot/osx_file_save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Geequlim/NativeDialogs/ca0cb49ce4d08f20cb6ff72c691cf427d655e0aa/screenshot/osx_file_save.png -------------------------------------------------------------------------------- /screenshot/osx_message_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Geequlim/NativeDialogs/ca0cb49ce4d08f20cb6ff72c691cf427d655e0aa/screenshot/osx_message_0.png -------------------------------------------------------------------------------- /screenshot/osx_message_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Geequlim/NativeDialogs/ca0cb49ce4d08f20cb6ff72c691cf427d655e0aa/screenshot/osx_message_1.png -------------------------------------------------------------------------------- /screenshot/osx_with_glfw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Geequlim/NativeDialogs/ca0cb49ce4d08f20cb6ff72c691cf427d655e0aa/screenshot/osx_with_glfw.png -------------------------------------------------------------------------------- /screenshot/win_color_picker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Geequlim/NativeDialogs/ca0cb49ce4d08f20cb6ff72c691cf427d655e0aa/screenshot/win_color_picker.png -------------------------------------------------------------------------------- /screenshot/win_file_open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Geequlim/NativeDialogs/ca0cb49ce4d08f20cb6ff72c691cf427d655e0aa/screenshot/win_file_open.png -------------------------------------------------------------------------------- /screenshot/win_file_open_dir.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Geequlim/NativeDialogs/ca0cb49ce4d08f20cb6ff72c691cf427d655e0aa/screenshot/win_file_open_dir.png -------------------------------------------------------------------------------- /screenshot/win_file_save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Geequlim/NativeDialogs/ca0cb49ce4d08f20cb6ff72c691cf427d655e0aa/screenshot/win_file_save.png -------------------------------------------------------------------------------- /screenshot/win_message_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Geequlim/NativeDialogs/ca0cb49ce4d08f20cb6ff72c691cf427d655e0aa/screenshot/win_message_0.png -------------------------------------------------------------------------------- /screenshot/win_message_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Geequlim/NativeDialogs/ca0cb49ce4d08f20cb6ff72c691cf427d655e0aa/screenshot/win_message_1.png -------------------------------------------------------------------------------- /src/ColorPickerDialog.h: -------------------------------------------------------------------------------- 1 | #ifndef __ND_ColorPicker_Dialog_H__ 2 | #define __ND_ColorPicker_Dialog_H__ 3 | 4 | namespace NativeDialog 5 | { 6 | /// Color Chooser Dialog 7 | class ColorPickerDialog : public Dialog 8 | { 9 | using super = Dialog; 10 | public: 11 | 12 | /// Color structure all compnents' value is in range [0,1] 13 | struct Color 14 | { 15 | double r,g,b,a; 16 | }; 17 | 18 | /*! 19 | @brief Construct a color picker dialog 20 | @param title The dialog title 21 | */ 22 | ColorPickerDialog(const string& title) 23 | { 24 | m_title = title; 25 | } 26 | ~ColorPickerDialog(); 27 | 28 | /// Show the color chooser dialog 29 | virtual void show() override; 30 | 31 | /// Set selected color of the dialog 32 | inline ColorPickerDialog& setColor( const Color& color ) 33 | { 34 | m_color = color; 35 | return *this; 36 | } 37 | 38 | /// Get selected color of the dialog 39 | inline const Color& color()const{ return m_color;} 40 | 41 | private: 42 | /// Selected color 43 | Color m_color; 44 | }; 45 | } 46 | 47 | #endif //__ND_ColorPicker_Dialog_H__ 48 | -------------------------------------------------------------------------------- /src/FileDialog.h: -------------------------------------------------------------------------------- 1 | #ifndef __ND_FileDialog_H__ 2 | #define __ND_FileDialog_H__ 3 | 4 | namespace NativeDialog 5 | { 6 | /*! 7 | @brief File dialog to select file pathes both open and save mode are supported 8 | @par Select file(s) and directory(directories) 9 | @par Selection filters are supported 10 | @par Platforms: Windows, Mac OSX and *inux(GTK+3.0 is required) 11 | @attention Select both file and directory mode is not supported on Linux if you set to this mode the dialog only allows folder selection 12 | @attention Select both file and directory mode is NOT full supported on windows for select multiple file and dirctory mode 13 | @note The @b decideHandler whould be called when selection confirmed or call the @b cancelHandler when selection canceled 14 | @see Dialog::EventHandler 15 | */ 16 | class FileDialog : public Dialog 17 | { 18 | using super = Dialog; 19 | 20 | public: 21 | /// Dialog selection mode 22 | enum FileDialogMode : unsigned int 23 | { 24 | /// Select file mode 25 | SELECT_FILE = 1U, 26 | /// Select directory mode 27 | SELECT_DIR = 2U, 28 | /// Select file and directory mode 29 | SELECT_FILE_DIR = SELECT_FILE | SELECT_DIR , 30 | /// Allows multi selection , Windows does not support multiselect directorys 31 | MULTI_SELECT = 4U, 32 | /// Select save file mode 33 | SELECT_TO_SAVE = 8U 34 | }; 35 | 36 | ///Default constructor 37 | FileDialog() 38 | { 39 | m_eventOwner = nullptr; 40 | m_pHostWnd = nullptr; 41 | cleanUp(); 42 | } 43 | 44 | /*! 45 | @brief Construct a file selection dialog 46 | @param title The title text of the dialog 47 | @param mode Selection mode 48 | @param eventOwner The event owner pointer 49 | @param pHostWnd The host window pointer 50 | */ 51 | FileDialog(const string& title,int mode, 52 | void* eventOwner = nullptr, 53 | void* pHostWnd = nullptr) 54 | { 55 | m_eventOwner = eventOwner; 56 | m_pHostWnd = pHostWnd; 57 | m_mode = mode; 58 | m_title = title; 59 | } 60 | 61 | ~FileDialog() 62 | { 63 | m_eventOwner = nullptr; 64 | m_pHostWnd = nullptr; 65 | } 66 | 67 | /// Clean up all configrations and options 68 | inline FileDialog& cleanUp() 69 | { 70 | m_mode = SELECT_FILE; 71 | m_title = nullstr; 72 | m_defaultPath = nullstr; 73 | setEventOwner(nullptr); 74 | setHostWindow(nullptr); 75 | m_filter.clear(); 76 | return *this; 77 | } 78 | 79 | /// Show dialog 80 | virtual void show() override; 81 | 82 | /*! 83 | @brief Check if is saving file mode 84 | @return Is save mode 85 | */ 86 | inline bool saveMode()const{ return m_mode & SELECT_TO_SAVE; } 87 | 88 | /*! 89 | @brief Set to if is saving file mode 90 | @param saveMode Is save mode 91 | */ 92 | inline FileDialog& setSaveMode( bool saveMode) 93 | { 94 | if(saveMode) 95 | m_mode |= SELECT_TO_SAVE; 96 | else 97 | m_mode &= ~SELECT_TO_SAVE; 98 | return *this; 99 | } 100 | 101 | /*! 102 | @brief Check allows to multi-selection 103 | @return Allows multi-selection 104 | */ 105 | inline bool allowsMultipleSelection()const{ return m_mode & MULTI_SELECT; } 106 | 107 | /*! 108 | @brief Set to if is allowed to multi-selection 109 | @param allowded Allows multi-selection 110 | */ 111 | inline FileDialog& setAllowsMultipleSelection(bool allowded) 112 | { 113 | if(allowded) 114 | m_mode |= MULTI_SELECT; 115 | else 116 | m_mode &= ~MULTI_SELECT; 117 | return *this; 118 | } 119 | 120 | 121 | /*! 122 | @brief Check if allows to select file(s) 123 | @return Select file(s) allowded 124 | */ 125 | inline bool allowsFileSelection()const { return m_mode & SELECT_FILE; } 126 | 127 | /*! 128 | @brief Set if is allowed to select file(s) 129 | @param allowed Allows select file 130 | */ 131 | inline FileDialog& setAllowsFileSelection(bool allowed) 132 | { 133 | if(allowed) 134 | m_mode |= SELECT_FILE; 135 | else 136 | m_mode &= ~SELECT_FILE; 137 | return *this; 138 | } 139 | 140 | /*! 141 | @brief Check if allows to select directory(directories) 142 | @return Allowed to select directory 143 | */ 144 | inline bool allowsDirectorySelection()const { return m_mode & SELECT_DIR; } 145 | 146 | /*! 147 | @brief Set if is allowed to select directory(directories) 148 | @param allowded Allowded to select directory 149 | */ 150 | inline FileDialog& setAllowsDirectorySelection(bool allowded ) 151 | { 152 | if(allowded) 153 | m_mode |= SELECT_DIR; 154 | else 155 | m_mode &= ~SELECT_DIR; 156 | return *this; 157 | } 158 | 159 | /*! 160 | @brief Set default selected path 161 | @param initDir The default selected path(absolute path) 162 | */ 163 | inline FileDialog& setDefaultPath(const string& initDir) 164 | { 165 | m_defaultPath = initDir; 166 | return *this; 167 | } 168 | 169 | /*! 170 | @brief Get default selected path 171 | @return The default selected path(absolute path) 172 | */ 173 | inline const string& defaultPath()const { return m_defaultPath;} 174 | 175 | /*! 176 | @brief Add selection filter 177 | @param label Filter name. Invisiable on Mac OSX 178 | @param types Fileter's extention name. 179 | @n Does not contains '.' 180 | @n '*' to allow all files.for more extentions,seperate with ';' e.g “txt;TXT” to allow select files name ends with '.txt' and '.TXT' 181 | @attention: 182 | @li It is not allowed to use '*' to select all files on Mac OSX. 183 | @li If you have to select all files just leave the filter empty for all platform. 184 | */ 185 | inline FileDialog& addFilter(const string & label ,const string &types) 186 | { 187 | m_filter.push_back(std::pair(label,types)); 188 | return *this; 189 | } 190 | 191 | /// Clear filters that means allow to select all kind of files 192 | inline FileDialog& clearFilters() 193 | { 194 | m_filter.clear(); 195 | return *this; 196 | } 197 | 198 | /*! 199 | @brief Get selected pathes 200 | @return The vector contains all selected pathes( abusolute pathes ) 201 | */ 202 | inline const vector& selectedPathes()const{ return m_selectedPathes;} 203 | 204 | /*! 205 | @brief Get the read only dialog's event owner 206 | @return The dialog event owner 207 | */ 208 | inline const void* eventOwner()const{ return m_eventOwner; } 209 | 210 | /*! 211 | @brief Get the writable dialog's event owner 212 | @return The dialog event owner 213 | */ 214 | inline void* eventOwner(){ return m_eventOwner;} 215 | 216 | /*! 217 | @brief Set the dialog's event owner 218 | @param obj The event owner object 219 | */ 220 | inline FileDialog& setEventOwner(void* obj) 221 | { 222 | m_eventOwner = obj; 223 | return *this; 224 | } 225 | 226 | /*! 227 | @brief Get the read only host window of the dialog 228 | @return The host window 229 | */ 230 | inline const void* hostWindow()const{ return m_pHostWnd; } 231 | 232 | /*! 233 | @brief Get the writable host window of the dialog 234 | @return The host window 235 | */ 236 | inline void* hostWindow(){return m_pHostWnd;} 237 | 238 | /*! 239 | @brief Set the host window of the dialog 240 | @param pWnd The window to host the dialog 241 | */ 242 | inline FileDialog& setHostWindow(void* pWnd) 243 | { 244 | m_pHostWnd = pWnd; 245 | return *this; 246 | } 247 | 248 | /** 249 | * @brief Set dialog mode 250 | * 251 | * @param mode The mode e.g (SELECT_FILE | SELECT_DIR | MULTI_SELECT) allows to select files and dirctories 252 | */ 253 | inline FileDialog& setMode(unsigned int mode) 254 | { 255 | m_mode = mode; 256 | return *this; 257 | } 258 | 259 | /*! 260 | @brief Get the dialog selection mode 261 | @return The selection mode 262 | */ 263 | inline unsigned int mode()const{ return m_mode;} 264 | 265 | protected: 266 | /// Default selected path 267 | string m_defaultPath; 268 | /// Dialog select mode 269 | unsigned m_mode; 270 | /// Filters for select files 271 | vector > m_filter; 272 | /// Selected pathes (absolute pathes) 273 | std::vector m_selectedPathes; 274 | /// The event owner 275 | void * m_eventOwner; 276 | /// Dialog's host window 277 | void * m_pHostWnd; 278 | }; 279 | } 280 | 281 | #endif //__ND_FileDialog_H__ 282 | -------------------------------------------------------------------------------- /src/MessageDialog.h: -------------------------------------------------------------------------------- 1 | #ifndef __ND_NativeMessageDialog_H__ 2 | #define __ND_NativeMessageDialog_H__ 3 | 4 | namespace NativeDialog 5 | { 6 | /// Meesage dialog with native user interfaces 7 | class MessageDialog : public Dialog 8 | { 9 | using super = Dialog; 10 | public: 11 | 12 | /*! 13 | @brief Construct a message dialog 14 | @param title The dialog title 15 | @param message The message content 16 | @param buttons Button titles 17 | */ 18 | MessageDialog( const string& title, 19 | const string& message, 20 | const vector& buttons) 21 | { 22 | this->m_title = title; 23 | m_message = message; 24 | m_buttons = buttons; 25 | } 26 | 27 | ~MessageDialog(){} 28 | 29 | /// Get button titles 30 | inline const vector& buttons()const{ return m_buttons; } 31 | 32 | /// Get writable button titles 33 | inline vector& buttons(){ return m_buttons; } 34 | 35 | /// Set button titles 36 | inline MessageDialog& setButtons(const vector& buttons) 37 | { 38 | m_buttons = buttons; 39 | return *this; 40 | } 41 | 42 | /// Get message content 43 | inline const string& message()const { return m_message; } 44 | 45 | /// Set message content 46 | inline MessageDialog& setMessage(const string& msg) 47 | { 48 | m_message = msg; 49 | return *this; 50 | } 51 | 52 | /// Show the dialog 53 | virtual void show() override; 54 | 55 | /// Get the response button title 56 | inline const string& responseButtonTitle()const 57 | { 58 | const string* buttonTitle = &nullstr; 59 | if( m_responseIndex>=0 && m_responseIndex< int(m_buttons.size()) ) 60 | buttonTitle = &m_buttons.at(m_responseIndex); 61 | return *buttonTitle; 62 | } 63 | 64 | /// Get the response button index starts with 0 65 | inline int responseButtonIndex()const { return m_responseIndex; } 66 | 67 | private: 68 | /// The resonse button index 69 | int m_responseIndex; 70 | 71 | /// Message content 72 | string m_message; 73 | 74 | /// Buttons' title 75 | vector m_buttons; 76 | }; 77 | 78 | } 79 | #endif //__ND_NativeMessageDialog_H__ 80 | -------------------------------------------------------------------------------- /src/NativeDialog.cpp: -------------------------------------------------------------------------------- 1 | #include "NativeDialog.h" 2 | #ifdef ND_PLATFORM_GTK 3 | #include 4 | #endif 5 | #ifdef ND_PLATFORM_WIN 6 | #include 7 | #include 8 | #if defined(_M_IX86) 9 | #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"") 10 | #elif defined(_M_IA64) 11 | #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='ia64' publicKeyToken='6595b64144ccf1df' language='*'\"") 12 | #elif defined(_M_X64) 13 | #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"") 14 | #else 15 | #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") 16 | #endif 17 | #endif 18 | 19 | namespace NativeDialog 20 | { 21 | /// The event handler do nothing 22 | const Dialog::EventHandler Dialog::nullHandler = [](const Dialog&){}; 23 | 24 | #ifdef ND_PLATFORM_GTK 25 | // Iterate GTK events 26 | void iterateGTKEvents() 27 | { 28 | while (gtk_events_pending()) 29 | gtk_main_iteration(); 30 | } 31 | #endif 32 | 33 | namespace String 34 | { 35 | // split string to vector 36 | vector split(const string & soueceStr,const string& pattern) 37 | { 38 | string::size_type pos; 39 | std::vector result; 40 | //expend for convenience 41 | string str( soueceStr + pattern); 42 | string::size_type size =str.size(); 43 | for( string::size_type i=0; i unicode(len); 65 | MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), -1, &unicode[0], len); 66 | return wstring(&unicode[0]); 67 | } 68 | 69 | string wstring2string(const wstring& wideStr) 70 | { 71 | int len = WideCharToMultiByte(CP_UTF8, 0, wideStr.c_str(), -1, NULL, 0, NULL, NULL); 72 | if (len == 0) 73 | return nullstr; 74 | vector utf8(len); 75 | WideCharToMultiByte(CP_UTF8, 0, wideStr.c_str(), -1, &utf8[0], len, NULL, NULL); 76 | return string(&utf8[0]); 77 | } 78 | 79 | wstring multibyteString2wstring(const string& multibyteStr) 80 | { 81 | int len = MultiByteToWideChar(CP_ACP, 0, multibyteStr.c_str(), -1, NULL, 0); 82 | if (len == 0) 83 | return nullwstr; 84 | vector unicode(len); 85 | MultiByteToWideChar(CP_ACP, 0, multibyteStr.c_str(), -1, &unicode[0], len); 86 | return wstring(&unicode[0]); 87 | } 88 | 89 | string wstring2multibyteString(const wstring& wideStr) 90 | { 91 | int len = WideCharToMultiByte(CP_ACP, 0, wideStr.c_str(), -1, NULL, 0, NULL, NULL); 92 | if (len == 0) 93 | return nullstr; 94 | vector utf8(len); 95 | WideCharToMultiByte(CP_ACP, 0, wideStr.c_str(), -1, &utf8[0], len, NULL, NULL); 96 | return string(&utf8[0]); 97 | } 98 | #endif 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/NativeDialog.h: -------------------------------------------------------------------------------- 1 | #ifndef __ND_DIALOGS_H__ 2 | #define __ND_DIALOGS_H__ 3 | 4 | #if _WIN64 || _WIN32 5 | #define ND_PLATFORM_WIN 6 | #define UNICODE 7 | #define _UNICODE 8 | #elif __APPLE__ 9 | #include "TargetConditionals.h" 10 | //iOS Simulator 11 | #if !TARGET_OS_IPHONE 12 | #define ND_PLATFORM_OSX 13 | #endif 14 | #else 15 | #define ND_PLATFORM_GTK 16 | namespace NativeDialog 17 | { 18 | /// Iterate GTK events 19 | void iterateGTKEvents(); 20 | } 21 | #endif 22 | 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | namespace NativeDialog 29 | { 30 | using std::string; 31 | using std::vector; 32 | /// The empty string definition 33 | static string nullstr; 34 | 35 | /** 36 | * @class Dialog 37 | * @brief The abstract dialog class for native dialogs 38 | */ 39 | class Dialog 40 | { 41 | /// Event handler type definition 42 | using EventHandler = std::function; 43 | public: 44 | /// The event handler do nothing 45 | static const EventHandler nullHandler; 46 | /// Show dialog 47 | virtual void show() = 0; 48 | 49 | /// Get dialog title 50 | const string& title()const{ return m_title; } 51 | 52 | /// Set dialog title 53 | inline Dialog& setTitle(const string& title) 54 | { 55 | m_title = title; 56 | return *this; 57 | } 58 | 59 | /// Get decide handler function 60 | inline const EventHandler& decideHandler()const { return m_decideHandler;} 61 | 62 | /// Set decide handler function 63 | inline Dialog& setDecideHandler(const EventHandler& handler) 64 | { 65 | m_decideHandler = handler; 66 | return *this; 67 | } 68 | 69 | /// Get cancel handler function 70 | inline const EventHandler& cancelHandler()const{ return m_cancelHandler;} 71 | 72 | /// Set cancel handler function 73 | inline Dialog& setCancelHandler(const EventHandler& handler) 74 | { 75 | m_cancelHandler = handler; 76 | return *this; 77 | } 78 | 79 | protected: 80 | /// Decide function handler 81 | EventHandler m_decideHandler = nullHandler; 82 | /// Cancel function handler 83 | EventHandler m_cancelHandler = nullHandler; 84 | /// Title text 85 | string m_title = nullstr; 86 | }; 87 | 88 | 89 | namespace String 90 | { 91 | /// split string with pattern 92 | vector split(const string & soueceStr,const string& pattern); 93 | 94 | #ifdef ND_PLATFORM_WIN 95 | using std::wstring; 96 | extern wstring nullwstr; 97 | /*! 98 | @brief Convert utf-8 string to wide string 99 | @param utf8Str The source string to convert 100 | @return The generated wide string 101 | */ 102 | wstring string2wstring(const string& utf8Str); 103 | 104 | /*! 105 | @brief Convert wide string to utf-8 string 106 | @param wideStr The source string to convert 107 | @return The generated string 108 | */ 109 | string wstring2string(const wstring& wideStr); 110 | 111 | /*! 112 | @brief Convert multibyte string to wide string 113 | @param multibyteStr The source string to convert 114 | @return The generated wide string 115 | */ 116 | wstring multibyteString2wstring(const string& multibyteStr); 117 | 118 | /*! 119 | @brief Convert wide string to wide string 120 | @param wideStr The source string to convert 121 | @return The generated multibyte string 122 | */ 123 | string wstring2multibyteString(const wstring& wideStr); 124 | #endif //ND_PLATFORM_WIN 125 | } 126 | } 127 | 128 | #include "FileDialog.h" 129 | #include "MessageDialog.h" 130 | #include "ColorPickerDialog.h" 131 | 132 | #endif //__ND_DIALOGS_H__ 133 | -------------------------------------------------------------------------------- /src/gtk/ColorPickerDialog-GTK.cpp: -------------------------------------------------------------------------------- 1 | #include "../NativeDialog.h" 2 | #ifdef ND_PLATFORM_GTK 3 | #include 4 | 5 | namespace NativeDialog 6 | { 7 | // Show gtk color chooser dialog 8 | void ColorPickerDialog::show() 9 | { 10 | const int COLOR_SELECTED = -5; 11 | auto dialog = gtk_color_chooser_dialog_new(m_title.c_str(),nullptr); 12 | GdkRGBA color{ m_color.r, 13 | m_color.g, 14 | m_color.b, 15 | m_color.a 16 | }; 17 | 18 | gtk_color_chooser_set_rgba((GtkColorChooser *)dialog,&color); 19 | if( gtk_dialog_run(GTK_DIALOG (dialog) ) == COLOR_SELECTED ) 20 | { 21 | gtk_color_chooser_get_rgba((GtkColorChooser *)dialog,&color); 22 | 23 | // Get selected color value 24 | m_color = Color{ color.red , color.green , color.blue , color.alpha}; 25 | 26 | m_decideHandler(*this); 27 | } 28 | else 29 | { 30 | m_cancelHandler(*this); 31 | } 32 | gtk_widget_destroy(dialog); 33 | iterateGTKEvents(); 34 | } 35 | 36 | ColorPickerDialog::~ColorPickerDialog(){} 37 | } 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /src/gtk/FileDialog-GTK.cpp: -------------------------------------------------------------------------------- 1 | #include "../NativeDialog.h" 2 | #ifdef ND_PLATFORM_GTK 3 | #include 4 | 5 | namespace NativeDialog 6 | { 7 | // Add fileters to dialog 8 | void ApplyFiltersToDialog(const vector >& filters,GtkWidget* dialog) 9 | { 10 | for( auto & curFilterSrc : filters ) 11 | { 12 | GtkFileFilter * filter = gtk_file_filter_new (); 13 | gtk_file_filter_set_name (filter, curFilterSrc.first.c_str() ); 14 | 15 | auto extNames = String::split( curFilterSrc.second , ";" ); 16 | for(auto curExt : extNames) 17 | { 18 | string pattern = "*"; 19 | if(curExt != "*") 20 | pattern = "*.";pattern+=curExt; 21 | gtk_file_filter_add_pattern (filter, pattern.c_str()); 22 | } 23 | gtk_file_chooser_add_filter( GTK_FILE_CHOOSER(dialog), filter ); 24 | } 25 | } 26 | 27 | // Show GTK file chooser dialog 28 | void FileDialog::show() 29 | { 30 | m_selectedPathes.clear(); 31 | // The dialgo mode 32 | GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN; // Open File mode 33 | if( allowsDirectorySelection() && saveMode() && !allowsFileSelection() ) 34 | action = GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER; // Create folders mode 35 | else if ( saveMode() ) 36 | action = GTK_FILE_CHOOSER_ACTION_SAVE; // Save file mode 37 | else if( allowsDirectorySelection() ) 38 | action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER; // Open folder mode 39 | 40 | // create file chooser dialog 41 | GtkWidget *dialog = gtk_file_chooser_dialog_new( m_title.c_str() , 42 | nullptr, 43 | action, 44 | "_Cancel",GTK_RESPONSE_CANCEL, 45 | saveMode()?"_Save":"_Open", GTK_RESPONSE_ACCEPT, 46 | nullptr ); 47 | 48 | /// Build the filter list 49 | ApplyFiltersToDialog(m_filter,dialog); 50 | 51 | // Set the default path 52 | gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER(dialog), m_defaultPath.c_str() ); 53 | 54 | // Set dialog flags 55 | if(saveMode()) 56 | gtk_file_chooser_set_do_overwrite_confirmation( GTK_FILE_CHOOSER(dialog), true ); 57 | else if(allowsMultipleSelection()) 58 | gtk_file_chooser_set_select_multiple( GTK_FILE_CHOOSER(dialog), true ); 59 | 60 | if( gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT ) 61 | { 62 | GSList *fileList = gtk_file_chooser_get_filenames( GTK_FILE_CHOOSER(dialog) ); 63 | while(fileList) 64 | { 65 | if( fileList->data ) 66 | { 67 | const char* curCString = (char*)(fileList->data); 68 | if( curCString ) 69 | m_selectedPathes.push_back( string(curCString) ); 70 | } 71 | fileList = fileList->next; 72 | } 73 | 74 | m_decideHandler(*this); 75 | 76 | } 77 | else 78 | { 79 | m_cancelHandler(*this); 80 | } 81 | gtk_widget_destroy(dialog); 82 | iterateGTKEvents(); 83 | } 84 | } 85 | #endif 86 | -------------------------------------------------------------------------------- /src/gtk/MessageDialog-GTK.cpp: -------------------------------------------------------------------------------- 1 | #include "../NativeDialog.h" 2 | #ifdef ND_PLATFORM_GTK 3 | #include 4 | 5 | namespace NativeDialog 6 | { 7 | void MessageDialog::show() 8 | { 9 | // Create the widgets 10 | auto dialog = gtk_dialog_new(); 11 | // Set dialog title 12 | gtk_window_set_title( &((GtkDialog*)dialog)->window ,m_title.c_str()); 13 | 14 | // Add content widgets to dialog 15 | auto content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); 16 | gtk_container_set_border_width(GTK_CONTAINER (content_area),8); 17 | 18 | // Add the message label 19 | auto label = gtk_label_new (m_message.c_str()); 20 | 21 | auto box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); 22 | gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 12); 23 | gtk_container_add (GTK_CONTAINER (content_area), box); 24 | 25 | gtk_widget_show_all (dialog); 26 | 27 | // Add buttons 28 | for(int responseID=0; responseID!= int(m_buttons.size()); ++responseID) 29 | { 30 | gtk_dialog_add_button( (GtkDialog*)dialog, 31 | m_buttons[responseID].c_str(), 32 | responseID 33 | ); 34 | } 35 | // Show the dialog and retrive the selection result 36 | m_responseIndex = gtk_dialog_run (GTK_DIALOG (dialog)); 37 | if( m_responseIndex!= GTK_RESPONSE_NONE && m_responseIndex 5 | #import 6 | #include 7 | 8 | using namespace NativeDialog; 9 | 10 | @interface OSXColorPanel : NSObject 11 | -(void)colorUpdate:(NSColorPanel*)colorPanel; 12 | -(void)show:(ColorPickerDialog*)dialog; 13 | @property ColorPickerDialog* dialog; 14 | @end 15 | 16 | @implementation OSXColorPanel 17 | -(void)show:(NativeDialog::ColorPickerDialog*)dialog{ 18 | [self setDialog:dialog]; 19 | NSColorPanel *colorPanel = [NSColorPanel sharedColorPanel]; 20 | if(dialog) 21 | { 22 | const auto & color = dialog->color(); 23 | NSColor* nscolor = [NSColor colorWithRed:color.r 24 | green:color.g 25 | blue:color.b 26 | alpha:color.a]; 27 | 28 | [colorPanel setColor:nscolor]; 29 | [colorPanel setTitle: [NSString stringWithUTF8String:dialog->title().c_str()]]; 30 | } 31 | [colorPanel setShowsAlpha:YES]; 32 | [colorPanel setTarget:self]; 33 | [colorPanel setAction: @selector(colorUpdate:) ]; 34 | [colorPanel setIsVisible:YES]; 35 | } 36 | 37 | -(void)colorUpdate:(NSColorPanel*)colorPanel{ 38 | NSColor* theColor = colorPanel.color; 39 | ColorPickerDialog::Color color({ 40 | [theColor redComponent] , 41 | [theColor greenComponent], 42 | [theColor blueComponent], 43 | [theColor alphaComponent] 44 | }); 45 | 46 | if( [self dialog] ) 47 | { [self dialog]->setColor(color); 48 | [self dialog]->decideHandler()(*[self dialog]); 49 | } 50 | } 51 | @end 52 | 53 | namespace NativeDialog 54 | { 55 | OSXColorPanel * nativeCaller = nil; 56 | 57 | std::map dialog_panelMap; 58 | 59 | 60 | void ColorPickerDialog::show() 61 | { 62 | auto foundIt = dialog_panelMap.find(this); 63 | if( foundIt == dialog_panelMap.end() || foundIt->second == nullptr ) 64 | { 65 | OSXColorPanel* colorPanel = [[OSXColorPanel alloc]init]; 66 | dialog_panelMap[this] = colorPanel; 67 | [colorPanel show:this]; 68 | } 69 | else 70 | { 71 | OSXColorPanel* colorPanel = foundIt->second; 72 | [colorPanel show:this]; 73 | } 74 | 75 | } 76 | 77 | ColorPickerDialog::~ColorPickerDialog() 78 | { 79 | auto foundIt = dialog_panelMap.find(this); 80 | if( foundIt!= dialog_panelMap.end()) 81 | { 82 | OSXColorPanel* bindPanel = foundIt->second; 83 | if( bindPanel ) 84 | { 85 | [bindPanel setDialog:nullptr]; 86 | [bindPanel release]; 87 | } 88 | foundIt->second = nullptr; 89 | dialog_panelMap[this] = nullptr; 90 | } 91 | } 92 | } 93 | #endif 94 | -------------------------------------------------------------------------------- /src/osx/FileDialog-OSX.mm: -------------------------------------------------------------------------------- 1 | #include "../NativeDialog.h" 2 | #ifdef ND_PLATFORM_OSX 3 | #import 4 | 5 | namespace NativeDialog 6 | { 7 | using std::pair; 8 | 9 | void initializePanel(NSSavePanel* panel , const string &wndTitle , const string &initDir ,vector> &filter) 10 | { 11 | //set initialize directory 12 | if(initDir.length()) 13 | { 14 | 15 | NSString * _initDir = [[NSString alloc] initWithUTF8String:(initDir.c_str())]; 16 | [panel setDirectoryURL:[NSURL URLWithString:_initDir] ]; 17 | } 18 | //set window title text 19 | if(wndTitle.length()) 20 | { 21 | NSString * _wndTitle = [[NSString alloc] initWithUTF8String:(wndTitle.c_str())]; 22 | [panel setTitle:(_wndTitle)]; 23 | } 24 | //Set filters 25 | NSMutableArray *allowTypeArray = [[NSMutableArray alloc] init]; 26 | for( vector>::iterator it=filter.begin() ;it!=filter.end();++it ) 27 | { 28 | string curFilterTypes = it->second; 29 | if (curFilterTypes.compare("*"))//OSX ignores '*' filter 30 | { 31 | vector curFilters = String::split(curFilterTypes, ";"); 32 | 33 | for (const string & curType :curFilters) 34 | [allowTypeArray addObject: [NSString stringWithUTF8String:curType.c_str()]]; 35 | } 36 | } 37 | if([allowTypeArray count]) 38 | [panel setAllowedFileTypes:allowTypeArray]; 39 | [allowTypeArray release]; 40 | 41 | //Can create directories 42 | [panel setCanCreateDirectories:YES]; 43 | } 44 | 45 | void FileDialog::show() 46 | { 47 | bool saveModel = m_mode & SELECT_TO_SAVE; 48 | //File dialog panel object 49 | NSSavePanel * panel = nullptr; 50 | if(saveModel) 51 | panel = [NSSavePanel savePanel]; 52 | else 53 | panel = [NSOpenPanel openPanel]; 54 | 55 | initializePanel(panel,m_title,m_defaultPath,m_filter); 56 | m_selectedPathes.clear(); 57 | 58 | if(!saveModel) 59 | { 60 | NSOpenPanel * openPanel = (NSOpenPanel *) panel; 61 | //Set options for opening 62 | [openPanel setCanChooseFiles:NO]; 63 | [openPanel setCanChooseDirectories:NO]; 64 | [openPanel setAllowsMultipleSelection:NO]; 65 | if( m_mode & SELECT_DIR ) 66 | [openPanel setCanChooseDirectories:YES]; 67 | if( m_mode & SELECT_FILE ) 68 | [openPanel setCanChooseFiles:YES]; 69 | if( m_mode & MULTI_SELECT ) 70 | [openPanel setAllowsMultipleSelection:YES]; 71 | } 72 | [panel setAllowsOtherFileTypes:NO]; 73 | [panel setCanCreateDirectories:YES]; 74 | 75 | //Set host window 76 | NSWindow * _hostWnd = nullptr; 77 | if(m_pHostWnd) 78 | _hostWnd = (NSWindow*)m_pHostWnd; 79 | [panel beginSheetModalForWindow:_hostWnd completionHandler: ^(NSInteger result){ 80 | if ( result == NSModalResponseOK ) 81 | { 82 | if( !saveMode() ) 83 | { 84 | NSArray *urls = [(NSOpenPanel*)panel URLs]; 85 | for ( int i=0; i!=[urls count];++i ) 86 | { 87 | NSURL * url = [urls objectAtIndex:i]; 88 | m_selectedPathes.push_back( [[url path] UTF8String] ); 89 | } 90 | } 91 | else 92 | m_selectedPathes.push_back( [[[panel URL] path] UTF8String] ); 93 | 94 | m_decideHandler(*this); 95 | } 96 | else 97 | { 98 | m_cancelHandler(*this); 99 | } 100 | }]; 101 | } 102 | } 103 | #endif -------------------------------------------------------------------------------- /src/osx/MessageDialog-OSX.mm: -------------------------------------------------------------------------------- 1 | #include "../NativeDialog.h" 2 | #ifdef ND_PLATFORM_OSX 3 | 4 | #import 5 | #import 6 | 7 | 8 | namespace NativeDialog 9 | { 10 | void MessageDialog::show() 11 | { 12 | NSAlert *alert = [[NSAlert alloc] init]; 13 | // Set text content 14 | [alert setMessageText:[NSString stringWithUTF8String:m_title.c_str()]]; 15 | [alert setInformativeText:[NSString stringWithUTF8String:m_message.c_str()]]; 16 | // Add buttons 17 | for(auto it=m_buttons.rbegin();it!=m_buttons.rend();++it) 18 | [alert addButtonWithTitle:[NSString stringWithUTF8String:it->c_str()]]; 19 | 20 | int result = (int)[alert runModal]; 21 | int btnIndex = (int)m_buttons.size()- 1 - ( result - NSAlertFirstButtonReturn ); 22 | // Get responser 23 | if(btnIndex>=0 && btnIndex < m_buttons.size()) 24 | { 25 | m_responseIndex = btnIndex; 26 | m_decideHandler(*this); 27 | } 28 | else 29 | { 30 | m_cancelHandler(*this); 31 | } 32 | [alert release]; 33 | } 34 | } 35 | #endif -------------------------------------------------------------------------------- /src/win/ColorPickerDialog.cpp: -------------------------------------------------------------------------------- 1 | #include "../NativeDialog.h" 2 | #ifdef ND_PLATFORM_WIN 3 | #include 4 | #include 5 | #pragma comment(lib, "Comdlg32.lib") 6 | 7 | namespace NativeDialog 8 | { 9 | using String::wstring; 10 | void ColorPickerDialog::show() 11 | { 12 | CHOOSECOLOR cc; // common dialog box structure 13 | static COLORREF acrCustClr[16]; // array of custom colors 14 | HWND hwnd = nullptr; // owner window 15 | 16 | 17 | static DWORD rgbCurrent = RGB( // initial color selection 18 | m_color.r*255, 19 | m_color.g*255, 20 | m_color.b*255); 21 | 22 | // Initialize CHOOSECOLOR 23 | ZeroMemory(&cc, sizeof(cc)); 24 | cc.lStructSize = sizeof(cc); 25 | cc.hwndOwner = hwnd; 26 | cc.lpCustColors = (LPDWORD)acrCustClr; 27 | cc.rgbResult = rgbCurrent; 28 | cc.Flags = CC_FULLOPEN | CC_RGBINIT | CC_ANYCOLOR ; 29 | wstring title = String::string2wstring(m_title); 30 | cc.lpTemplateName = title.c_str(); 31 | if (ChooseColor(&cc) == TRUE) 32 | { 33 | rgbCurrent = cc.rgbResult; 34 | m_color = { GetRValue(rgbCurrent)/255.0, 35 | GetGValue(rgbCurrent)/255.0, 36 | GetBValue(rgbCurrent)/255.0, 37 | m_color.a }; 38 | m_decideHandler(*this); 39 | } 40 | else 41 | { 42 | m_cancelHandler(*this); 43 | } 44 | } 45 | 46 | ColorPickerDialog::~ColorPickerDialog() 47 | { 48 | 49 | } 50 | } 51 | #endif //ND_PLATFORM_WIN -------------------------------------------------------------------------------- /src/win/FileDialog-Windows.cpp: -------------------------------------------------------------------------------- 1 | #include "../NativeDialog.h" 2 | #ifdef ND_PLATFORM_WIN 3 | #include 4 | #include 5 | #include 6 | #pragma comment(lib, "Comdlg32.lib") 7 | /// The file path string max length(in characters) definition on windows platform 8 | #ifndef PAHT_MAX_LENGHT 9 | #define PAHT_MAX_LENGHT 4096 10 | #endif 11 | 12 | namespace NativeDialog 13 | { 14 | using String::wstring; 15 | using String::nullwstr; 16 | 17 | //The static variable for default diretory 18 | static wstring init_directory = nullwstr; 19 | 20 | // show directory selector dialog 21 | bool showDirectorySelector(wchar_t * buffer, wchar_t* wndTitle, bool withFile, const string& initDirectory); 22 | 23 | // convert string to TCHAR* string 24 | TCHAR* string2TString(const string& wstr) 25 | { 26 | TCHAR* tstring = nullptr; 27 | if (wstr.length()) 28 | { 29 | wstring wideStr = String::string2wstring(wstr); 30 | tstring = new TCHAR[wideStr.length() + 1]; 31 | for (size_t i = 0; i != wideStr.length(); ++i) 32 | tstring[i] = wideStr[i]; 33 | tstring[wideStr.length()] = L'\0'; 34 | } 35 | return tstring; 36 | } 37 | 38 | void FileDialog::show() 39 | { 40 | bool selected = false; 41 | 42 | // The buffer to save pathes selected 43 | TCHAR szBuffer[PAHT_MAX_LENGHT] = { 0 }; 44 | 45 | // The dialog window title 46 | TCHAR *_wndTitle = string2TString(m_title); 47 | 48 | // The initilize directory 49 | TCHAR *_initDir = string2TString(m_defaultPath); 50 | 51 | //Select diectory or file 52 | if (allowsDirectorySelection()) 53 | selected = showDirectorySelector(szBuffer, _wndTitle, allowsFileSelection(), m_defaultPath); 54 | //Select file 55 | else 56 | { 57 | #pragma region Select files 58 | OPENFILENAME ofn = { 0 }; 59 | ofn.lStructSize = sizeof(ofn); 60 | ofn.hwndOwner = nullptr; 61 | //Dialog title 62 | ofn.lpstrTitle = _wndTitle; 63 | // default directory 64 | ofn.lpstrInitialDir = _initDir; 65 | 66 | //Set filters 67 | TCHAR * lpstrFilter = nullptr; 68 | 69 | if (m_filter.size()) 70 | { 71 | string tempFilter; 72 | //Format filter string: Text Files%*.txt;*.TXT%XML Files%*.xml;*.XML%All Files%*.*% 73 | for (auto it = m_filter.begin(); it != m_filter.end(); ++it) 74 | { 75 | tempFilter += it->first; 76 | tempFilter += "%"; 77 | //Add *. to extentions: txt;h;cpp -> *.txt;*.h;*.cpp 78 | { 79 | string curFilterResult = nullstr; 80 | string curFilter = it->second; 81 | size_t curTypeStartAt = 0;//Selected filter index 82 | for (size_t i = 0; i != curFilter.length(); ++i) 83 | { 84 | if (*(curFilter.c_str() + i) == ';') 85 | { 86 | if (curTypeStartAt != 0) 87 | curFilterResult += ";"; 88 | 89 | curFilterResult += string("*.") + curFilter.substr(curTypeStartAt, i - curTypeStartAt); 90 | curTypeStartAt = i + 1; 91 | } 92 | 93 | } 94 | if (curTypeStartAt != 0) 95 | curFilterResult += ";"; 96 | curFilterResult += string("*.") + curFilter.substr(curTypeStartAt, curFilter.length() - curTypeStartAt); 97 | tempFilter += curFilterResult; 98 | tempFilter += "%"; 99 | } 100 | } 101 | //Format filter string: Text Files\0*.txt;*.TXT\0XML Files\0*.xml;*.XML\0All Files\0*.*\0 102 | lpstrFilter = string2TString(tempFilter); 103 | 104 | for (int i = 0; lpstrFilter[i] != '\0'; i++) 105 | { 106 | if (lpstrFilter[i] == L'%') 107 | lpstrFilter[i] = L'\0'; 108 | } 109 | ofn.lpstrFilter = lpstrFilter; 110 | } 111 | else 112 | ofn.lpstrFilter = L"All Files(*.*)\0*.*\0"; 113 | ofn.nFilterIndex = 0; 114 | 115 | // Set selected path buffer 116 | ofn.lpstrFile = szBuffer; 117 | ofn.nMaxFile = sizeof(szBuffer) / sizeof(*szBuffer); 118 | 119 | 120 | //Set dialog flags 121 | ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_EXPLORER; 122 | if ( allowsMultipleSelection() ) 123 | ofn.Flags = ofn.Flags | OFN_ALLOWMULTISELECT; 124 | if(saveMode()) 125 | ofn.Flags = OFN_EXPLORER | OFN_OVERWRITEPROMPT; 126 | //Save current working dirctory 127 | TCHAR curWorkDir[MAX_PATH] = { 0 }; 128 | GetCurrentDirectory(MAX_PATH, curWorkDir); 129 | 130 | // Show the dialog 131 | if( saveMode() ) 132 | GetSaveFileName(&ofn); 133 | else 134 | GetOpenFileName(&ofn); 135 | 136 | // Restore the saved working directory 137 | SetCurrentDirectory(curWorkDir); 138 | 139 | // free memery of fliters 140 | delete[] lpstrFilter; 141 | 142 | #pragma endregion Select Files 143 | } 144 | //free memery 145 | delete[] _wndTitle; 146 | delete[] _initDir; 147 | 148 | #pragma region Get selected file pathes 149 | 150 | m_selectedPathes.clear(); 151 | string path = String::wstring2string(szBuffer); 152 | vector speratorPos; 153 | size_t endPos = 0; 154 | // replace \0 to % in the buffer 155 | for (size_t i = 0; i != PAHT_MAX_LENGHT; ++i) 156 | { 157 | if (szBuffer[i] == L'\0' && szBuffer[i + 1] != '\0') 158 | { 159 | szBuffer[i] = '%'; 160 | speratorPos.push_back(i); 161 | } 162 | else if (szBuffer[i] == '\0' && szBuffer[i + 1] == '\0') 163 | { 164 | endPos = i; 165 | break; 166 | } 167 | } 168 | if (speratorPos.size() >= 1) 169 | { 170 | //Get file pathes between %s 171 | for (int i = 0; i != speratorPos.size() - 1; ++i) 172 | { 173 | TCHAR * tempPath = new TCHAR[speratorPos.at(i + 1) - speratorPos.at(i)]; 174 | size_t curTempPos = 0; 175 | for (size_t curCharPos = speratorPos.at(i) + 1; curCharPos != speratorPos.at(i + 1); ++curCharPos, ++curTempPos) 176 | tempPath[curTempPos] = szBuffer[curCharPos]; 177 | tempPath[speratorPos.at(i + 1) - speratorPos.at(i) - 1] = '\0'; 178 | 179 | string filePath = path + "\\" + String::wstring2string(tempPath); 180 | m_selectedPathes.push_back(filePath); 181 | delete[] tempPath; 182 | } 183 | // The last selected path 184 | if (endPos) 185 | { 186 | TCHAR * tempPath = new TCHAR[endPos - speratorPos.at(speratorPos.size() - 1)]; 187 | size_t curTempPos = 0; 188 | for (size_t curCharPos = speratorPos.at(speratorPos.size() - 1) + 1; curCharPos != endPos; ++curCharPos, ++curTempPos) 189 | tempPath[curTempPos] = szBuffer[curCharPos]; 190 | tempPath[endPos - speratorPos.at(speratorPos.size() - 1) - 1] = '\0'; 191 | 192 | string filePath = path + "\\" + String::wstring2string(tempPath); 193 | m_selectedPathes.push_back(filePath); 194 | delete[] tempPath; 195 | } 196 | } 197 | else if (path.length()) 198 | m_selectedPathes.push_back(path); 199 | 200 | #pragma endregion Get selected file pathes 201 | 202 | // Dispatching event 203 | selected = m_selectedPathes.size() != 0; 204 | if(selected) 205 | { 206 | m_decideHandler(*this); 207 | } 208 | else 209 | { 210 | m_cancelHandler(*this); 211 | } 212 | } 213 | 214 | bool showDirectorySelector(wchar_t * buffer, wchar_t* wndTitle, bool withFile, const string& initDirectory) 215 | { 216 | bool selected = false; 217 | 218 | if (initDirectory.length()) 219 | init_directory = String::string2wstring(initDirectory); 220 | else 221 | init_directory = nullwstr; 222 | 223 | BROWSEINFO bi; 224 | ZeroMemory(&bi, sizeof(BROWSEINFO)); 225 | bi.hwndOwner = nullptr; 226 | // The selected buffer 227 | bi.pszDisplayName = buffer; 228 | // Set window title 229 | bi.lpszTitle = wndTitle; 230 | // Set scan type 231 | bi.ulFlags = BIF_RETURNFSANCESTORS; 232 | if (withFile) 233 | bi.ulFlags = BIF_BROWSEINCLUDEFILES;// allows select files 234 | bi.ulFlags = bi.ulFlags | BIF_NEWDIALOGSTYLE;//allow create new directory 235 | bi.ulFlags = bi.ulFlags | BIF_STATUSTEXT; 236 | //bi.ulFlags = bi.ulFlags | BIF_EDITBOX;// Show selected edit box 237 | 238 | //Set callbacks of the selector 239 | bi.lpfn = [](HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData) 240 | { 241 | switch (uMsg) 242 | { 243 | case BFFM_INITIALIZED: //Set default selected path 244 | ::SendMessage(hwnd, BFFM_SETSELECTION, TRUE, (LPARAM)(LPTSTR)init_directory.c_str()); 245 | break; 246 | default: 247 | break; 248 | } 249 | return 0; 250 | }; 251 | bi.lParam = long(init_directory.c_str()); //Set default directory 252 | LPITEMIDLIST idl = SHBrowseForFolder(&bi);//Show the directory selector dialog 253 | if (idl) 254 | { 255 | SHGetPathFromIDList(idl, buffer); // save selected pathes 256 | selected = true; 257 | } 258 | return selected; 259 | } 260 | } 261 | #endif 262 | -------------------------------------------------------------------------------- /src/win/MessageDialog.cpp: -------------------------------------------------------------------------------- 1 | #include "../NativeDialog.h" 2 | #ifdef ND_PLATFORM_WIN 3 | #include 4 | #include 5 | #include 6 | #include 7 | #pragma comment(lib, "Comctl32.lib") 8 | 9 | namespace NativeDialog 10 | { 11 | void MessageDialog::show() 12 | { 13 | int nButtonPressed = 0; 14 | TASKDIALOGCONFIG config = {0}; 15 | TASKDIALOG_BUTTON * buttons = new TASKDIALOG_BUTTON[m_buttons.size()]; 16 | vector button_titles(m_buttons.size()); 17 | 18 | std::map choices; 19 | for( auto i = 0u; i < m_buttons.size(); ++i ) { 20 | button_titles[i] = String::string2wstring(m_buttons[i]); 21 | buttons[i].pszButtonText = button_titles[i].c_str(); 22 | buttons[i].nButtonID = i; 23 | choices[ buttons[i].nButtonID ] = i; 24 | } 25 | 26 | config.cbSize = sizeof(config); 27 | config.hInstance = NULL; 28 | std::wstring title = String::string2wstring(m_title); 29 | config.pszMainInstruction = title.c_str(); 30 | std::wstring content = String::string2wstring(m_message); 31 | config.pszContent = content.c_str(); 32 | config.pButtons = buttons; 33 | config.cButtons = (UINT)m_buttons.size(); 34 | 35 | int choice = -1; 36 | TaskDialogIndirect(&config, &nButtonPressed, NULL, NULL); 37 | if (choice >= 0 && choice < m_buttons.size()) 38 | { 39 | m_responseIndex = choice; 40 | m_decideHandler(*this); 41 | } 42 | else 43 | { 44 | m_cancelHandler(*this); 45 | } 46 | 47 | delete [] buttons; 48 | } 49 | } 50 | #endif //ND_PLATFORM_WIN 51 | -------------------------------------------------------------------------------- /test.cc: -------------------------------------------------------------------------------- 1 | #include "NativeDialog.h" 2 | #ifdef ND_PLATFORM_GTK 3 | #include 4 | #endif 5 | 6 | using namespace NativeDialog; 7 | 8 | // File dialog decide function handler 9 | // Called when file selected 10 | void fileSelHandler(const Dialog& dlg); 11 | 12 | 13 | // Message Dialog 14 | MessageDialog msgDialog("Message Dialog","\nSelect the next dialog to show.\n", 15 | {"File Dialog","Color Picker","Nothing"}); 16 | 17 | // Color Picker Dialog 18 | ColorPickerDialog cdlg("Pick a color you like"); 19 | 20 | // File Dialog 21 | FileDialog fdlg("Open some text files", FileDialog::SELECT_FILE|FileDialog::MULTI_SELECT); 22 | 23 | int main(int argc , char *argv[] ) 24 | { 25 | #ifdef ND_PLATFORM_GTK 26 | gtk_init(nullptr,nullptr); 27 | #endif 28 | 29 | msgDialog.setDecideHandler([](const Dialog& dlg) 30 | { 31 | auto mdlg = dynamic_cast(dlg); 32 | if( mdlg.responseButtonIndex() == 1 ) 33 | { 34 | 35 | cdlg.setColor({1,0,1,1}) 36 | .setDecideHandler( [](const Dialog& dlg) 37 | { 38 | auto colorDlg = dynamic_cast(dlg); 39 | auto color = colorDlg.color(); 40 | string msg = "You like the color: ("; 41 | msg += std::to_string(color.r)+","+std::to_string(color.g)+","+ 42 | std::to_string(color.b)+","+std::to_string(color.a)+")"; 43 | MessageDialog(nullstr,msg,{"Close"}).show(); 44 | }) 45 | .show(); 46 | } 47 | else if( mdlg.responseButtonTitle() == "File Dialog" ) 48 | { 49 | 50 | fdlg.setDefaultPath("..") 51 | .addFilter("Text Files","txt") 52 | .addFilter("Code files","c;cpp;h;hpp") 53 | .addFilter("All files","*") 54 | //.setAllowsDirectorySelection(true) 55 | //.setSaveMode(true) // Set save mode 56 | .setDecideHandler(fileSelHandler) 57 | .setCancelHandler( [](const Dialog& dlg) 58 | { 59 | MessageDialog("","You canceled select files",{"Close"}).show(); 60 | } 61 | ) 62 | .show(); 63 | } 64 | } 65 | ) 66 | .show(); 67 | 68 | return 0; 69 | } 70 | 71 | void fileSelHandler(const Dialog& dlg) 72 | { 73 | auto fdlg = dynamic_cast(dlg); 74 | const auto& fileList = fdlg.selectedPathes(); 75 | string msg = "You have selected files below:\n"; 76 | for( const string& path : fileList ) 77 | { 78 | msg += path; 79 | msg += "\n"; 80 | } 81 | MessageDialog("Notice",msg,{"Close"}).show(); 82 | 83 | } 84 | -------------------------------------------------------------------------------- /test.osx.mm: -------------------------------------------------------------------------------- 1 | #include 2 | #define GLFW_EXPOSE_NATIVE_COCOA 3 | #define GLFW_EXPOSE_NATIVE_NSGL 4 | #include 5 | 6 | #include "NativeDialog.h" 7 | using namespace NativeDialog; 8 | 9 | void showDialogs(GLFWwindow* wnd); 10 | 11 | ColorPickerDialog cdlg("Pick a color you like"); 12 | 13 | FileDialog fdlg("Open some text files", 14 | FileDialog::SELECT_FILE|FileDialog::MULTI_SELECT); 15 | 16 | MessageDialog msgDialog("Message Dialog","\nSelect the next dialog to show.\n", 17 | {"File Dialog","Color Picker","Nothing"} 18 | ); 19 | 20 | int main(void) 21 | { 22 | GLFWwindow* window; 23 | 24 | /* Initialize the library */ 25 | if (!glfwInit()) 26 | return -1; 27 | glfwSetErrorCallback([](int,const char* e){ 28 | MessageDialog("Error",e,{"OK"}).show(); 29 | }); 30 | /* Create a windowed mode window and its OpenGL context */ 31 | window = glfwCreateWindow(960, 640, "Native Dialogs", NULL, NULL); 32 | if (!window) 33 | { 34 | glfwTerminate(); 35 | return -1; 36 | } 37 | 38 | /* Make the window's context current */ 39 | glfwMakeContextCurrent(window); 40 | glClearColor(0,0,0, 1); 41 | 42 | /* Loop until the user closes the window */ 43 | while (!glfwWindowShouldClose(window)) 44 | { 45 | /* Poll for and process events */ 46 | glfwPollEvents(); 47 | if( glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS ) 48 | { 49 | showDialogs(window); 50 | } 51 | 52 | /* Render Color here */ 53 | glClear(GL_COLOR_BUFFER_BIT); 54 | glfwSwapBuffers(window); 55 | } 56 | 57 | glfwTerminate(); 58 | return 0; 59 | } 60 | 61 | void fileSelHandler(const Dialog& dlg); 62 | 63 | void showDialogs(GLFWwindow* wnd) 64 | { 65 | msgDialog.setDecideHandler([wnd](const Dialog& dlg) 66 | { 67 | auto mdlg = dynamic_cast(dlg); 68 | if( mdlg.responseButtonIndex() == 1 ) 69 | { 70 | cdlg.setColor({1,0,1,1}) 71 | .setDecideHandler( [](const Dialog& dlg) 72 | { 73 | auto colorDlg = dynamic_cast(dlg); 74 | auto color = colorDlg.color(); 75 | glClearColor(color.r, color.g, color.b, color.a); 76 | glClear(GL_COLOR_BUFFER_BIT); 77 | }) 78 | .show(); 79 | } 80 | else if( mdlg.responseButtonTitle() == "File Dialog" ) 81 | { 82 | 83 | fdlg.setDefaultPath("..") 84 | .addFilter("Text Files","txt") 85 | .addFilter("Code files","c;cpp;h;hpp") 86 | .addFilter("All files","*") 87 | //.setSaveMode(true) // Set save mode 88 | .setHostWindow(glfwGetCocoaWindow(wnd) ) 89 | .setDecideHandler(fileSelHandler) 90 | .setCancelHandler( [](const Dialog& dlg) 91 | { 92 | MessageDialog("","You canceled select files",{"Close"}).show(); 93 | }) 94 | .show(); 95 | } 96 | }) 97 | .show(); 98 | } 99 | 100 | void fileSelHandler(const Dialog& dlg) 101 | { 102 | auto fdlg = dynamic_cast(dlg); 103 | const auto& fileList = fdlg.selectedPathes(); 104 | string msg = "You have selected files below:\n"; 105 | for( const string& path : fileList ) 106 | { 107 | msg += path; 108 | msg += "\n"; 109 | } 110 | MessageDialog("Notice",msg,{"Close"}).show(); 111 | } --------------------------------------------------------------------------------