├── .clang-format ├── .editorconfig ├── .github └── workflows │ └── Tests.yml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE ├── Makefile ├── README.md ├── demo ├── README.md ├── common │ ├── nuklear_console_demo.c │ └── nuklear_console_demo.gif ├── glfw │ ├── Makefile │ └── main.c ├── pntr │ ├── .cmake │ │ ├── Findnuklear_gamepad.cmake │ │ ├── Findpntr.cmake │ │ ├── Findpntr_app.cmake │ │ ├── Findpntr_app_starter.cmake │ │ ├── Findpntr_nuklear.cmake │ │ └── Findraylib.cmake │ ├── CMakeLists.txt │ ├── Makefile │ ├── main.c │ └── resources │ │ └── image.png ├── raylib │ ├── CMakeLists.txt │ ├── Makefile │ ├── main.c │ ├── resources │ │ └── image.png │ └── shell.html └── sdl_renderer │ ├── Makefile │ ├── image.bmp │ └── main.c ├── nuklear_console.h ├── nuklear_console.hpp ├── nuklear_console_button.h ├── nuklear_console_checkbox.h ├── nuklear_console_color.h ├── nuklear_console_combobox.h ├── nuklear_console_file.h ├── nuklear_console_file_system.h ├── nuklear_console_image.h ├── nuklear_console_input.h ├── nuklear_console_knob.h ├── nuklear_console_label.h ├── nuklear_console_message.h ├── nuklear_console_progress.h ├── nuklear_console_property.h ├── nuklear_console_radio.h ├── nuklear_console_row.h ├── nuklear_console_spacing.h ├── nuklear_console_textedit.h ├── nuklear_console_textedit_text.h └── test ├── CMakeLists.txt ├── nuklear_console_test.c └── resources └── image.png /.clang-format: -------------------------------------------------------------------------------- 1 | # Docs: 2 | # http://clang.llvm.org/docs/ClangFormatStyleOptions.html 3 | 4 | # The style used for all options not specifically set in the configuration. 5 | # Available options: LLVM, Google, Chromium, Mozilla, WebKit 6 | BasedOnStyle: Google 7 | 8 | # The extra indent or outdent of access modifiers, e.g. public:. 9 | AccessModifierOffset: -4 10 | 11 | # If true, horizontally aligns arguments after an open bracket. 12 | # This applies to round brackets (parentheses), angle brackets and square brackets. This will result in formattings like code someLongFunction(argument1, argument2); endcode 13 | AlignAfterOpenBracket: AlwaysBreak 14 | 15 | # If true, aligns escaped newlines as far left as possible. Otherwise puts them into the right-most column. 16 | AlignEscapedNewlinesLeft: Left 17 | 18 | # If true, horizontally align operands of binary and ternary expressions. 19 | AlignOperands: true 20 | 21 | # If true, aligns trailing comments. 22 | AlignTrailingComments: false 23 | 24 | # If a function call or braced initializer list doesn’t fit on a line, allow putting all arguments onto the next line, even if BinPackArguments is false. 25 | AllowAllArgumentsOnNextLine: false 26 | 27 | # Allow putting all parameters of a function declaration onto the next line even if BinPackParameters is false. 28 | AllowAllParametersOfDeclarationOnNextLine: false 29 | 30 | # Allows contracting simple braced statements to a single line. 31 | # E.g., this allows if (a) { return; } to be put on a single line. 32 | # Shayan: I set it to false. I think if you want to create a single line block, 33 | # might as well just not create the block in the first place. 34 | AllowShortBlocksOnASingleLine: false 35 | 36 | # If true, short case labels will be contracted to a single line. 37 | AllowShortCaseLabelsOnASingleLine: true 38 | 39 | # Dependent on the value, int f() { return 0; } can be put on a single line. 40 | # SFS_None (in configuration: None) Never merge functions into a single line. 41 | # SFS_Inline (in configuration: Inline) Only merge functions defined inside a class. 42 | # SFS_Empty (in configuration: Empty) Only merge empty functions. 43 | # SFS_All (in configuration: All) Merge all functions fitting on a single line. 44 | AllowShortFunctionsOnASingleLine: Inline 45 | 46 | # If true, if (a) return; can be put on a single line. 47 | # Lagrange convention: 48 | # if( condition ) do something; -> YES 49 | # if( condition ) 50 | # do something; -> DON'T 51 | AllowShortIfStatementsOnASingleLine: true 52 | 53 | # Dependent on the value, auto lambda []() { return 0; } can be put on a single line. 54 | AllowShortLambdasOnASingleLine: All 55 | 56 | # If true, while (true) continue; can be put on a single line. 57 | # Lagrange convention: 58 | # while( condition ) do something; -> YES 59 | # while( condition ) 60 | # do something; -> DON'T 61 | AllowShortLoopsOnASingleLine: true 62 | 63 | # The function definition return type breaking style to use. This option is deprecated and is 64 | # retained for backwards compatibility. 65 | # AlwaysBreakAfterDefinitionReturnType: false 66 | 67 | # If true, always break before multiline string literals. 68 | AlwaysBreakBeforeMultilineStrings: false 69 | 70 | # If true, always break after the template<...> of a template declaration. 71 | AlwaysBreakTemplateDeclarations: true 72 | 73 | # If false, a function call's arguments will either be all on the same line or will have one line each. 74 | BinPackArguments: false 75 | 76 | # If false, a function call's arguments will either be all 77 | # on the same line or will have one line each. 78 | BinPackParameters: false 79 | 80 | # The way to wrap binary operators. 81 | # BOS_None (in configuration: None) Break after operators. 82 | # BOS_NonAssignment (in configuration: NonAssignment) Break before operators that aren't assignments. 83 | # BOS_All (in configuration: All) Break before operators. 84 | #BreakBeforeBinaryOperators: None 85 | 86 | # The brace breaking style to use. 87 | # BS_Attach (in configuration: Attach) Always attach braces to surrounding context. 88 | # BS_Linux (in configuration: Linux) Like Attach, but break before braces on function, namespace and class definitions. 89 | # BS_Stroustrup (in configuration: Stroustrup) Like Attach, but break before function definitions, and `else`. 90 | # BS_Allman (in configuration: Allman) Always break before braces. 91 | # BS_GNU (in configuration: GNU) Always break before braces and add an extra level of indentation to braces of control statements, not to those of class, function or other definitions. 92 | BraceWrapping: 93 | AfterClass: false 94 | AfterControlStatement: false 95 | AfterEnum: false 96 | AfterFunction: false 97 | AfterNamespace: false 98 | AfterObjCDeclaration: false 99 | AfterStruct: false 100 | AfterUnion: false 101 | BeforeCatch: false 102 | BeforeElse: true 103 | IndentBraces: false 104 | SplitEmptyFunction: false 105 | BreakBeforeBraces: Custom 106 | 107 | # If true, ternary operators will be placed after line breaks. 108 | BreakBeforeTernaryOperators: true 109 | 110 | # Always break constructor initializers before commas and align the commas with the colon. 111 | BreakConstructorInitializersBeforeComma: true 112 | 113 | # The column limit. 114 | # A column limit of 0 means that there is no column limit. In this case, clang-format will respect the input's line breaking decisions within statements unless they contradict other rules. 115 | ColumnLimit: 0 116 | 117 | # A regular expression that describes comments with special meaning, which should not be split into lines or otherwise changed. 118 | CommentPragmas: "\/*(.*)*\/" 119 | 120 | # If the constructor initializers don't fit on a line, put each initializer on its own line. 121 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 122 | 123 | # The number of characters to use for indentation of constructor initializer lists. 124 | ConstructorInitializerIndentWidth: 4 125 | 126 | # Indent width for line continuations. 127 | # A little more than normal indent 128 | ContinuationIndentWidth: 4 129 | 130 | # If true, format braced lists as best suited for C++11 braced lists. 131 | # Important differences: - No spaces inside the braced list. - No line break before the closing brace. - Indentation with the continuation indent, not with the block indent. 132 | # Fundamentally, C++11 braced lists are formatted exactly like function calls would be formatted in their place. If the braced list follows a name (e.g. a type or variable name), clang-format formats as if the {} were the parentheses of a function call with that name. If there is no name, a zero-length name is assumed. 133 | Cpp11BracedListStyle: true 134 | 135 | # If true, analyze the formatted file for the most common alignment of & and *. Point 136 | DerivePointerAlignment: false 137 | 138 | # Disables formatting at all. 139 | #DisableFormat: false 140 | 141 | # If true, clang-format detects whether function calls and definitions are formatted with one parameter per line. 142 | # Each call can be bin-packed, one-per-line or inconclusive. If it is inconclusive, e.g. completely on one line, but a decision needs to be made, clang-format analyzes whether there are other bin-packed cases in the input file and act accordingly. 143 | # NOTE: This is an experimental flag, that might go away or be renamed. Do not use this in config files, etc. Use at your own risk. 144 | #ExperimentalAutoDetectBinPacking: false 145 | 146 | # If true, clang-format adds missing namespace end comments and fixes invalid existing ones. 147 | FixNamespaceComments: true 148 | 149 | # A vector of macros that should be interpreted as foreach loops instead of as function calls. 150 | # These are expected to be macros of the form: code FOREACH(, ...) endcode 151 | # For example: BOOST_FOREACH. 152 | #ForEachMacros (std::vector) 153 | 154 | # Dependent on the value, multiple #include blocks can be sorted as one and divided based on category. 155 | IncludeBlocks: Preserve 156 | 157 | # Indent case labels one level from the switch statement. 158 | # When false, use the same indentation level as for the switch statement. Switch statement body is always indented one level more than case labels. 159 | IndentCaseLabels: true 160 | 161 | # The preprocessor directive indenting style to use. 162 | IndentPPDirectives: None 163 | 164 | # The number of columns to use for indentation. 165 | IndentWidth: 4 166 | 167 | # Indent if a function definition or declaration is wrapped after the type. 168 | #IndentWrappedFunctionNames: false 169 | 170 | # If true, empty lines at the start of blocks are kept. 171 | #KeepEmptyLinesAtTheStartOfBlocks: false 172 | 173 | # Language, this format style is targeted at. 174 | # LK_None (in configuration: None) Do not use. 175 | # LK_Cpp (in configuration: Cpp) Should be used for C, C++, ObjectiveC, ObjectiveC++. 176 | # LK_Java (in configuration: Java) Should be used for Java. 177 | # LK_JavaScript (in configuration: JavaScript) Should be used for JavaScript. 178 | # LK_Proto (in configuration: Proto) Should be used for Protocol Buffers (https://developers.google.com/protocol-buffers/). 179 | Language: Cpp 180 | 181 | # The maximum number of consecutive empty lines to keep. 182 | MaxEmptyLinesToKeep: 2 183 | 184 | # The indentation used for namespaces. 185 | # NI_None (in configuration: None) Don't indent in namespaces. 186 | # NI_Inner (in configuration: Inner) Indent only in inner namespaces (nested in other namespaces). 187 | # NI_All (in configuration: All) Indent in all namespaces. 188 | NamespaceIndentation: None 189 | 190 | # The number of characters to use for indentation of ObjC blocks. 191 | #ObjCBlockIndentWidth: 4 192 | 193 | # Add a space after @property in Objective-C, i.e. use \@property (readonly) instead of \@property(readonly). 194 | #ObjCSpaceAfterProperty: false 195 | 196 | # Add a space in front of an Objective-C protocol list, i.e. use Foo instead of Foo. 197 | #ObjCSpaceBeforeProtocolList: false 198 | 199 | # The penalty for breaking a function call after `call(`. 200 | #PenaltyBreakBeforeFirstCallParameter (unsigned) 201 | 202 | # The penalty for each line break introduced inside a comment. 203 | #PenaltyBreakComment (unsigned) 204 | 205 | # The penalty for breaking before the first <<. 206 | #PenaltyBreakFirstLessLess (unsigned) 207 | 208 | # The penalty for each line break introduced inside a string literal. 209 | #PenaltyBreakString (unsigned) 210 | 211 | # The penalty for each character outside of the column limit. 212 | #PenaltyExcessCharacter (unsigned) 213 | 214 | # Penalty for putting the return type of a function onto its own line. 215 | #PenaltyReturnTypeOnItsOwnLine (unsigned) 216 | 217 | # Pointer and reference alignment style. 218 | PointerAlignment: Left 219 | 220 | # If true, clang-format will attempt to re-flow comments. 221 | ReflowComments: true 222 | 223 | # Add a space before parentheses 224 | # if (condition) -> tick 225 | # if(condition) -> cross 226 | SpaceBeforeParens: ControlStatements 227 | 228 | # If true, a space may be inserted after C style casts. 229 | SpaceAfterCStyleCast: false 230 | 231 | # If false, spaces will be removed before assignment operators. 232 | #SpaceBeforeAssignmentOperators: true 233 | 234 | # If true, spaces may be inserted into `()`. 235 | SpaceInEmptyParentheses: false 236 | 237 | # The number of spaces before trailing line comments (// - comments). 238 | # This does not affect trailing block comments (/**/ - comments) as those commonly have different usage patterns and a number of special cases. 239 | SpacesBeforeTrailingComments: 1 240 | 241 | # If true, spaces will be inserted after `<` and before `>` in template argument lists 242 | SpacesInAngles: false 243 | 244 | # If true, spaces may be inserted into C style casts. 245 | SpacesInCStyleCastParentheses: false 246 | 247 | # If true, spaces are inserted inside container literals (e.g. ObjC and Javascript array and dict literals). 248 | SpacesInContainerLiterals: false 249 | 250 | # If true, spaces will be inserted after `(` and before `)`. 251 | SpacesInParentheses: false 252 | 253 | # If true, spaces will be inserted after `[` and before `]`. 254 | SpacesInSquareBrackets: false 255 | 256 | # Format compatible with this standard, e.g. use A > instead of A> for LS_Cpp03. 257 | # LS_Cpp03 (in configuration: Cpp03) Use C++03-compatible syntax. 258 | # LS_Cpp11 (in configuration: Cpp11) Use features of C++11 (e.g. A> instead of A >). 259 | # LS_Auto (in configuration: Auto) Automatic detection based on the input. 260 | Standard: Cpp11 261 | 262 | # The number of columns used for tab stops. 263 | TabWidth: 4 264 | 265 | # The way to use tab characters in the resulting file. 266 | # UT_Never (in configuration: Never) Never use tab. 267 | # UT_ForIndentation (in configuration: ForIndentation) Use tabs only for indentation. 268 | # UT_Always (in configuration: Always) Use tabs whenever we need to fill whitespace that spans at least from one tab stop to the next one. 269 | UseTab: Never 270 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_size = 4 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [Makefile] 12 | indent_style = tab 13 | -------------------------------------------------------------------------------- /.github/workflows/Tests.yml: -------------------------------------------------------------------------------- 1 | name: Build and Test 2 | 3 | on: 4 | push: 5 | branches: 6 | - '*' 7 | paths-ignore: 8 | - '**/README.md' 9 | pull_request: 10 | branches: 11 | - '*' 12 | workflow_dispatch: 13 | 14 | jobs: 15 | build: 16 | name: ${{ matrix.platform.os }} ${{ matrix.config.name }} ${{ matrix.type.name }} 17 | runs-on: ${{ matrix.platform.os }} 18 | 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | platform: 23 | # - { name: Windows VS2019, os: windows-2019 } 24 | # - { name: Windows VS2022, os: windows-2022 } 25 | - { name: Linux GCC, os: ubuntu-22.04, flags: -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_CXX_STANDARD=17 } 26 | #- { name: Linux Clang, os: ubuntu-22.04, flags: -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_CXX_STANDARD=17 } 27 | #- { name: Linux GCC, os: ubuntu-18.04, flags: -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_CXX_STANDARD=14 } 28 | #- { name: Linux Clang, os: ubuntu-18.04, flags: -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_CXX_STANDARD=14 } 29 | # - { name: MacOS XCode, os: macos-latest } 30 | config: 31 | #- { name: Shared, flags: -DBUILD_SHARED_LIBS=TRUE } 32 | - { name: Static, flags: -DBUILD_SHARED_LIBS=FALSE } 33 | type: 34 | #- { name: Release, flags: -DCMAKE_BUILD_TYPE=Release } 35 | - { name: Debug, flags: -DCMAKE_BUILD_TYPE=Debug } 36 | 37 | steps: 38 | - uses: actions/checkout@v4 39 | with: 40 | submodules: recursive 41 | 42 | #- name: Install Linux Dependencies 43 | # if: runner.os == 'Linux' 44 | # run: sudo apt-get update && sudo apt-get install -y libasound2-dev libx11-dev libxrandr-dev libxi-dev libgl1-mesa-dev libglu1-mesa-dev libxcursor-dev libxinerama-dev 45 | 46 | - name: Configure 47 | run: cmake -B build -S . -DCMAKE_CXX_FLAGS="-Wall -Wextra -Wpedantic" ${{ matrix.platform.flags }} ${{ matrix.type.flags }} ${{ matrix.config.flags }} 48 | 49 | - name: Build 50 | run: cmake --build build 51 | 52 | - name: Test 53 | run: ctest --verbose --test-dir build 54 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | *.o 3 | build 4 | nuklear_console_demo_* 5 | *.gif 6 | screenshot* 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "vendor/c-vector"] 2 | path = vendor/c-vector 3 | url = https://github.com/eteran/c-vector.git 4 | ignore = dirty 5 | branch = master 6 | [submodule "vendor/nuklear_gamepad"] 7 | path = vendor/nuklear_gamepad 8 | url = https://github.com/RobLoach/nuklear_gamepad.git 9 | ignore = dirty 10 | branch = master 11 | [submodule "vendor/Nuklear"] 12 | path = vendor/Nuklear 13 | url = https://github.com/Immediate-Mode-UI/Nuklear.git 14 | ignore = dirty 15 | branch = master 16 | [submodule "vendor/tinydir"] 17 | path = vendor/tinydir 18 | url = https://github.com/cxong/tinydir.git 19 | ignore = dirty 20 | branch = master 21 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.11) 2 | project(nuklear_console 3 | DESCRIPTION "nuklear_console: Console-like interface for Nuklear GUI" 4 | HOMEPAGE_URL "https://github.com/robloach/nuklear_console" 5 | VERSION 0.1.0 6 | LANGUAGES C 7 | ) 8 | 9 | # nuklear_gamepad 10 | find_package(nuklear_gamepad QUIET) 11 | if (NOT nuklear_gamepad_FOUND) 12 | include(FetchContent) 13 | FetchContent_Declare( 14 | nuklear_gamepad 15 | GIT_REPOSITORY https://github.com/RobLoach/nuklear_gamepad.git 16 | GIT_TAG 49c1cba 17 | ) 18 | FetchContent_GetProperties(nuklear_gamepad) 19 | if (NOT nuklear_gamepad_POPULATED) 20 | set(FETCHCONTENT_QUIET NO) 21 | FetchContent_Populate(nuklear_gamepad) 22 | add_subdirectory(${nuklear_gamepad_SOURCE_DIR} ${nuklear_gamepad_BINARY_DIR}) 23 | endif() 24 | endif() 25 | 26 | # nuklear_console 27 | add_library(nuklear_console INTERFACE) 28 | target_include_directories(nuklear_console INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) 29 | target_link_libraries(nuklear_console INTERFACE nuklear_gamepad) 30 | 31 | # Options 32 | if ("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}") 33 | set(NUKLEAR_CONSOLE_IS_MAIN TRUE) 34 | else() 35 | set(NUKLEAR_CONSOLE_IS_MAIN FALSE) 36 | endif() 37 | 38 | option(NUKLEAR_CONSOLE_BUILD_TESTS "Tests" ${NUKLEAR_CONSOLE_IS_MAIN}) 39 | 40 | # Examples 41 | if (NUKLEAR_CONSOLE_BUILD_TESTS) 42 | include(CTest) 43 | enable_testing() 44 | if (BUILD_TESTING) 45 | # set(CTEST_CUSTOM_TESTS_IGNORE 46 | # pkg-config--static 47 | # ) 48 | # Always print verbose output when tests fail if run using `make test`. 49 | list(APPEND CMAKE_CTEST_ARGUMENTS "--output-on-failure") 50 | add_subdirectory(test) 51 | endif() 52 | endif() 53 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2024 Rob Loach 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. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | build: glfw raylib sdl pntr 2 | 3 | test: clean test-sdl 4 | 5 | clean: 6 | $(MAKE) -C demo/sdl_renderer clean 7 | $(MAKE) -C demo/raylib clean 8 | $(MAKE) -C demo/glfw clean 9 | $(MAKE) -C demo/pntr clean 10 | rm -rf build 11 | 12 | raylib: 13 | $(MAKE) -C demo/raylib test 14 | 15 | glfw: 16 | $(MAKE) -C demo/glfw test 17 | 18 | sdl: 19 | $(MAKE) -C demo/sdl_renderer test 20 | 21 | pntr: 22 | $(MAKE) -C demo/pntr test 23 | 24 | web: 25 | $(MAKE) -C demo/raylib web 26 | 27 | clang: 28 | clang-format -i *.h 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nuklear_console 2 | 3 | Console-like user interface for [Nuklear](https://github.com/Immediate-Mode-UI/Nuklear) with [gamepad](https://github.com/robloach/nuklear_gamepad), keyboard, and mouse support. 4 | 5 | ![nuklear_console_demo Screenshot](demo/common/nuklear_console_demo.gif) 6 | 7 | ## Usage 8 | 9 | ``` c 10 | #define NK_IMPLEMENTATION 11 | #include "nukear.h" 12 | 13 | // Gamepad support https://github.com/robloach/nuklear_gamepad 14 | #define NK_GAMEPAD_IMPLEMENTATION 15 | #include "nuklear_gamepad.h" 16 | 17 | #define NK_CONSOLE_IMPLEMENTATION 18 | #include "nuklear_console.h" 19 | 20 | int main() { 21 | // Set up the console within the Nuklear context 22 | nk_console* console = nk_console_init(ctx); 23 | 24 | // Add some widgets 25 | nk_console_button(console, "New Game"); 26 | nk_console* options = nk_console_button(console, "Options"); 27 | { 28 | nk_console_button(options, "Some cool option!"); 29 | nk_console_button(options, "Option #2"); 30 | nk_console_button_onclick(options, "Back", nk_console_button_back); 31 | } 32 | nk_console_button(console, "Load Game"); 33 | nk_console_button(console, "Save Game"); 34 | 35 | // Render the console in a window 36 | nk_console_render_window(console, "nuklear_console", nk_rect(0, 0, 400, 300), NK_WINDOW_TITLE); 37 | 38 | // Clean it up 39 | nk_console_free(console); 40 | 41 | return 0; 42 | } 43 | ``` 44 | 45 | ## Widgets 46 | 47 | Buttons, Checkboxes, Color Select, Comboboxes, Files, Directories, Gamepad Input Buttons, Labels, Properties, Sliders, Knobs, Radio Options, Images, Knob, Rows, TextEdit, Messages. 48 | 49 | ## API 50 | 51 | ``` c 52 | // Console 53 | nk_console* nk_console_init(struct nk_context* context); 54 | void nk_console_free(nk_console* console); 55 | void nk_console_render(nk_console* console); 56 | void nk_console_render_window(nk_console* console, const char* title, struct nk_rect bounds, nk_uint flags); 57 | 58 | // Widgets 59 | nk_console* nk_console_button(nk_console* parent, const char* text); 60 | nk_console* nk_console_checkbox(nk_console* parent, const char* text, nk_bool* active); 61 | nk_console* nk_console_color(nk_console* parent, const char* label, struct nk_colorf* color, enum nk_color_format format); 62 | nk_console* nk_console_combobox(nk_console* parent, const char* label, const char *items_separated_by_separator, int separator, int* selected); 63 | nk_console* nk_console_label(nk_console* parent, const char* text); 64 | nk_console* nk_console_progress(nk_console* parent, const char* text, nk_size* current, nk_size max); 65 | nk_console* nk_console_property_int(nk_console* parent, const char* label, int min, int *val, int max, int step, float inc_per_pixel); 66 | nk_console* nk_console_property_float(nk_console* parent, const char* label, float min, float *val, float max, float step, float inc_per_pixel); 67 | nk_console* nk_console_slider_int(nk_console* parent, const char* label, int min, int* val, int max, int step); 68 | nk_console* nk_console_slider_float(nk_console* parent, const char* label, float min, float* val, float max, float step); 69 | nk_console* nk_console_radio(nk_console* parent, const char* label, int* selected); 70 | nk_console* nk_console_row_begin(nk_console* parent); 71 | void nk_console_row_end(nk_console* console); 72 | nk_console* nk_console_textedit(nk_console* parent, const char* label, char* buffer, int buffer_size); 73 | nk_console* nk_console_file(nk_console* parent, const char* label, char* file_path_buffer, int file_path_buffer_size); 74 | nk_console* nk_console_input(nk_console* parent, const char* label, int gamepad_num, int* out_gamepad_num, enum nk_gamepad_button* out_gamepad_button); 75 | void nk_console_show_message(nk_console* console, const char* text); 76 | 77 | // Utilities 78 | void nk_console_button_back(nk_console* button); 79 | nk_console* nk_console_button_onclick(nk_console* parent, const char* text, void (*onclick)(struct nk_console*)); 80 | nk_console* nk_console_get_top(nk_console* widget); 81 | int nk_console_get_widget_index(nk_console* widget); 82 | void nk_console_check_tooltip(nk_console* console); 83 | void nk_console_check_up_down(nk_console* widget, struct nk_rect bounds); 84 | nk_bool nk_console_is_active_widget(nk_console* widget); 85 | void nk_console_set_active_parent(nk_console* new_parent); 86 | void nk_console_set_active_widget(nk_console* widget); 87 | ``` 88 | 89 | ## Dependencies 90 | 91 | - C99+ 92 | - [Nuklear](https://github.com/Immediate-Mode-UI/Nuklear) 93 | - [nuklear_gamepad](https://github.com/robloach/nuklear_gamepad) 94 | - [c-vector](https://github.com/eteran/c-vector/) 95 | 96 | ## Development 97 | 98 | Use [clang-format](https://clang.llvm.org/docs/ClangFormat.html) to apply coding standards. 99 | ``` sh 100 | clang-format -i *.h 101 | ``` 102 | 103 | ## License 104 | 105 | [MIT](LICENSE) 106 | -------------------------------------------------------------------------------- /demo/README.md: -------------------------------------------------------------------------------- 1 | # nuklear_console_demo 2 | 3 | See [nuklear_console_demo.c](common/nuklear_console_demo.c) for an example of how to use nuklear_console. 4 | 5 | ![nuklear_console_demo Screenshot](common/nuklear_console_demo.gif) 6 | -------------------------------------------------------------------------------- /demo/common/nuklear_console_demo.c: -------------------------------------------------------------------------------- 1 | #include // strcmp 2 | #include // snprintf 3 | 4 | #include "../../vendor/Nuklear/demo/common/style.c" 5 | 6 | #define NK_GAMEPAD_IMPLEMENTATION 7 | #include "../../vendor/nuklear_gamepad/nuklear_gamepad.h" 8 | 9 | #define NK_CONSOLE_IMPLEMENTATION 10 | #include "../../nuklear_console.h" 11 | 12 | // Demo 13 | static struct nk_console* console = NULL; 14 | static struct nk_gamepads gamepads; 15 | static nk_bool shouldClose = nk_false; 16 | 17 | // Theme 18 | static int theme = THEME_DRACULA; 19 | 20 | // Progress 21 | static nk_size progressValue = 50; 22 | 23 | // Combobox 24 | static int weapon = 1; 25 | 26 | // Property 27 | static int property_int_test = 20; 28 | static float property_float_test = 0.4f; 29 | 30 | // Slider 31 | static int slider_int_test = 20; 32 | static float slider_float_test = 0.4f; 33 | 34 | // Knob 35 | static int knob_int_test = 20; 36 | static float knob_float_test = 0.4f; 37 | 38 | // Radio 39 | static int radio_option = 1; 40 | static int radio_option2 = 0; 41 | 42 | // Checkbox 43 | static nk_bool checkbox1 = nk_false; 44 | static nk_bool checkbox2 = nk_false; 45 | static nk_bool checkbox3 = nk_false; 46 | static nk_bool checkbox4 = nk_false; 47 | static nk_bool checkbox5 = nk_false; 48 | static nk_bool checkbox6 = nk_true; 49 | 50 | // Messages 51 | static int message_count = 0; 52 | 53 | // File 54 | static char file_path_buffer[4096] = {0}; 55 | static int file_path_buffer_size = 4096; 56 | 57 | // Directory 58 | static char dir_buffer[4096] = {0}; 59 | static int dir_buffer_size = 4096; 60 | 61 | // Textedit 62 | static const int textedit_buffer_size = 256; 63 | static char textedit_buffer[256] = "vurtun"; 64 | 65 | // Input 66 | static int gamepad_number = 0; 67 | static enum nk_gamepad_button gamepad_button = NK_GAMEPAD_BUTTON_A; 68 | 69 | // Color 70 | static struct nk_colorf color = {0.31f, 1.0f, 0.48f, 1.0f}; 71 | 72 | void button_clicked(struct nk_console* button, void* user_data) { 73 | NK_UNUSED(user_data); 74 | if (strcmp(nk_console_get_label(button), "Quit Game") == 0) { 75 | shouldClose = nk_true; 76 | } 77 | } 78 | 79 | void theme_changed(struct nk_console* combobox, void* user_data) { 80 | NK_UNUSED(user_data); 81 | set_style(combobox->ctx, (enum theme)theme); 82 | } 83 | 84 | void exclude_other_checkbox(nk_console* unused, void* user_data) { 85 | NK_UNUSED(unused); 86 | nk_console* other = (nk_console*)user_data; 87 | other->disabled = !other->disabled; 88 | } 89 | 90 | void toggle_visibility(nk_console* unused, void* user_data) { 91 | NK_UNUSED(unused); 92 | nk_console* other = (nk_console*)user_data; 93 | other->visible = !other->visible; 94 | } 95 | 96 | void nk_console_demo_show_message(struct nk_console* button, void* user_data) { 97 | NK_UNUSED(user_data); 98 | char message[128]; 99 | snprintf(message, 128, "This is message #%d!", ++message_count); 100 | nk_console_show_message(button, message); 101 | } 102 | 103 | void nk_console_radio_changed(struct nk_console* radio, void* user_data) { 104 | NK_UNUSED(user_data); 105 | nk_console_show_message(radio, radio->label); 106 | } 107 | 108 | nk_console* nuklear_console_demo_init(struct nk_context* ctx, void* user_data, struct nk_image image) { 109 | console = nk_console_init(ctx); 110 | 111 | nk_gamepad_init(&gamepads, ctx, user_data); 112 | nk_console_set_gamepads(console, &gamepads); 113 | 114 | // New Game 115 | nk_console* newgame = nk_console_button(console, "New Game"); 116 | { 117 | nk_console_button_set_symbol(newgame, NK_SYMBOL_PLUS); 118 | nk_console_label(newgame, "This would start a new game!"); 119 | nk_console_button_onclick(newgame, "Back", &nk_console_button_back); 120 | } 121 | 122 | // Widgets 123 | nk_console* widgets = nk_console_button(console, "Widgets"); 124 | { 125 | nk_console_set_tooltip(widgets, "Displays some random options!"); 126 | 127 | nk_console* label_button = nk_console_button(widgets, "Labels"); 128 | { 129 | nk_console_label(label_button, "Simple label."); 130 | nk_console* label1 = nk_console_label(label_button, "Selectable label #1"); 131 | label1->selectable = nk_true; 132 | nk_console_add_event(label1, NK_CONSOLE_EVENT_CLICKED, &nk_console_demo_show_message); 133 | 134 | nk_console_label(label_button, "Selectable label #2.") 135 | ->selectable = nk_true; 136 | nk_console_label(label_button, "This is a label that will wrap across multiple lines.") 137 | ->height = 128; 138 | nk_console_label(label_button, "This is a disabled label") 139 | ->disabled = nk_true; 140 | nk_console_label(label_button, "Center Aligned Label!") 141 | ->alignment = NK_TEXT_CENTERED; 142 | nk_console_label(label_button, "Right Aligned Label!") 143 | ->alignment = NK_TEXT_RIGHT; 144 | nk_console_button_onclick(label_button, "Back", &nk_console_button_back); 145 | } 146 | 147 | nk_console* checkbox_button = nk_console_button(widgets, "Checkboxes"); 148 | { 149 | nk_console_checkbox(checkbox_button, "Checkbox", &checkbox1) 150 | ->tooltip = "This is a checkbox!"; 151 | nk_console_checkbox(checkbox_button, "Right aligned", &checkbox3) 152 | ->alignment = NK_TEXT_RIGHT; 153 | nk_console_checkbox(checkbox_button, "Disabled Checkbox", &checkbox2) 154 | ->disabled = nk_true; 155 | 156 | // Onchange callbacks can be used to implement custom logic. 157 | // These two checkboxes disable each other when checked. 158 | nk_console* exclude_a = nk_console_checkbox(checkbox_button, "Exclusive A (disables B)", &checkbox4); 159 | nk_console* exclude_b = nk_console_checkbox(checkbox_button, "Exclusive B (disables A)", &checkbox5); 160 | nk_console_add_event_handler(exclude_a, NK_CONSOLE_EVENT_CHANGED, &exclude_other_checkbox, exclude_b, NULL); 161 | nk_console_add_event_handler(exclude_b, NK_CONSOLE_EVENT_CHANGED, &exclude_other_checkbox, exclude_a, NULL); 162 | 163 | // Checkbox that will show/hide the below label. 164 | nk_console* checkbox_show_label = nk_console_checkbox(checkbox_button, "Show Label", &checkbox6); 165 | nk_console* label_to_show = nk_console_label(checkbox_button, "This label is only shown when the checkbox is checked."); 166 | nk_console_add_event_handler(checkbox_show_label, NK_CONSOLE_EVENT_CHANGED, &toggle_visibility, label_to_show, NULL); 167 | 168 | nk_console_button_onclick(checkbox_button, "Back", &nk_console_button_back); 169 | } 170 | 171 | nk_console* buttons = nk_console_button(widgets, "Buttons"); 172 | { 173 | nk_console_button(buttons, "Button"); 174 | nk_console_button(buttons, "Button #2"); 175 | nk_console_button(buttons, "Disabled Button") 176 | ->disabled = nk_true; 177 | 178 | // Image Button 179 | nk_console* image_button = nk_console_button(buttons, "Image"); 180 | nk_console_button_set_image(image_button, image); 181 | image_button->height = 128; 182 | 183 | nk_console_button_onclick(buttons, "Back", &nk_console_button_back); 184 | } 185 | 186 | // Radio Buttons 187 | nk_console* radios = nk_console_button(widgets, "Radios"); 188 | { 189 | nk_console_label(radios, "Option A:"); 190 | nk_console_radio(radios, "Radio #1", &radio_option); 191 | nk_console_radio(radios, "Radio #2", &radio_option); 192 | nk_console_radio(radios, "Radio #3", &radio_option); 193 | nk_console_radio(radios, "Radio #4", &radio_option); 194 | 195 | nk_console_label(radios, "Option B:"); 196 | nk_console_radio(radios, "Left Aligned #1", &radio_option2)->alignment = NK_TEXT_LEFT; 197 | nk_console_radio(radios, "Left Aligned #2", &radio_option2)->alignment = NK_TEXT_LEFT; 198 | nk_console_radio(radios, "Center Aligned #1", &radio_option2)->alignment = NK_TEXT_CENTERED; 199 | nk_console_radio(radios, "Center Aligned #2", &radio_option2)->alignment = NK_TEXT_CENTERED; 200 | nk_console_radio(radios, "Right Aligned #1", &radio_option2)->alignment = NK_TEXT_RIGHT; 201 | nk_console_radio(radios, "Right Aligned #2", &radio_option2)->alignment = NK_TEXT_RIGHT; 202 | nk_console_radio(radios, "Disabled", &radio_option2)->disabled = nk_true; 203 | nk_console_radio(radios, "Invisible", &radio_option2)->visible = nk_false; 204 | 205 | nk_console_button_onclick(radios, "Back", &nk_console_button_back); 206 | } 207 | 208 | // Images 209 | nk_console* images = nk_console_button(widgets, "Images"); 210 | { 211 | nk_console* img = nk_console_image(images, image); 212 | nk_console_set_height(img, image.h); 213 | img = nk_console_image_color(images, image, nk_rgb(255, 0, 0)); 214 | nk_console_set_height(img, image.h); 215 | 216 | nk_console_button_onclick(images, "Back", &nk_console_button_back); 217 | } 218 | 219 | // Spacing 220 | nk_console* spacing = nk_console_button(widgets, "Spacing"); 221 | { 222 | nk_console* row = nk_console_row_begin(spacing); 223 | nk_console_spacing(row, 1); 224 | nk_console* b = nk_console_button(row,""); 225 | nk_console_button_set_symbol(b, NK_SYMBOL_TRIANGLE_UP); 226 | nk_console_spacing(row, 1); 227 | nk_console_row_end(row); 228 | 229 | row = nk_console_row_begin(spacing); 230 | b = nk_console_button(row,""); 231 | nk_console_button_set_symbol(b, NK_SYMBOL_TRIANGLE_LEFT); 232 | nk_console_spacing(row, 1); 233 | b = nk_console_button(row,""); 234 | nk_console_button_set_symbol(b, NK_SYMBOL_TRIANGLE_RIGHT); 235 | nk_console_row_end(row); 236 | 237 | row = nk_console_row_begin(spacing); 238 | nk_console_spacing(row, 1); 239 | b = nk_console_button(row,""); 240 | nk_console_button_set_symbol(b, NK_SYMBOL_TRIANGLE_DOWN); 241 | nk_console_spacing(row, 1); 242 | nk_console_row_end(row); 243 | 244 | nk_console_button_onclick(spacing, "Back", &nk_console_button_back); 245 | } 246 | 247 | // Progress Bar 248 | nk_console* progressbar = nk_console_button(widgets, "Progress Bar"); 249 | { 250 | nk_console_progress(progressbar, "Progress", &progressValue, 100); 251 | nk_console_button_onclick(progressbar, "Back", &nk_console_button_back); 252 | } 253 | 254 | // Input: From any gamepad (-1) 255 | nk_console_input(widgets, "Input Button", -1, &gamepad_number, &gamepad_button); 256 | 257 | // Combobox 258 | nk_console_combobox(widgets, "ComboBox", "Fists;Chainsaw;Pistol;Shotgun;Chaingun", ';', &weapon) 259 | ->tooltip = "Choose a weapon! The chainsaw is the best!"; 260 | 261 | // Property 262 | nk_console* properties = nk_console_button(widgets, "Property"); 263 | { 264 | nk_console_property_int(properties, "Property Int", 10, &property_int_test, 30, 1, 1); 265 | nk_console_property_float(properties, "Property Float", 0.0f, &property_float_test, 2.0f, 0.1f, 1); 266 | nk_console_knob_int(properties, "Knob Int", 0, &knob_int_test, 30, 1, 1); 267 | nk_console_knob_float(properties, "Knob Float", 0.0f, &knob_float_test, 2.0f, 0.1f, 1); 268 | nk_console_button_onclick(properties, "Back", &nk_console_button_back); 269 | } 270 | 271 | // Sliders 272 | nk_console* sliders = nk_console_button(widgets, "Sliders"); 273 | { 274 | nk_console_slider_float(sliders, "Slider Float", 0.0f, &slider_float_test, 2.0f, 0.1f)->tooltip = "Slider float is cool! It's what you want to use."; 275 | nk_console_slider_int(sliders, "Slider Int", 0, &slider_int_test, 20, 1); 276 | nk_console_button_onclick(sliders, "Back", &nk_console_button_back); 277 | } 278 | 279 | // Textedit 280 | nk_console* textedit = nk_console_textedit(widgets, "Username", textedit_buffer, textedit_buffer_size); 281 | nk_console_set_tooltip(textedit, "Enter your username!"); 282 | 283 | // Color 284 | nk_console_color(widgets, "Select Color", &color, NK_RGBA); 285 | 286 | // File 287 | nk_console_file(widgets, "File", file_path_buffer, file_path_buffer_size); 288 | nk_console_dir(widgets, "Directory", dir_buffer, dir_buffer_size); 289 | 290 | // Messages 291 | nk_console_button_onclick(widgets, "Show Message", &nk_console_demo_show_message); 292 | 293 | // Back Button 294 | nk_console_button_set_symbol( 295 | nk_console_button_onclick(widgets, "Back", &nk_console_button_back), 296 | NK_SYMBOL_TRIANGLE_LEFT); 297 | } 298 | 299 | nk_console* theme_options = nk_console_combobox(console, "Theme", "Black;White;Red;Blue;Dark;Dracula;Default", ';', &theme); 300 | nk_console_add_event(theme_options, NK_CONSOLE_EVENT_CHANGED, &theme_changed); 301 | theme_options->tooltip = "Change the theme of the console!"; 302 | set_style(ctx, (enum theme)theme); 303 | 304 | // Rows 305 | nk_console* calc = nk_console_button(console, "Calculator"); 306 | { 307 | nk_console* row = nk_console_row_begin(calc); 308 | nk_console_button(row, "sqrt"); 309 | nk_console_button(row, "pi"); 310 | nk_console_row_end(row); 311 | 312 | row = nk_console_row_begin(calc); 313 | nk_console_button(row, "AC"); 314 | nk_console_button(row, "()"); 315 | nk_console_button(row, "%"); 316 | nk_console_button(row, "/"); 317 | nk_console_row_end(row); 318 | 319 | row = nk_console_row_begin(calc); 320 | nk_console_button(row, "7"); 321 | nk_console_button(row, "8"); 322 | nk_console_button(row, "9"); 323 | nk_console_button(row, "*"); 324 | nk_console_row_end(row); 325 | 326 | row = nk_console_row_begin(calc); 327 | nk_console_button(row, "4"); 328 | nk_console_button(row, "5"); 329 | nk_console_button(row, "6"); 330 | nk_console_button(row, "-"); 331 | nk_console_row_end(row); 332 | 333 | row = nk_console_row_begin(calc); 334 | nk_console_button(row, "1"); 335 | nk_console_button(row, "2"); 336 | nk_console_button(row, "3"); 337 | nk_console_button(row, "+"); 338 | nk_console_row_end(row); 339 | 340 | row = nk_console_row_begin(calc); 341 | nk_console_button(row, "0"); 342 | nk_console_button(row, "."); 343 | nk_console_button(row, "bksp"); 344 | nk_console_button(row, "="); 345 | nk_console_row_end(row); 346 | 347 | nk_console_button_set_symbol( 348 | nk_console_button_onclick(calc, "Back", &nk_console_button_back), 349 | NK_SYMBOL_TRIANGLE_LEFT); 350 | 351 | calc->tooltip = "Demo rows and grids!"; 352 | } 353 | 354 | nk_console_button(console, "Save Game")->disabled = nk_true; 355 | nk_console_button_onclick(console, "Quit Game", &button_clicked); 356 | 357 | return console; 358 | } 359 | 360 | nk_bool nuklear_console_demo_render() { 361 | nk_console_render(console); 362 | 363 | return shouldClose;; 364 | } 365 | 366 | void nuklear_console_demo_free() { 367 | nk_gamepad_free(nk_console_get_gamepads(console)); 368 | nk_console_free(console); 369 | } 370 | -------------------------------------------------------------------------------- /demo/common/nuklear_console_demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/nuklear_console/e6001e75d0f237356865bb4306cc97d174640d1d/demo/common/nuklear_console_demo.gif -------------------------------------------------------------------------------- /demo/glfw/Makefile: -------------------------------------------------------------------------------- 1 | # Install 2 | BIN = nuklear_console_demo_glfw 3 | 4 | # Flags 5 | #CFLAGS += -std=c89 -Wall -Wextra -pedantic -O2 6 | CFLAGS += -std=c99 -Wall -Wextra -pedantic -g -O0 7 | 8 | SRC = main.c 9 | OBJ = $(SRC:.c=.o) 10 | 11 | ifeq ($(OS),Windows_NT) 12 | BIN := $(BIN).exe 13 | LIBS = -lglfw3 -lopengl32 -lm -lGLU32 -lGLEW32 14 | else 15 | UNAME_S := $(shell uname -s) 16 | GLFW3 := $(shell pkg-config --libs glfw3) 17 | ifeq ($(UNAME_S),Darwin) 18 | LIBS := $(GLFW3) -framework OpenGL -framework Cocoa -framework IOKit -framework CoreVideo -lm -lGLEW -L/usr/local/lib 19 | else 20 | LIBS = $(GLFW3) -lGL -lm -lGLU -lGLEW 21 | endif 22 | endif 23 | 24 | $(BIN): clean 25 | $(CC) $(SRC) $(CFLAGS) -o $(BIN) $(LIBS) 26 | 27 | test: $(BIN) 28 | ./$(BIN) 29 | 30 | clean: 31 | rm -rf $(BIN) $(OBJS) 32 | -------------------------------------------------------------------------------- /demo/glfw/main.c: -------------------------------------------------------------------------------- 1 | /* nuklear - 1.32.0 - public domain */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | #define NK_INCLUDE_FIXED_TYPES 16 | #define NK_INCLUDE_STANDARD_IO 17 | #define NK_INCLUDE_STANDARD_VARARGS 18 | #define NK_INCLUDE_DEFAULT_ALLOCATOR 19 | #define NK_INCLUDE_VERTEX_BUFFER_OUTPUT 20 | #define NK_INCLUDE_FONT_BAKING 21 | #define NK_INCLUDE_DEFAULT_FONT 22 | #define NK_IMPLEMENTATION 23 | #define NK_GLFW_GL3_IMPLEMENTATION 24 | #define NK_KEYSTATE_BASED_INPUT 25 | #include "../../vendor/Nuklear/nuklear.h" 26 | #include "../../vendor/Nuklear/demo/glfw_opengl3/nuklear_glfw_gl3.h" 27 | 28 | #define WINDOW_WIDTH 800 29 | #define WINDOW_HEIGHT 600 30 | 31 | #define MAX_VERTEX_BUFFER 512 * 1024 32 | #define MAX_ELEMENT_BUFFER 128 * 1024 33 | 34 | /* =============================================================== 35 | * 36 | * EXAMPLE 37 | * 38 | * ===============================================================*/ 39 | 40 | #define NK_CONSOLE_ENABLE_TINYDIR 41 | #include "../common/nuklear_console_demo.c" 42 | 43 | 44 | /* =============================================================== 45 | * 46 | * DEMO 47 | * 48 | * ===============================================================*/ 49 | static void error_callback(int e, const char *d) 50 | {printf("Error %d: %s\n", e, d);} 51 | 52 | int main(void) 53 | { 54 | /* Platform */ 55 | struct nk_glfw glfw = {0}; 56 | static GLFWwindow *win; 57 | int width = 0, height = 0; 58 | struct nk_context *ctx; 59 | float font_scale = 3; 60 | 61 | /* GLFW */ 62 | glfwSetErrorCallback(error_callback); 63 | if (!glfwInit()) { 64 | fprintf(stdout, "[GFLW] failed to init!\n"); 65 | exit(1); 66 | } 67 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 68 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); 69 | glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 70 | #ifdef __APPLE__ 71 | glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); 72 | #endif 73 | win = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "Demo", NULL, NULL); 74 | glfwMakeContextCurrent(win); 75 | glfwGetWindowSize(win, &width, &height); 76 | 77 | /* OpenGL */ 78 | glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); 79 | glewExperimental = 1; 80 | if (glewInit() != GLEW_OK) { 81 | fprintf(stderr, "Failed to setup GLEW\n"); 82 | exit(1); 83 | } 84 | 85 | ctx = nk_glfw3_init(&glfw, win, NK_GLFW3_INSTALL_CALLBACKS); 86 | /* Load Fonts: if none of these are loaded a default font will be used */ 87 | /* Load Cursor: if you uncomment cursor loading please hide the cursor */ 88 | { 89 | struct nk_font_atlas *atlas; 90 | struct nk_font_config config = nk_font_config(0); 91 | struct nk_font *font; 92 | nk_glfw3_font_stash_begin(&glfw, &atlas); 93 | font = nk_font_atlas_add_default(atlas, 13 * font_scale, &config); 94 | /*struct nk_font *droid = nk_font_atlas_add_from_file(atlas, "../../../extra_font/DroidSans.ttf", 14, 0);*/ 95 | /*struct nk_font *roboto = nk_font_atlas_add_from_file(atlas, "../../../extra_font/Roboto-Regular.ttf", 14, 0);*/ 96 | /*struct nk_font *future = nk_font_atlas_add_from_file(atlas, "../../../extra_font/kenvector_future_thin.ttf", 13, 0);*/ 97 | /*struct nk_font *clean = nk_font_atlas_add_from_file(atlas, "../../../extra_font/ProggyClean.ttf", 12, 0);*/ 98 | /*struct nk_font *tiny = nk_font_atlas_add_from_file(atlas, "../../../extra_font/ProggyTiny.ttf", 10, 0);*/ 99 | /*struct nk_font *cousine = nk_font_atlas_add_from_file(atlas, "../../../extra_font/Cousine-Regular.ttf", 13, 0);*/ 100 | nk_glfw3_font_stash_end(&glfw); 101 | nk_style_set_font(ctx, &font->handle); 102 | /*nk_style_load_all_cursors(ctx, atlas->cursors);*/ 103 | /*nk_style_set_font(ctx, &droid->handle);*/ 104 | } 105 | 106 | nk_console* console = nuklear_console_demo_init(ctx, NULL, nk_image_id(0)); 107 | 108 | while (!glfwWindowShouldClose(win)) 109 | { 110 | /* Input */ 111 | glfwPollEvents(); 112 | nk_glfw3_new_frame(&glfw); 113 | 114 | nk_gamepad_update(nk_console_get_gamepads(console)); 115 | 116 | int flags = NK_WINDOW_SCROLL_AUTO_HIDE | NK_WINDOW_TITLE; 117 | 118 | /* GUI */ 119 | if (nk_begin(ctx, "nuklear_console", nk_rect(0, 0, width, height), flags)) { 120 | /* Render it, and see if we're to stop running. */ 121 | if (nuklear_console_demo_render()) { 122 | glfwSetWindowShouldClose(win, GLFW_TRUE); 123 | } 124 | } 125 | nk_end(ctx); 126 | 127 | /* Draw */ 128 | glfwGetWindowSize(win, &width, &height); 129 | glViewport(0, 0, width, height); 130 | glClear(GL_COLOR_BUFFER_BIT); 131 | glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 132 | /* IMPORTANT: `nk_glfw_render` modifies some global OpenGL state 133 | * with blending, scissor, face culling, depth test and viewport and 134 | * defaults everything back into a default state. 135 | * Make sure to either a.) save and restore or b.) reset your own state after 136 | * rendering the UI. */ 137 | nk_glfw3_render(&glfw, NK_ANTI_ALIASING_ON, MAX_VERTEX_BUFFER, MAX_ELEMENT_BUFFER); 138 | glfwSwapBuffers(win); 139 | } 140 | 141 | nuklear_console_demo_free(); 142 | 143 | nk_glfw3_shutdown(&glfw); 144 | glfwTerminate(); 145 | return 0; 146 | } 147 | -------------------------------------------------------------------------------- /demo/pntr/.cmake/Findnuklear_gamepad.cmake: -------------------------------------------------------------------------------- 1 | # nuklear_gamepad 2 | include(FetchContent) 3 | FetchContent_Declare( 4 | nuklear_gamepad 5 | GIT_REPOSITORY https://github.com/RobLoach/nuklear_gamepad.git 6 | GIT_TAG 49c1cba 7 | ) 8 | FetchContent_MakeAvailable(nuklear_gamepad) 9 | -------------------------------------------------------------------------------- /demo/pntr/.cmake/Findpntr.cmake: -------------------------------------------------------------------------------- 1 | # pntr 2 | include(FetchContent) 3 | FetchContent_Declare( 4 | pntr 5 | GIT_REPOSITORY https://github.com/robloach/pntr.git 6 | GIT_TAG 6fff5d4 7 | ) 8 | FetchContent_MakeAvailable(pntr) 9 | -------------------------------------------------------------------------------- /demo/pntr/.cmake/Findpntr_app.cmake: -------------------------------------------------------------------------------- 1 | # pntr_app 2 | include(FetchContent) 3 | FetchContent_Declare( 4 | pntr_app 5 | GIT_REPOSITORY https://github.com/robloach/pntr_app.git 6 | GIT_TAG 19038c4 7 | ) 8 | FetchContent_MakeAvailable(pntr_app) 9 | -------------------------------------------------------------------------------- /demo/pntr/.cmake/Findpntr_app_starter.cmake: -------------------------------------------------------------------------------- 1 | find_package(pntr REQUIRED) 2 | find_package(pntr_app REQUIRED) 3 | 4 | list(APPEND LIBRARIES 5 | pntr 6 | pntr_app 7 | ) 8 | 9 | list(TRANSFORM SOURCES PREPEND ${PROJECT_SOURCE_DIR}/) 10 | 11 | # Resources 12 | if (EXISTS ${PROJECT_SOURCE_DIR}/resources) 13 | file(GLOB resources ${PROJECT_SOURCE_DIR}/resources/*) 14 | set(examples_resources) 15 | list(APPEND examples_resources ${resources}) 16 | file(COPY ${examples_resources} DESTINATION "resources/") 17 | set(EMSCRIPTEN_RESOURCES "--preload-file ${PROJECT_SOURCE_DIR}/resources@/resources") 18 | else() 19 | set(EMSCRIPTEN_RESOURCES "") 20 | endif() 21 | 22 | if (RAYLIB) 23 | find_package(raylib QUIET) 24 | set(project_name_raylib ${PROJECT_NAME}_raylib) 25 | 26 | add_executable(${project_name_raylib} 27 | ${SOURCES} 28 | ) 29 | 30 | target_link_libraries(${project_name_raylib} PUBLIC 31 | raylib_static 32 | ${LIBRARIES} 33 | ) 34 | 35 | # Platform Updates 36 | if (EMSCRIPTEN) 37 | set_target_properties(${project_name_raylib} PROPERTIES OUTPUT_NAME "index") 38 | set_target_properties(${project_name_raylib} PROPERTIES SUFFIX ".html") 39 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ASSERTIONS=1 -s WASM=1 -s ASYNCIFY --preload-file ${PROJECT_SOURCE_DIR}/resources@/resources --shell-file ${CMAKE_CURRENT_LIST_DIR}/shell.html") 40 | 41 | target_compile_definitions(${project_name_raylib} PUBLIC 42 | PLATFORM=Web 43 | ) 44 | else() 45 | set_property(TARGET ${project_name_raylib} PROPERTY C_STANDARD 99) 46 | endif() 47 | 48 | if (APPLE AND NOT EMSCRIPTEN) 49 | target_link_libraries(${project_name_raylib} PUBLIC "-framework IOKit") 50 | target_link_libraries(${project_name_raylib} PUBLIC "-framework Cocoa") 51 | target_link_libraries(${project_name_raylib} PUBLIC "-framework OpenGL") 52 | endif() 53 | 54 | target_compile_definitions(${project_name_raylib} PUBLIC 55 | PNTR_APP_RAYLIB 56 | ) 57 | endif() 58 | -------------------------------------------------------------------------------- /demo/pntr/.cmake/Findpntr_nuklear.cmake: -------------------------------------------------------------------------------- 1 | # pntr_nuklear 2 | include(FetchContent) 3 | FetchContent_Declare( 4 | pntr_nuklear 5 | GIT_REPOSITORY https://github.com/RobLoach/pntr_nuklear.git 6 | GIT_TAG d87c940 7 | ) 8 | FetchContent_MakeAvailable(pntr_nuklear) 9 | -------------------------------------------------------------------------------- /demo/pntr/.cmake/Findraylib.cmake: -------------------------------------------------------------------------------- 1 | # raylib 2 | include(FetchContent) 3 | FetchContent_Declare( 4 | raylib 5 | GIT_REPOSITORY https://github.com/raysan5/raylib.git 6 | GIT_TAG 5.5 7 | GIT_SHALLOW 1 8 | ) 9 | FetchContent_GetProperties(raylib) 10 | if (NOT raylib_POPULATED) 11 | set(FETCHCONTENT_QUIET NO) 12 | FetchContent_Populate(raylib) 13 | set(BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) 14 | set(BUILD_GAMES OFF CACHE BOOL "" FORCE) 15 | 16 | add_subdirectory(${raylib_SOURCE_DIR} ${raylib_BINARY_DIR}) 17 | endif() 18 | -------------------------------------------------------------------------------- /demo/pntr/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.11) 2 | project(nuklear_console_demo_pntr 3 | DESCRIPTION "nuklear_console_demo_pntr" 4 | HOMEPAGE_URL "https://github.com/robloach/nuklear_console" 5 | VERSION 0.0.1 6 | LANGUAGES C 7 | ) 8 | 9 | # CMAKE Modules 10 | set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/.cmake") 11 | 12 | # Targets 13 | option(RAYLIB "Build raylib" TRUE) 14 | 15 | # Sources 16 | set(SOURCES 17 | main.c 18 | ) 19 | 20 | # Libraries 21 | find_package(pntr_nuklear REQUIRED) 22 | list(APPEND LIBRARIES pntr_nuklear) 23 | 24 | find_package(nuklear_gamepad REQUIRED) 25 | list(APPEND LIBRARIES nuklear_gamepad) 26 | 27 | # Have pntr_app_starter build the project 28 | list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/.cmake") 29 | find_package(pntr_app_starter) 30 | -------------------------------------------------------------------------------- /demo/pntr/Makefile: -------------------------------------------------------------------------------- 1 | compile: build/Makefile 2 | cmake --build build 3 | 4 | build/Makefile: 5 | cmake -B build -S . 6 | 7 | test: compile 8 | ./build/nuklear_console_demo_pntr_raylib 9 | 10 | clean: 11 | rm -rf build 12 | -------------------------------------------------------------------------------- /demo/pntr/main.c: -------------------------------------------------------------------------------- 1 | #define PNTR_APP_IMPLEMENTATION 2 | #define PNTR_ENABLE_DEFAULT_FONT 3 | #define PNTR_ENABLE_MATH 4 | #include "pntr_app.h" 5 | 6 | #define NK_INCLUDE_DEFAULT_ALLOCATOR 7 | #define PNTR_NUKLEAR_IMPLEMENTATION 8 | #include "pntr_nuklear.h" 9 | 10 | #define NK_GAMEPAD_PNTR 11 | #define NK_GAMEPAD_IMPLEMENTATION 12 | #include "nuklear_gamepad.h" 13 | 14 | #define NK_CONSOLE_ENABLE_TINYDIR 15 | #include "../common/nuklear_console_demo.c" 16 | 17 | typedef struct AppData { 18 | pntr_font* font; 19 | struct nk_context* ctx; 20 | pntr_image* image; 21 | struct nk_console* console; 22 | } AppData; 23 | 24 | bool Init(pntr_app* app) { 25 | AppData* appData = pntr_load_memory(sizeof(AppData)); 26 | pntr_app_set_userdata(app, appData); 27 | 28 | // Load the default font 29 | appData->font = pntr_load_font_default(); 30 | appData->ctx = pntr_load_nuklear(appData->font); 31 | appData->image = pntr_load_image("resources/image.png"); 32 | 33 | // Initialize the Gamepads 34 | appData->console = nuklear_console_demo_init(appData->ctx, app, pntr_image_nk(appData->image)); 35 | 36 | return true; 37 | } 38 | 39 | bool Update(pntr_app* app, pntr_image* screen) { 40 | AppData* appData = (AppData*)pntr_app_userdata(app); 41 | struct nk_context* ctx = appData->ctx; 42 | 43 | // Update the pntr input state. 44 | pntr_nuklear_update(ctx, app); 45 | 46 | // Update the gamepad state 47 | nk_gamepad_update(nk_console_get_gamepads(console)); 48 | 49 | // Clear the background 50 | pntr_clear_background(screen, PNTR_BLACK); 51 | 52 | /* GUI */ 53 | int flags = NK_WINDOW_SCROLL_AUTO_HIDE | NK_WINDOW_TITLE; 54 | if (nk_begin(ctx, "nuklear_console", nk_rect(0, 0, screen->width, screen->height), flags)) { 55 | /* Render it, and see if we're to stop running. */ 56 | if (nuklear_console_demo_render()) { 57 | nk_end(ctx); 58 | return false; 59 | } 60 | } 61 | nk_end(ctx); 62 | 63 | // Draw it on the screen 64 | pntr_draw_nuklear(screen, ctx); 65 | 66 | return true; 67 | } 68 | 69 | void Close(pntr_app* app) { 70 | AppData* appData = (AppData*)pntr_app_userdata(app); 71 | 72 | nuklear_console_demo_free(); 73 | 74 | // Unload the font 75 | pntr_unload_font(appData->font); 76 | pntr_unload_image(appData->image); 77 | pntr_unload_nuklear(appData->ctx); 78 | 79 | pntr_unload_memory(appData); 80 | } 81 | 82 | pntr_app Main(int argc, char* argv[]) { 83 | return (pntr_app) { 84 | .width = 400, 85 | .height = 300, 86 | .title = "nuklear_console_demo_pntr", 87 | .init = Init, 88 | .update = Update, 89 | .close = Close, 90 | .fps = 60 91 | }; 92 | } 93 | -------------------------------------------------------------------------------- /demo/pntr/resources/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/nuklear_console/e6001e75d0f237356865bb4306cc97d174640d1d/demo/pntr/resources/image.png -------------------------------------------------------------------------------- /demo/raylib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.11) # FetchContent is available in 3.11+ 2 | project(nuklear_console_demo_raylib) 3 | 4 | # C 5 | set(CMAKE_C_STANDARD 99) 6 | set(CMAKE_C_STANDARD_REQUIRED ON) 7 | 8 | # raylib 9 | find_package(raylib QUIET) 10 | if (NOT raylib_FOUND) 11 | include(FetchContent) 12 | FetchContent_Declare( 13 | raylib 14 | GIT_REPOSITORY https://github.com/raysan5/raylib.git 15 | GIT_TAG 5.5 16 | ) 17 | FetchContent_GetProperties(raylib) 18 | if (NOT raylib_POPULATED) # Have we downloaded raylib yet? 19 | set(FETCHCONTENT_QUIET NO) 20 | FetchContent_Populate(raylib) 21 | set(BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) 22 | add_subdirectory(${raylib_SOURCE_DIR} ${raylib_BINARY_DIR}) 23 | endif() 24 | endif() 25 | 26 | # raylib_nuklear 27 | find_package(raylib_nuklear QUIET) 28 | if (NOT raylib_nuklear_FOUND) 29 | include(FetchContent) 30 | FetchContent_Declare( 31 | raylib_nuklear 32 | GIT_REPOSITORY https://github.com/RobLoach/raylib-nuklear.git 33 | GIT_TAG d2bd2b7 34 | ) 35 | FetchContent_GetProperties(raylib_nuklear) 36 | if (NOT raylib_nuklear_POPULATED) # Have we downloaded raylib yet? 37 | set(FETCHCONTENT_QUIET NO) 38 | FetchContent_Populate(raylib_nuklear) 39 | add_subdirectory(${raylib_nuklear_SOURCE_DIR} ${raylib_nuklear_BINARY_DIR}) 40 | endif() 41 | endif() 42 | 43 | # nuklear_gamepad 44 | find_package(nuklear_gamepad QUIET) 45 | if (NOT nuklear_gamepad_FOUND) 46 | include(FetchContent) 47 | FetchContent_Declare( 48 | nuklear_gamepad 49 | GIT_REPOSITORY https://github.com/RobLoach/nuklear_gamepad.git 50 | GIT_TAG 49c1cba 51 | ) 52 | FetchContent_GetProperties(nuklear_gamepad) 53 | if (NOT nuklear_gamepad_POPULATED) # Have we downloaded raylib yet? 54 | set(FETCHCONTENT_QUIET NO) 55 | FetchContent_Populate(nuklear_gamepad) 56 | add_subdirectory(${nuklear_gamepad_SOURCE_DIR} ${nuklear_gamepad_BINARY_DIR}) 57 | endif() 58 | endif() 59 | 60 | # Resources 61 | if (EXISTS ${PROJECT_SOURCE_DIR}/resources) 62 | file(GLOB resources ${PROJECT_SOURCE_DIR}/resources/*) 63 | set(examples_resources) 64 | list(APPEND examples_resources ${resources}) 65 | file(COPY ${examples_resources} DESTINATION "resources/") 66 | set(EMSCRIPTEN_RESOURCES "--preload-file ${PROJECT_SOURCE_DIR}/resources@/resources") 67 | else() 68 | set(EMSCRIPTEN_RESOURCES "") 69 | endif() 70 | 71 | # Setup the example 72 | add_executable(${PROJECT_NAME} main.c) 73 | 74 | # Link dependencies 75 | target_link_libraries(${PROJECT_NAME} PUBLIC 76 | raylib 77 | raylib_nuklear 78 | nuklear_gamepad 79 | ) 80 | 81 | # Web Configurations 82 | #if (${PLATFORM} STREQUAL "Web") 83 | if (EMSCRIPTEN) 84 | # Tell Emscripten to build an example.html file. 85 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -sUSE_GLFW=3") 86 | set_target_properties(${PROJECT_NAME} PROPERTIES SUFFIX ".html") 87 | set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME "index") 88 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --shell-file ${CMAKE_CURRENT_SOURCE_DIR}/shell.html") 89 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --preload-file resources") 90 | #set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s USE_GLFW=3") 91 | target_compile_definitions(${PROJECT_NAME} PUBLIC PLATFORM=Web) 92 | target_compile_definitions(${PROJECT_NAME} PUBLIC CMAKE_BUILD_TYPE=Release) 93 | endif() 94 | -------------------------------------------------------------------------------- /demo/raylib/Makefile: -------------------------------------------------------------------------------- 1 | compile: build/Makefile 2 | cmake --build build 3 | 4 | build/Makefile: 5 | cmake -B build -S . 6 | 7 | test: compile 8 | ./build/nuklear_console_demo_raylib 9 | 10 | clean: 11 | rm -rf build 12 | 13 | web: 14 | emcmake cmake -B build -S . -DPLATFORM=Web 15 | emmake make -C build 16 | -------------------------------------------------------------------------------- /demo/raylib/main.c: -------------------------------------------------------------------------------- 1 | #ifdef PLATFORM_WEB 2 | #include 3 | #endif 4 | 5 | #include "raylib.h" 6 | 7 | #define RAYLIB_NUKLEAR_IMPLEMENTATION 8 | #define RAYLIB_NUKLEAR_INCLUDE_DEFAULT_FONT 9 | #define NK_INCLUDE_DEFAULT_ALLOCATOR 10 | #include "raylib-nuklear.h" 11 | 12 | #include "../common/nuklear_console_demo.c" 13 | 14 | void UpdateDrawFrame(void); 15 | 16 | struct nk_context *ctx; 17 | nk_bool closeWindow = nk_false; 18 | 19 | int main() { 20 | SetConfigFlags(FLAG_WINDOW_RESIZABLE); 21 | InitWindow(800, 600, "nuklear_console_demo"); 22 | SetWindowMinSize(200, 200); 23 | 24 | // Create the Nuklear Context 25 | int fontSize = 13 * 3; 26 | Font font = LoadFontFromNuklear(fontSize); 27 | GenTextureMipmaps(&font.texture); 28 | ctx = InitNuklearEx(font, fontSize); 29 | Texture texture = LoadTexture("resources/image.png"); 30 | 31 | console = nuklear_console_demo_init(ctx, NULL, TextureToNuklear(texture)); 32 | 33 | #if defined(PLATFORM_WEB) 34 | emscripten_set_main_loop(UpdateDrawFrame, 0, 1); 35 | #else 36 | while (!WindowShouldClose()) { 37 | UpdateDrawFrame(); 38 | 39 | if (closeWindow) { 40 | break; 41 | } 42 | } 43 | #endif 44 | 45 | // De-initialize the Nuklear GUI 46 | UnloadTexture(texture); 47 | nuklear_console_demo_free(); 48 | UnloadNuklear(ctx); 49 | UnloadFont(font); 50 | 51 | CloseWindow(); 52 | return 0; 53 | } 54 | 55 | void UpdateDrawFrame(void) { 56 | // Update the Nuklear context, along with input 57 | UpdateNuklear(ctx); 58 | 59 | nk_gamepad_update(nk_console_get_gamepads(console)); 60 | 61 | int flags = NK_WINDOW_SCROLL_AUTO_HIDE | NK_WINDOW_TITLE; 62 | int padding = 0; 63 | 64 | // Nuklear GUI Code 65 | if (nk_begin(ctx, "nuklear_console", nk_rect(padding, padding, GetScreenWidth() - padding * 2, GetScreenHeight() - padding * 2), flags)) { 66 | if (nuklear_console_demo_render()) { 67 | closeWindow = nk_true; 68 | } 69 | } 70 | nk_end(ctx); 71 | 72 | // Render 73 | BeginDrawing(); 74 | ClearBackground(BLACK); 75 | 76 | // Render the Nuklear GUI 77 | DrawNuklear(ctx); 78 | 79 | EndDrawing(); 80 | 81 | #ifdef PLATFORM_WEB 82 | if (shouldClose) { 83 | emscripten_cancel_main_loop(); 84 | } 85 | #endif 86 | } 87 | -------------------------------------------------------------------------------- /demo/raylib/resources/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/nuklear_console/e6001e75d0f237356865bb4306cc97d174640d1d/demo/raylib/resources/image.png -------------------------------------------------------------------------------- /demo/raylib/shell.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | nuklear_console: Demo 7 | 8 | 9 | 10 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 51 | {{{ SCRIPT }}} 52 | 53 | 54 | -------------------------------------------------------------------------------- /demo/sdl_renderer/Makefile: -------------------------------------------------------------------------------- 1 | # Install 2 | BIN = nuklear_console_demo_sdl 3 | 4 | # Flags 5 | 6 | # Use C++ 7 | CFLAGS += -std=c99 -pedantic -O0 8 | CXXFLAGS += -pedantic -O0 -fpermissive -std=c++17 9 | 10 | CFLAGS += `sdl2-config --cflags` 11 | CXXFLAGS += `sdl2-config --cflags` 12 | 13 | SRC = main.c 14 | OBJ = $(SRC:.c=.o) 15 | 16 | ifeq ($(OS),Windows_NT) 17 | #TODO 18 | #BIN := $(BIN).exe 19 | #LIBS = -lmingw32 -lSDL2main -lSDL2 -lopengl32 -lm -lGLU32 20 | else 21 | UNAME_S := $(shell uname -s) 22 | ifeq ($(UNAME_S),Darwin) 23 | #TODO LIBS = -lSDL2 -framework OpenGL -lm 24 | else 25 | LIBS += -lm -ldl `sdl2-config --libs` 26 | endif 27 | endif 28 | 29 | $(BIN): clean 30 | $(CC) $(SRC) $(CFLAGS) -o $(BIN) $(LIBS) 31 | 32 | clean: 33 | rm -rf $(BIN) $(OBJS) 34 | 35 | test: $(BIN) 36 | ./$(BIN) 37 | -------------------------------------------------------------------------------- /demo/sdl_renderer/image.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/nuklear_console/e6001e75d0f237356865bb4306cc97d174640d1d/demo/sdl_renderer/image.bmp -------------------------------------------------------------------------------- /demo/sdl_renderer/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #define NK_INCLUDE_FIXED_TYPES 14 | #define NK_INCLUDE_STANDARD_IO 15 | #define NK_INCLUDE_STANDARD_VARARGS 16 | #define NK_INCLUDE_DEFAULT_ALLOCATOR 17 | #define NK_INCLUDE_VERTEX_BUFFER_OUTPUT 18 | #define NK_INCLUDE_FONT_BAKING 19 | #define NK_INCLUDE_DEFAULT_FONT 20 | #define NK_INCLUDE_COMMAND_USERDATA 21 | #define NK_IMPLEMENTATION 22 | #define NK_SDL_RENDERER_IMPLEMENTATION 23 | #include "../../vendor/Nuklear/nuklear.h" 24 | #include "../../vendor/Nuklear/demo/sdl_renderer/nuklear_sdl_renderer.h" 25 | 26 | #define WINDOW_WIDTH 800 27 | #define WINDOW_HEIGHT 600 28 | 29 | #include "../common/nuklear_console_demo.c" 30 | 31 | int main(int argc, char *argv[]) { 32 | NK_UNUSED(argc); 33 | NK_UNUSED(argv); 34 | /* Platform */ 35 | SDL_Window *win; 36 | SDL_Renderer *renderer; 37 | int running = 1; 38 | int flags = 0; 39 | float font_scale = 3; 40 | 41 | /* GUI */ 42 | struct nk_context *ctx; 43 | 44 | /* SDL setup */ 45 | SDL_SetHint(SDL_HINT_VIDEO_HIGHDPI_DISABLED, "0"); 46 | SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_GAMECONTROLLER); 47 | 48 | win = SDL_CreateWindow("nuklear_console_demo", 49 | SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 50 | WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_SHOWN|SDL_WINDOW_ALLOW_HIGHDPI); 51 | 52 | if (win == NULL) { 53 | SDL_Log("Error SDL_CreateWindow %s", SDL_GetError()); 54 | return 1; 55 | } 56 | 57 | //flags |= SDL_RENDERER_ACCELERATED; 58 | //flags |= SDL_RENDERER_PRESENTVSYNC; 59 | 60 | #if 0 61 | SDL_SetHint(SDL_HINT_RENDER_BATCHING, "1"); 62 | SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software"); 63 | SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl"); 64 | SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software"); 65 | SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengles2"); 66 | #endif 67 | 68 | renderer = SDL_CreateRenderer(win, -1, flags); 69 | 70 | if (renderer == NULL) { 71 | SDL_Log("Error SDL_CreateRenderer %s", SDL_GetError()); 72 | return 1; 73 | } 74 | 75 | /* GUI */ 76 | ctx = nk_sdl_init(win, renderer); 77 | { 78 | struct nk_font_atlas *atlas; 79 | struct nk_font_config config = nk_font_config(0); 80 | struct nk_font *font; 81 | 82 | nk_sdl_font_stash_begin(&atlas); 83 | font = nk_font_atlas_add_default(atlas, 13 * font_scale, &config); 84 | nk_sdl_font_stash_end(); 85 | nk_style_set_font(ctx, &font->handle); 86 | } 87 | 88 | // Attempt to load the sample image. 89 | struct nk_image img = nk_image_id(0); 90 | SDL_Surface *surface = SDL_LoadBMP("image.bmp"); 91 | SDL_Texture* texture = NULL; 92 | if (surface != NULL) { 93 | texture = SDL_CreateTextureFromSurface(renderer, surface); 94 | if (texture != NULL) { 95 | img = nk_image_ptr(texture); 96 | img.w = surface->w; 97 | img.h = surface->h; 98 | img.region[0] = 0; 99 | img.region[1] = 0; 100 | img.region[2] = surface->w; 101 | img.region[3] = surface->h; 102 | } 103 | SDL_FreeSurface(surface); 104 | } 105 | 106 | nk_console* console = nuklear_console_demo_init(ctx, NULL, img); 107 | 108 | while (running) { 109 | /* Input */ 110 | SDL_Event evt; 111 | nk_input_begin(ctx); 112 | nk_gamepad_update(nk_console_get_gamepads(console)); 113 | while (SDL_PollEvent(&evt)) { 114 | if (evt.type == SDL_QUIT) goto cleanup; 115 | if (evt.type == SDL_KEYUP && evt.key.keysym.scancode == SDL_SCANCODE_ESCAPE) running = 0; 116 | 117 | nk_sdl_handle_event(&evt); 118 | nk_gamepad_sdl_handle_event(nk_console_get_gamepads(console), &evt); 119 | } 120 | nk_input_end(ctx); 121 | 122 | int flags = NK_WINDOW_SCROLL_AUTO_HIDE | NK_WINDOW_TITLE; 123 | 124 | /* GUI */ 125 | if (nk_begin(ctx, "nuklear_console", nk_rect(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT), flags)) { 126 | /* Render it, and see if we're to stop running. */ 127 | if (nuklear_console_demo_render()) { 128 | running = 0; 129 | } 130 | } 131 | nk_end(ctx); 132 | 133 | SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); 134 | SDL_RenderClear(renderer); 135 | 136 | nk_sdl_render(NK_ANTI_ALIASING_ON); 137 | 138 | SDL_RenderPresent(renderer); 139 | } 140 | 141 | cleanup: 142 | if (texture != NULL) { 143 | SDL_DestroyTexture(texture); 144 | } 145 | nuklear_console_demo_free(); 146 | nk_sdl_shutdown(); 147 | SDL_DestroyRenderer(renderer); 148 | SDL_DestroyWindow(win); 149 | SDL_Quit(); 150 | 151 | return 0; 152 | } 153 | -------------------------------------------------------------------------------- /nuklear_console.hpp: -------------------------------------------------------------------------------- 1 | #ifndef NK_CONSOLE_HPP__ 2 | #define NK_CONSOLE_HPP__ 3 | 4 | #include 5 | 6 | template 7 | void nk_console_event_handler_call(nk_console* widget, void* user_data) { 8 | T* t = reinterpret_cast(user_data); 9 | (*t)(widget); 10 | } 11 | 12 | template 13 | void nk_console_event_handler_destroy(nk_console*, void* user_data) { 14 | T* t = reinterpret_cast(user_data); 15 | t->~T(); 16 | nk_console_mfree(nk_handle_id(0), user_data); 17 | } 18 | 19 | template 20 | void nk_console_add_event_handler(nk_console* widget, nk_console_event_type type, T&& t) { 21 | void* memory = nk_console_malloc(nk_handle_id(0), NULL, sizeof(T)); 22 | T* user_data = new (memory) T(std::move(t)); 23 | nk_console_add_event_handler(widget, type, &nk_console_event_handler_call, user_data, &nk_console_event_handler_destroy); 24 | } 25 | 26 | template 27 | nk_console* nk_console_button_onclick_handler(nk_console* parent, const char* text, T&& t) { 28 | nk_console* button = nk_console_button(parent, text); 29 | nk_console_add_event_handler(button, NK_CONSOLE_EVENT_CLICKED, std::move(t)); 30 | return button; 31 | } 32 | 33 | #endif // NK_CONSOLE_HPP__ 34 | -------------------------------------------------------------------------------- /nuklear_console_button.h: -------------------------------------------------------------------------------- 1 | #ifndef NK_CONSOLE_BUTTON_H__ 2 | #define NK_CONSOLE_BUTTON_H__ 3 | 4 | typedef struct nk_console_button_data { 5 | enum nk_symbol_type symbol; 6 | struct nk_image image; 7 | } nk_console_button_data; 8 | 9 | #if defined(__cplusplus) 10 | extern "C" { 11 | #endif 12 | 13 | NK_API nk_console* nk_console_button(nk_console* parent, const char* text); 14 | NK_API struct nk_rect nk_console_button_render(nk_console* console); 15 | NK_API void nk_console_button_back(nk_console* button, void* user_data); 16 | NK_API nk_console* nk_console_button_onclick(nk_console* parent, const char* text, nk_console_event onclick); 17 | NK_API nk_console* nk_console_button_onclick_handler(nk_console* parent, const char* text, nk_console_event callback, void* data, nk_console_event destructor); 18 | 19 | NK_API enum nk_symbol_type nk_console_button_get_symbol(nk_console* button); 20 | NK_API void nk_console_button_set_symbol(nk_console* button, enum nk_symbol_type symbol); 21 | 22 | NK_API void nk_console_button_set_image(nk_console* button, struct nk_image image); 23 | NK_API struct nk_image nk_console_button_get_image(nk_console* button); 24 | 25 | #if defined(__cplusplus) 26 | } 27 | #endif 28 | 29 | #endif // NK_CONSOLE_BUTTON_H__ 30 | 31 | #if defined(NK_CONSOLE_IMPLEMENTATION) && !defined(NK_CONSOLE_HEADER_ONLY) 32 | #ifndef NK_CONSOLE_BUTTON_IMPLEMENTATION_ONCE 33 | #define NK_CONSOLE_BUTTON_IMPLEMENTATION_ONCE 34 | 35 | #if defined(__cplusplus) 36 | extern "C" { 37 | #endif 38 | 39 | NK_API enum nk_symbol_type nk_console_button_get_symbol(nk_console* button) { 40 | if (button == NULL || button->data == NULL) { 41 | return NK_SYMBOL_NONE; 42 | } 43 | nk_console_button_data* data = (nk_console_button_data*)button->data; 44 | return data->symbol; 45 | } 46 | 47 | NK_API void nk_console_button_set_symbol(nk_console* button, enum nk_symbol_type symbol) { 48 | if (button == NULL || button->data == NULL) { 49 | return; 50 | } 51 | nk_console_button_data* data = (nk_console_button_data*)button->data; 52 | data->symbol = symbol; 53 | } 54 | 55 | NK_API void nk_console_button_set_image(nk_console* button, struct nk_image image) { 56 | if (button == NULL || button->data == NULL) { 57 | return; 58 | } 59 | nk_console_button_data* data = (nk_console_button_data*)button->data; 60 | data->image = image; 61 | 62 | // While automatically setting the height to the button height is an option here, we will opt out of doing that. 63 | // button->height = (int)image.h; 64 | } 65 | 66 | NK_API struct nk_image nk_console_button_get_image(nk_console* button) { 67 | if (button == NULL || button->data == NULL) { 68 | struct nk_image output = {0}; 69 | return output; 70 | } 71 | nk_console_button_data* data = (nk_console_button_data*)button->data; 72 | return data->image; 73 | } 74 | 75 | NK_API struct nk_rect nk_console_button_render(nk_console* console) { 76 | nk_console_button_data* data = (nk_console_button_data*)console->data; 77 | if (data == NULL) { 78 | return nk_rect(0, 0, 0, 0); 79 | } 80 | 81 | nk_console* top = nk_console_get_top(console); 82 | nk_console_top_data* top_data = (nk_console_top_data*)top->data; 83 | 84 | nk_console_layout_widget(console); 85 | 86 | struct nk_rect widget_bounds = nk_layout_widget_bounds(console->ctx); 87 | 88 | if (console->disabled) { 89 | nk_widget_disable_begin(console->ctx); 90 | } 91 | 92 | // Check the button state. 93 | nk_bool selected = nk_false; 94 | if (!console->disabled && nk_console_is_active_widget(console) && !top_data->input_processed && nk_console_button_pushed(top, NK_GAMEPAD_BUTTON_A)) { 95 | selected = nk_true; 96 | } 97 | 98 | // Apply the style. 99 | struct nk_style_item buttonStyle = console->ctx->style.button.normal; 100 | struct nk_color textColor = console->ctx->style.button.text_normal; 101 | if (nk_console_is_active_widget(console)) { 102 | if (selected) { 103 | console->ctx->style.button.normal = console->ctx->style.button.active; 104 | console->ctx->style.button.text_normal = console->ctx->style.button.text_active; 105 | } 106 | else { 107 | console->ctx->style.button.normal = console->ctx->style.button.hover; 108 | console->ctx->style.button.text_normal = console->ctx->style.button.text_hover; 109 | } 110 | } 111 | 112 | // Display the button. 113 | if (data->image.region[3] == 0) { 114 | // No image 115 | if (console->label_length <= 0) { 116 | // Check if there is a Label 117 | if (console->label != NULL && nk_strlen(console->label) > 0) { 118 | if (data->symbol == NK_SYMBOL_NONE) { 119 | selected |= nk_button_label(console->ctx, console->label); 120 | } 121 | else { 122 | selected |= nk_button_symbol_label(console->ctx, data->symbol, console->label, console->alignment); 123 | } 124 | } 125 | else { 126 | // Display the button as just a symbol? 127 | selected |= nk_button_symbol(console->ctx, data->symbol); 128 | } 129 | } 130 | else { 131 | if (data->symbol == NK_SYMBOL_NONE) { 132 | selected |= nk_button_text(console->ctx, console->label, console->label_length); 133 | } 134 | else { 135 | selected |= nk_button_symbol_text(console->ctx, data->symbol, console->label, console->label_length, console->alignment); 136 | } 137 | } 138 | } 139 | else { 140 | // Display the button with an image 141 | if (console->label_length > 0) { 142 | selected |= nk_button_image_text(console->ctx, data->image, console->label, console->label_length, console->alignment); 143 | } 144 | else if (console->label != NULL && nk_strlen(console->label) > 0) { 145 | selected |= nk_button_image_label(console->ctx, data->image, console->label, console->alignment); 146 | } 147 | else { 148 | selected |= nk_button_image(console->ctx, data->image); 149 | } 150 | } 151 | 152 | // Restore the styles 153 | console->ctx->style.button.normal = buttonStyle; 154 | console->ctx->style.button.text_normal = textColor; 155 | 156 | // Act on the button 157 | if (selected) { 158 | top_data->input_processed = nk_true; 159 | 160 | // Trigger the click event. If no event was invoked, switch the parent. 161 | if (nk_console_trigger_event(console, NK_CONSOLE_EVENT_CLICKED) == nk_false) { 162 | if (console->children != NULL) { 163 | nk_console_set_active_parent(console); 164 | } 165 | } 166 | } 167 | 168 | if (console->disabled) { 169 | nk_widget_disable_end(console->ctx); 170 | } 171 | 172 | // Allow switching up/down in widgets 173 | if (nk_console_is_active_widget(console)) { 174 | nk_console_check_up_down(console, widget_bounds); 175 | nk_console_check_tooltip(console); 176 | } 177 | 178 | return widget_bounds; 179 | } 180 | 181 | /** 182 | * Create a button. 183 | */ 184 | NK_API nk_console* nk_console_button(nk_console* parent, const char* text) { 185 | // Create the widget data. 186 | nk_console_button_data* data = (nk_console_button_data*)NK_CONSOLE_MALLOC(nk_handle_id(0), NULL, sizeof(nk_console_button_data)); 187 | nk_zero(data, sizeof(nk_console_button_data)); 188 | 189 | nk_console* button = nk_console_label(parent, text); 190 | button->type = NK_CONSOLE_BUTTON; 191 | button->data = (void*)data; 192 | button->selectable = nk_true; 193 | button->columns = 1; 194 | button->render = nk_console_button_render; 195 | return button; 196 | } 197 | 198 | /** 199 | * Take action on a BACK button. 200 | */ 201 | NK_API void nk_console_button_back(nk_console* button, void* user_data) { 202 | NK_UNUSED(user_data); 203 | if (button == NULL) { 204 | return; 205 | } 206 | 207 | nk_console* top = nk_console_get_top(button); 208 | nk_console_top_data* data = (nk_console_top_data*)top->data; 209 | 210 | nk_console* parent = button->parent; 211 | if (parent != NULL) { 212 | parent = parent->parent; 213 | } 214 | if (parent != NULL) { 215 | nk_console_set_active_parent(parent); 216 | } 217 | else { 218 | data->active_parent = top; 219 | } 220 | } 221 | 222 | NK_API nk_console* nk_console_button_onclick(nk_console* parent, const char* text, nk_console_event onclick) { 223 | nk_console* button = nk_console_button(parent, text); 224 | nk_console_add_event(button, NK_CONSOLE_EVENT_CLICKED, onclick); 225 | return button; 226 | } 227 | 228 | NK_API nk_console* nk_console_button_onclick_handler(nk_console* parent, const char* text, nk_console_event callback, void* data, nk_console_event destructor) { 229 | nk_console* button = nk_console_button(parent, text); 230 | nk_console_add_event_handler(button, NK_CONSOLE_EVENT_CLICKED, callback, data, destructor); 231 | return button; 232 | } 233 | 234 | #if defined(__cplusplus) 235 | } 236 | #endif 237 | 238 | #endif // NK_CONSOLE_BUTTON_IMPLEMENTATION_ONCE 239 | #endif // NK_CONSOLE_IMPLEMENTATION 240 | -------------------------------------------------------------------------------- /nuklear_console_checkbox.h: -------------------------------------------------------------------------------- 1 | #ifndef NK_CONSOLE_CHECKBOX_H__ 2 | #define NK_CONSOLE_CHECKBOX_H__ 3 | 4 | typedef struct nk_console_checkbox_data { 5 | nk_bool* value_bool; 6 | } nk_console_checkbox_data; 7 | 8 | #if defined(__cplusplus) 9 | extern "C" { 10 | #endif 11 | 12 | NK_API nk_console* nk_console_checkbox(nk_console* parent, const char* text, nk_bool* active); 13 | NK_API struct nk_rect nk_console_checkbox_render(nk_console* console); 14 | 15 | #if defined(__cplusplus) 16 | } 17 | #endif 18 | 19 | 20 | #endif // NK_CONSOLE_CHECKBOX_H__ 21 | 22 | #if defined(NK_CONSOLE_IMPLEMENTATION) && !defined(NK_CONSOLE_HEADER_ONLY) 23 | #ifndef NK_CONSOLE_CHECKBOX_IMPLEMENTATION_ONCE 24 | #define NK_CONSOLE_CHECKBOX_IMPLEMENTATION_ONCE 25 | 26 | #if defined(__cplusplus) 27 | extern "C" { 28 | #endif 29 | 30 | NK_API struct nk_rect nk_console_checkbox_render(nk_console* console) { 31 | nk_console_checkbox_data* data = (nk_console_checkbox_data*)console->data; 32 | if (data == NULL) { 33 | return nk_rect(0, 0, 0, 0); 34 | } 35 | 36 | nk_console_layout_widget(console); 37 | 38 | struct nk_rect widget_bounds = nk_layout_widget_bounds(console->ctx); 39 | nk_console* top = nk_console_get_top(console); 40 | nk_console_top_data* top_data = (nk_console_top_data*)top->data; 41 | 42 | // Allow changing the checkbox value. 43 | nk_bool active = nk_false; 44 | if (!console->disabled && nk_console_is_active_widget(console) && !top_data->input_processed) { 45 | if (nk_console_button_pushed(top, NK_GAMEPAD_BUTTON_A)) { 46 | if (data->value_bool != NULL) { 47 | *data->value_bool = !*data->value_bool; 48 | nk_console_trigger_event(console, NK_CONSOLE_EVENT_CHANGED); 49 | } 50 | active = nk_true; 51 | top_data->input_processed = nk_true; 52 | } 53 | else if (nk_console_button_pushed(top, NK_GAMEPAD_BUTTON_LEFT)) { 54 | if (data->value_bool != NULL) { 55 | *data->value_bool = nk_false; 56 | nk_console_trigger_event(console, NK_CONSOLE_EVENT_CHANGED); 57 | } 58 | active = nk_true; 59 | top_data->input_processed = nk_true; 60 | } 61 | else if (nk_console_button_pushed(top, NK_GAMEPAD_BUTTON_RIGHT)) { 62 | if (data->value_bool != NULL) { 63 | *data->value_bool = nk_true; 64 | nk_console_trigger_event(console, NK_CONSOLE_EVENT_CHANGED); 65 | } 66 | active = nk_true; 67 | top_data->input_processed = nk_true; 68 | } 69 | } 70 | 71 | // Style 72 | struct nk_style_item checkboxStyle = console->ctx->style.checkbox.normal; 73 | if (nk_console_is_active_widget(console)) { 74 | if (active) { 75 | console->ctx->style.checkbox.normal = console->ctx->style.checkbox.active; 76 | } 77 | else { 78 | console->ctx->style.checkbox.normal = console->ctx->style.checkbox.hover; 79 | } 80 | } 81 | 82 | if (console->disabled || !nk_console_is_active_widget(console)) { 83 | nk_widget_disable_begin(console->ctx); 84 | } 85 | 86 | // Display the checkbox with fixed alignment. 87 | nk_bool changed = nk_false; 88 | if (console->alignment == NK_TEXT_LEFT) { 89 | changed = nk_checkbox_label_align(console->ctx, console->label, data->value_bool, NK_TEXT_RIGHT, NK_TEXT_LEFT); 90 | } 91 | else { 92 | changed = nk_checkbox_label(console->ctx, console->label, data->value_bool); 93 | } 94 | 95 | // Invoke onchanged event. 96 | if (changed) { 97 | nk_console_trigger_event(console, NK_CONSOLE_EVENT_CHANGED); 98 | } 99 | 100 | if (console->disabled || !nk_console_is_active_widget(console)) { 101 | nk_widget_disable_end(console->ctx); 102 | } 103 | 104 | // Restore the styles 105 | console->ctx->style.checkbox.normal = checkboxStyle; 106 | 107 | // Allow switching up/down in widgets 108 | if (nk_console_is_active_widget(console)) { 109 | nk_console_check_up_down(console, widget_bounds); 110 | nk_console_check_tooltip(console); 111 | } 112 | 113 | return widget_bounds; 114 | } 115 | 116 | NK_API nk_console* nk_console_checkbox(nk_console* parent, const char* text, nk_bool* active) { 117 | NK_ASSERT(active != NULL); 118 | nk_console_checkbox_data* data = (nk_console_checkbox_data*)NK_CONSOLE_MALLOC(nk_handle_id(0), NULL, sizeof(nk_console_checkbox_data)); 119 | nk_zero(data, sizeof(nk_console_checkbox_data)); 120 | 121 | nk_console* checkbox = nk_console_label(parent, text); 122 | checkbox->render = nk_console_checkbox_render; 123 | data->value_bool = active; 124 | checkbox->type = NK_CONSOLE_CHECKBOX; 125 | checkbox->selectable = nk_true; 126 | checkbox->columns = 1; 127 | checkbox->data = (void*)data; 128 | return checkbox; 129 | } 130 | 131 | #if defined(__cplusplus) 132 | } 133 | #endif 134 | 135 | #endif // NK_CONSOLE_CHECKBOX_IMPLEMENTATION_ONCE 136 | #endif // NK_CONSOLE_IMPLEMENTATION 137 | -------------------------------------------------------------------------------- /nuklear_console_color.h: -------------------------------------------------------------------------------- 1 | #ifndef NK_CONSOLE_COLOR_H__ 2 | #define NK_CONSOLE_COLOR_H__ 3 | 4 | typedef struct nk_console_color_data { 5 | struct nk_console_button_data button; /** Inherited from button */ 6 | struct nk_colorf* color; /** The color which will be manipulated by the user. */ 7 | } nk_console_color_data; 8 | 9 | #if defined(__cplusplus) 10 | extern "C" { 11 | #endif 12 | 13 | /** 14 | * Create a color picker widget. 15 | * 16 | * @param parent The parent widget. 17 | * @param label The label for the widget 18 | * @param color A pointer to where the color should be held. 19 | * @param format Whether the color should be in RGB or RGBA format. 20 | * 21 | * @return The created widget. 22 | */ 23 | NK_API nk_console* nk_console_color(nk_console* parent, const char* label, struct nk_colorf* color, enum nk_color_format format); 24 | 25 | /** 26 | * Render callback to display a color widget. 27 | */ 28 | NK_API struct nk_rect nk_console_color_render(nk_console* widget); 29 | 30 | #if defined(__cplusplus) 31 | } 32 | #endif 33 | 34 | #endif // NK_CONSOLE_COLOR_H__ 35 | 36 | #if defined(NK_CONSOLE_IMPLEMENTATION) && !defined(NK_CONSOLE_HEADER_ONLY) 37 | #ifndef NK_CONSOLE_COLOR_IMPLEMENTATION_ONCE 38 | #define NK_CONSOLE_COLOR_IMPLEMENTATION_ONCE 39 | 40 | #if defined(__cplusplus) 41 | extern "C" { 42 | #endif 43 | 44 | NK_API struct nk_rect nk_console_color_render(nk_console* console) { 45 | if (console == NULL || console->data == NULL) { 46 | return nk_rect(0, 0, 0, 0); 47 | } 48 | nk_console_color_data* data = (nk_console_color_data*)console->data; 49 | if (data->color == NULL) { 50 | return nk_rect(0, 0, 0, 0); 51 | } 52 | 53 | nk_console_layout_widget(console); 54 | 55 | // Display the label 56 | if (console->label != NULL && console->label[0] != '\0') { 57 | if (!nk_console_is_active_widget(console)) { 58 | nk_widget_disable_begin(console->ctx); 59 | } 60 | if (console->label_length > 0) { 61 | nk_text(console->ctx, console->label, console->label_length, NK_TEXT_LEFT); 62 | } 63 | else { 64 | nk_label(console->ctx, console->label, NK_TEXT_LEFT); 65 | } 66 | if (!nk_console_is_active_widget(console)) { 67 | nk_widget_disable_end(console->ctx); 68 | } 69 | } 70 | 71 | // Swap the colors for the button 72 | struct nk_color current_color = nk_rgb_cf(*data->color); 73 | struct nk_color swap_text_normal = console->ctx->style.button.text_normal; 74 | struct nk_color swap_text_active = console->ctx->style.button.text_active; 75 | struct nk_color swap_text_hover = console->ctx->style.button.text_hover; 76 | struct nk_color swap_text_background = console->ctx->style.button.text_background; 77 | console->ctx->style.button.text_normal = current_color; 78 | console->ctx->style.button.text_active = current_color; 79 | console->ctx->style.button.text_hover = current_color; 80 | console->ctx->style.button.text_background = current_color; 81 | 82 | // Display the mocked button 83 | int swap_columns = console->columns; 84 | const char* swap_label = console->label; 85 | int swap_label_length = console->label_length; 86 | console->columns = 0; 87 | console->label = NULL; 88 | console->label_length = 0; 89 | struct nk_rect widget_bounds = nk_console_button_render(console); 90 | console->columns = swap_columns; 91 | console->label = swap_label; 92 | console->label_length = swap_label_length; 93 | 94 | console->ctx->style.button.text_normal = swap_text_normal; 95 | console->ctx->style.button.text_active = swap_text_active; 96 | console->ctx->style.button.text_hover = swap_text_hover; 97 | console->ctx->style.button.text_background = swap_text_background; 98 | 99 | return widget_bounds; 100 | } 101 | 102 | static void nk_console_color_event_changed(nk_console* property, void* user_data) { 103 | if (property == NULL || property->parent == NULL) { 104 | return; 105 | } 106 | NK_UNUSED(user_data); 107 | nk_console_trigger_event(property->parent, NK_CONSOLE_EVENT_CHANGED); 108 | } 109 | 110 | NK_API nk_console* nk_console_color(nk_console* parent, const char* label, struct nk_colorf* color, enum nk_color_format format) { 111 | if (parent == NULL || color == NULL) { 112 | return NULL; 113 | } 114 | 115 | // Create the widget data. 116 | nk_console_color_data* data = (nk_console_color_data*)NK_CONSOLE_MALLOC(nk_handle_id(0), NULL, sizeof(nk_console_color_data)); 117 | nk_zero(data, sizeof(nk_console_color_data)); 118 | data->color = color; 119 | 120 | nk_console* widget = nk_console_label(parent, label); 121 | widget->type = NK_CONSOLE_COLOR; 122 | widget->columns = label == NULL ? 1 : 2; 123 | widget->render = nk_console_color_render; 124 | widget->selectable = nk_true; 125 | widget->data = data; 126 | nk_console_button_set_symbol(widget, NK_SYMBOL_RECT_SOLID); 127 | 128 | // Active Color. 129 | if (label != NULL && nk_strlen(label) != 1 && label[0] != '@') { 130 | nk_console* color_display = nk_console_color(widget, "@", color, format); 131 | nk_console_add_event(color_display, NK_CONSOLE_EVENT_CLICKED, &nk_console_button_back); 132 | color_display->label = NULL; 133 | color_display->columns = 1; 134 | 135 | // Don't need any of the children. 136 | nk_console_free_children(color_display); 137 | } 138 | 139 | // Add the color components 140 | nk_console_add_event(nk_console_slider_float(widget, "Red", 0.0f, &color->r, 1.0f, 0.05f), NK_CONSOLE_EVENT_CHANGED, &nk_console_color_event_changed); 141 | nk_console_add_event(nk_console_slider_float(widget, "Green", 0.0f, &color->g, 1.0f, 0.05f), NK_CONSOLE_EVENT_CHANGED, &nk_console_color_event_changed); 142 | nk_console_add_event(nk_console_slider_float(widget, "Blue", 0.0f, &color->b, 1.0f, 0.05f), NK_CONSOLE_EVENT_CHANGED, &nk_console_color_event_changed); 143 | if (format == NK_RGBA) { 144 | nk_console_add_event(nk_console_slider_float(widget, "Alpha", 0.0f, &color->a, 1.0f, 0.05f), NK_CONSOLE_EVENT_CHANGED, &nk_console_color_event_changed); 145 | } 146 | 147 | // Back Button 148 | nk_console_button_onclick(widget, "Back", &nk_console_button_back); 149 | 150 | return widget; 151 | } 152 | 153 | #if defined(__cplusplus) 154 | } 155 | #endif 156 | 157 | #endif // NK_CONSOLE_COLOR_IMPLEMENTATION_ONCE 158 | #endif // NK_CONSOLE_IMPLEMENTATION 159 | -------------------------------------------------------------------------------- /nuklear_console_combobox.h: -------------------------------------------------------------------------------- 1 | #ifndef NK_CONSOLE_COMBOBOX_H__ 2 | #define NK_CONSOLE_COMBOBOX_H__ 3 | 4 | /** 5 | * Data for Combobox widgets. 6 | */ 7 | typedef struct nk_console_combobox_data { 8 | nk_console_button_data button; // Inherited from button. 9 | const char* label; 10 | const char* items_separated_by_separator; 11 | int separator; 12 | int* selected; 13 | int count; 14 | } nk_console_combobox_data; 15 | 16 | #if defined(__cplusplus) 17 | extern "C" { 18 | #endif 19 | 20 | NK_API nk_console* nk_console_combobox(nk_console* parent, const char* label, const char* items_separated_by_separator, int separator, int* selected); 21 | NK_API struct nk_rect nk_console_combobox_render(nk_console* console); 22 | NK_API void nk_console_combobox_button_click(nk_console* button, void* user_data); 23 | NK_API void nk_console_combobox_button_main_click(nk_console* button, void* user_data); 24 | 25 | #if defined(__cplusplus) 26 | } 27 | #endif 28 | 29 | #endif // NK_CONSOLE_COMBOBOX_H__ 30 | 31 | #if defined(NK_CONSOLE_IMPLEMENTATION) && !defined(NK_CONSOLE_HEADER_ONLY) 32 | #ifndef NK_CONSOLE_COMBOBOX_IMPLEMENTATION_ONCE 33 | #define NK_CONSOLE_COMBOBOX_IMPLEMENTATION_ONCE 34 | 35 | #if defined(__cplusplus) 36 | extern "C" { 37 | #endif 38 | 39 | /** 40 | * Handle the click event for combobox's children items. 41 | */ 42 | NK_API void nk_console_combobox_button_click(nk_console* button, void* user_data) { 43 | NK_UNUSED(user_data); 44 | nk_console* combobox = button->parent; 45 | nk_console_combobox_data* data = (nk_console_combobox_data*)combobox->data; 46 | 47 | // Find which option was selected. 48 | int selected = nk_console_get_widget_index(button); 49 | if (selected <= 0 || selected >= (int)cvector_size(combobox->children)) { 50 | nk_console_button_back(button, NULL); 51 | return; 52 | } 53 | 54 | // Update the active selected value. 55 | if (data->selected != NULL) { 56 | *data->selected = selected - 1; 57 | } 58 | 59 | // Change the combobox text that's displayed. 60 | combobox->label = button->label; 61 | combobox->label_length = button->label_length; 62 | 63 | // Go back 64 | nk_console_button_back(button, NULL); 65 | 66 | // Invoke the onchange callback. 67 | nk_console_trigger_event(combobox, NK_CONSOLE_EVENT_CHANGED); 68 | } 69 | 70 | /** 71 | * Handle the click event for the main button for the combobox. 72 | * 73 | * @see nk_console_combobox 74 | * @internal 75 | */ 76 | NK_API void nk_console_combobox_button_main_click(nk_console* button, void* user_data) { 77 | NK_UNUSED(user_data); 78 | nk_console_combobox_data* data = (nk_console_combobox_data*)button->data; 79 | int selected = data->selected == NULL ? 0 : *data->selected; 80 | if (button->children != NULL) { 81 | if ((int)cvector_size(button->children) > selected + 1) { 82 | nk_console_set_active_widget(button->children[selected + 1]); 83 | } 84 | } 85 | 86 | // Switch to show all the children. 87 | nk_console_set_active_parent(button); 88 | } 89 | 90 | NK_API nk_console* nk_console_combobox(nk_console* parent, const char* label, const char* items_separated_by_separator, int separator, int* selected) { 91 | // Create the widget data. 92 | nk_console_combobox_data* data = (nk_console_combobox_data*)NK_CONSOLE_MALLOC(nk_handle_id(0), NULL, sizeof(nk_console_combobox_data)); 93 | nk_zero(data, sizeof(nk_console_combobox_data)); 94 | 95 | nk_console* combobox = nk_console_label(parent, label); 96 | combobox->type = NK_CONSOLE_COMBOBOX; 97 | combobox->selectable = nk_true; 98 | data->items_separated_by_separator = items_separated_by_separator; 99 | data->separator = separator; 100 | data->selected = selected; 101 | data->label = label; 102 | combobox->columns = label != NULL ? 2 : 1; 103 | combobox->render = nk_console_combobox_render; 104 | combobox->data = data; 105 | 106 | nk_console_button_set_symbol(combobox, NK_SYMBOL_TRIANGLE_DOWN); 107 | nk_console_add_event(combobox, NK_CONSOLE_EVENT_CLICKED, nk_console_combobox_button_main_click); 108 | 109 | // Back button 110 | nk_console* backbutton = nk_console_button_onclick(combobox, label, &nk_console_combobox_button_click); 111 | nk_console_button_set_symbol(backbutton, NK_SYMBOL_TRIANGLE_UP); 112 | 113 | // Add all the sub-page buttons 114 | const char* button_text_start = items_separated_by_separator; 115 | int text_length = 0; 116 | for (int i = 0; items_separated_by_separator[i] != 0; i++) { 117 | text_length++; 118 | if (items_separated_by_separator[i] == (char)separator) { 119 | nk_console_button_onclick(combobox, button_text_start, &nk_console_combobox_button_click) 120 | ->label_length = text_length - 1; 121 | text_length = 0; 122 | button_text_start = items_separated_by_separator + i + 1; 123 | } 124 | } 125 | 126 | // Add the last item 127 | nk_console_button_onclick(combobox, button_text_start, &nk_console_combobox_button_click) 128 | ->label_length = text_length; 129 | 130 | if (selected != NULL) { 131 | if (*selected < 0) { 132 | *selected = 0; 133 | } 134 | else if (*selected >= (int)cvector_size(combobox->children) - 1) { 135 | *selected = (int)cvector_size(combobox->children) - 2; 136 | } 137 | 138 | combobox->label = combobox->children[*selected + 1]->label; 139 | combobox->label_length = combobox->children[*selected + 1]->label_length; 140 | } 141 | 142 | return combobox; 143 | } 144 | 145 | NK_API struct nk_rect nk_console_combobox_render(nk_console* console) { 146 | nk_console_combobox_data* data = (nk_console_combobox_data*)console->data; 147 | if (data == NULL) { 148 | return nk_rect(0, 0, 0, 0); 149 | } 150 | 151 | nk_console* top = nk_console_get_top(console); 152 | 153 | nk_console_layout_widget(console); 154 | 155 | // Allow changing the value with left/right 156 | if (!console->disabled && nk_console_is_active_widget(console)) { 157 | nk_console_top_data* top_data = (nk_console_top_data*)top->data; 158 | if (!top_data->input_processed && data->selected != NULL && console->children != NULL) { 159 | nk_bool changed = nk_false; 160 | if (nk_console_button_pushed(top, NK_GAMEPAD_BUTTON_LEFT) && *data->selected > 0) { 161 | *data->selected = *data->selected - 1; 162 | changed = nk_true; 163 | } 164 | else if (nk_console_button_pushed(top, NK_GAMEPAD_BUTTON_RIGHT) && *data->selected < (int)cvector_size(console->children) - 2) { 165 | *data->selected = *data->selected + 1; 166 | changed = nk_true; 167 | } 168 | 169 | if (changed) { 170 | top_data->input_processed = nk_true; 171 | console->label = console->children[*data->selected + 1]->label; 172 | console->label_length = console->children[*data->selected + 1]->label_length; 173 | nk_console_trigger_event(console, NK_CONSOLE_EVENT_CHANGED); 174 | } 175 | } 176 | } 177 | 178 | // Display the label 179 | if (nk_strlen(data->label) > 0) { 180 | if (!nk_console_is_active_widget(console)) { 181 | nk_widget_disable_begin(console->ctx); 182 | } 183 | nk_label(console->ctx, data->label, NK_TEXT_LEFT); 184 | if (!nk_console_is_active_widget(console)) { 185 | nk_widget_disable_end(console->ctx); 186 | } 187 | } 188 | 189 | // Display the mocked combobox button 190 | int swap_columns = console->columns; 191 | console->columns = 0; 192 | // console->type = NK_CONSOLE_BUTTON; 193 | if (nk_console_is_active_widget(console)) { 194 | nk_console_button_set_symbol(console, NK_SYMBOL_TRIANGLE_DOWN); 195 | } 196 | else { 197 | nk_console_button_set_symbol(console, NK_SYMBOL_NONE); 198 | } 199 | 200 | struct nk_rect widget_bounds = nk_console_button_render(console); 201 | console->columns = swap_columns; 202 | return widget_bounds; 203 | } 204 | 205 | #if defined(__cplusplus) 206 | } 207 | #endif 208 | 209 | #endif // NK_CONSOLE_COMBOBOX_IMPLEMENTATION_ONCE 210 | #endif // NK_CONSOLE_IMPLEMENTATION 211 | -------------------------------------------------------------------------------- /nuklear_console_file.h: -------------------------------------------------------------------------------- 1 | #ifndef NK_CONSOLE_FILE_H__ 2 | #define NK_CONSOLE_FILE_H__ 3 | 4 | #ifndef NK_CONSOLE_FILE_PATH_MAX 5 | #ifdef PATH_MAX 6 | #define NK_CONSOLE_FILE_PATH_MAX PATH_MAX 7 | #else 8 | #define NK_CONSOLE_FILE_PATH_MAX 4096 9 | #endif 10 | #endif // NK_CONSOLE_FILE_PATH_MAX 11 | 12 | /** 13 | * Custom data for the file widget. 14 | */ 15 | typedef struct nk_console_file_data { 16 | nk_console_button_data button; /** Inherited from Button */ 17 | char* file_path_buffer; /** The string buffer for the chosen file path. */ 18 | int file_path_buffer_size; /** The size of the buffer. */ 19 | char directory[NK_CONSOLE_FILE_PATH_MAX]; /** When selecting a file, this is the current directory. */ 20 | void* file_user_data; /** Custom user data for the file system. */ 21 | nk_bool select_directory; /** Flag indicating if we are selecting a directory. */ 22 | } nk_console_file_data; 23 | 24 | #if defined(__cplusplus) 25 | extern "C" { 26 | #endif 27 | 28 | /** 29 | * Creates a file widget that allows the user to select a file. 30 | * 31 | * @param parent The parent widget. 32 | * @param label The label for the file widget. For example: "Select a file". 33 | * @param file_path_buffer The buffer to store the file path. 34 | * @param file_path_buffer_size The size of the buffer. 35 | * 36 | * @return The new file widget. 37 | */ 38 | NK_API nk_console* nk_console_file(nk_console* parent, const char* label, char* file_path_buffer, int file_path_buffer_size); 39 | 40 | /** 41 | * Creates a directory widget that allows the user to select a directory. 42 | * 43 | * @param parent The parent widget. 44 | * @param label The label for the file widget. For example: "Select a file". 45 | * @param dir_buffer The buffer to store the file path. 46 | * @param dir_buffer_size The size of the buffer. 47 | * 48 | * @return The new file widget. 49 | */ 50 | NK_API nk_console* nk_console_dir(nk_console* parent, const char* label, char* dir_buffer, int dir_buffer_size); 51 | 52 | /** 53 | * Render callback to display the file widget. 54 | */ 55 | NK_API struct nk_rect nk_console_file_render(nk_console* widget); 56 | 57 | /** 58 | * Sets any custom user data specifically for the file widget. 59 | * 60 | * This can be helpful for storing additional file system data for the file widget. 61 | * 62 | * @param file The file widget. 63 | * @param user_data The custom user data. 64 | */ 65 | NK_API void nk_console_file_set_file_user_data(nk_console* file, void* user_data); 66 | 67 | /** 68 | * Gets the custom user data specifically for the file widget. 69 | * 70 | * @param file The file widget. 71 | * 72 | * @return The custom user data. 73 | */ 74 | NK_API void* nk_console_file_get_file_user_data(nk_console* file); 75 | 76 | /** 77 | * Add a individual file or directory to the given file widget as a child. 78 | * 79 | * This should be called from the file system callbacks. See `nuklear_console_file_system.h` for examples. 80 | * 81 | * @param parent The file widget. 82 | * @param path The path to the file or directory. 83 | * @param is_directory True if the path is a directory. False otherwise. 84 | * 85 | * @return The new button if it was successfully added, NULL otherwise. 86 | * 87 | * @see nk_console_file_destroy_tinydir() 88 | * @see nk_console_file_add_files_raylib() 89 | */ 90 | NK_API nk_console* nk_console_file_add_entry(nk_console* parent, const char* path, nk_bool is_directory); 91 | 92 | /** 93 | * Refreshes the file widget to display the contents of the current directory. 94 | * 95 | * @param widget The file widget to refresh. 96 | * 97 | * @see nk_console_file_data::directory 98 | */ 99 | NK_API void nk_console_file_refresh(nk_console* widget, void* user_data); 100 | 101 | #if defined(__cplusplus) 102 | } 103 | #endif 104 | 105 | #endif // NK_CONSOLE_FILE_H__ 106 | 107 | #if defined(NK_CONSOLE_IMPLEMENTATION) && !defined(NK_CONSOLE_HEADER_ONLY) 108 | #ifndef NK_CONSOLE_FILE_IMPLEMENTATION_ONCE 109 | #define NK_CONSOLE_FILE_IMPLEMENTATION_ONCE 110 | 111 | #include "nuklear_console_file_system.h" 112 | 113 | #if defined(__cplusplus) 114 | extern "C" { 115 | #endif 116 | 117 | /** 118 | * Gets the base name of a file path. 119 | */ 120 | static const char* nk_console_file_basename(const char* path) { 121 | if (path == NULL) { 122 | return NULL; 123 | } 124 | 125 | // TODO: Ensure UTF-8 compatibility. 126 | int len = nk_strlen(path); 127 | for (int i = len - 1; i > 0; i--) { 128 | if (path[i] == '\\' || path[i] == '/') { 129 | path = path + i + 1; 130 | break; 131 | } 132 | } 133 | 134 | return path; 135 | } 136 | 137 | NK_API struct nk_rect nk_console_file_render(nk_console* console) { 138 | if (console == NULL || console->data == NULL) { 139 | return nk_rect(0, 0, 0, 0); 140 | } 141 | nk_console_file_data* data = (nk_console_file_data*)console->data; 142 | 143 | nk_console_layout_widget(console); 144 | 145 | // Display the label 146 | if (console->label != NULL && console->label[0] != '\0') { 147 | if (!nk_console_is_active_widget(console)) { 148 | nk_widget_disable_begin(console->ctx); 149 | } 150 | if (console->label_length > 0) { 151 | nk_text(console->ctx, console->label, console->label_length, NK_TEXT_LEFT); 152 | } 153 | else { 154 | nk_label(console->ctx, console->label, NK_TEXT_LEFT); 155 | } 156 | if (!nk_console_is_active_widget(console)) { 157 | nk_widget_disable_end(console->ctx); 158 | } 159 | } 160 | 161 | // Display the mocked button 162 | int swap_columns = console->columns; 163 | const char* swap_label = console->label; 164 | console->columns = 0; 165 | if (data->file_path_buffer != NULL && data->file_path_buffer[0] != '\0') { 166 | console->label = nk_console_file_basename(data->file_path_buffer); 167 | } 168 | else { 169 | console->label = data->select_directory ? "[Select Directory]" : "[Select a File]"; 170 | } 171 | struct nk_rect widget_bounds = nk_console_button_render(console); 172 | console->columns = swap_columns; 173 | console->label = swap_label; 174 | 175 | return widget_bounds; 176 | } 177 | 178 | /** 179 | * Gets the file widget from a child button. 180 | */ 181 | static nk_console* nk_console_file_button_get_file_widget(nk_console* button) { 182 | if (button == NULL) { 183 | return NULL; 184 | } 185 | if (button->type == NK_CONSOLE_FILE) { 186 | return button; 187 | } 188 | while (button->parent != NULL) { 189 | button = button->parent; 190 | if (button->type == NK_CONSOLE_FILE) { 191 | return button; 192 | } 193 | } 194 | 195 | return NULL; 196 | } 197 | 198 | /** 199 | * Free the individual file entry buttons. This clears the label. 200 | */ 201 | NK_API void nk_console_file_free_entry(nk_console* button, void* user_data) { 202 | NK_UNUSED(user_data); 203 | if (button == NULL) { 204 | return; 205 | } 206 | 207 | if (button->label != NULL) { 208 | nk_console_mfree(nk_handle_id(0), (void*)button->label); 209 | button->label = NULL; 210 | } 211 | } 212 | 213 | NK_API void nk_console_file_entry_onclick(nk_console* button, void* user_data) { 214 | NK_UNUSED(user_data); 215 | if (button == NULL || button->label == NULL) { 216 | return; 217 | } 218 | 219 | nk_console* file = nk_console_file_button_get_file_widget(button); 220 | if (file == NULL || file->data == NULL) { 221 | return; 222 | } 223 | 224 | nk_console_file_data* data = (nk_console_file_data*)file->data; 225 | int len = nk_strlen(data->directory); 226 | 227 | // Append a slash if the directory is not empty. 228 | if (len == 1 && data->directory[0] == '.') { 229 | len = 0; 230 | data->directory[0] = '\0'; 231 | } 232 | else if (len > 0) { 233 | // TODO: file: Make sure this is cross-platform. 234 | #if defined(_WIN32) || defined(WIN32) 235 | data->directory[len] = '\\'; 236 | #else 237 | data->directory[len] = '/'; 238 | #endif 239 | data->directory[len + 1] = '\0'; 240 | len++; 241 | } 242 | 243 | // Concatenate the button label to the directory. 244 | // TODO: file: Resolve the path properly, so the paths don't recurse. For example: folder/../folder 245 | // TODO: file: Add UTF-8 support. 246 | NK_MEMCPY(data->directory + len, (void*)button->label, (nk_size)(nk_strlen(button->label) + 1)); 247 | 248 | enum nk_symbol_type symbol = nk_console_button_get_symbol(button); 249 | switch (symbol) { // Directory 250 | case NK_SYMBOL_TRIANGLE_LEFT: // Back 251 | case NK_SYMBOL_TRIANGLE_RIGHT: // Folder 252 | nk_console_set_active_parent(file); 253 | nk_console_add_event(file, NK_CONSOLE_EVENT_POST_RENDER_ONCE, &nk_console_file_refresh); 254 | break; 255 | default: // File 256 | { 257 | // Copy the string to the file buffer. 258 | // TODO: Ensure UTF-8 compatibility. 259 | int desired_length = nk_strlen(data->directory); 260 | if (desired_length >= data->file_path_buffer_size) { 261 | NK_ASSERT(0); // File path is too long 262 | nk_console_show_message(file, "Error: File path is too long."); 263 | } 264 | else { 265 | NK_MEMCPY(data->file_path_buffer, data->directory, (nk_size)desired_length); 266 | data->file_path_buffer[desired_length] = '\0'; 267 | 268 | // Trigger the onchange event and exit. 269 | nk_console_trigger_event(file, NK_CONSOLE_EVENT_CHANGED); 270 | } 271 | 272 | // Now that we selected a file, we can exit. 273 | nk_console_set_active_parent(file->parent); 274 | } break; 275 | } 276 | } 277 | 278 | NK_API nk_console* nk_console_file_add_entry(nk_console* parent, const char* path, nk_bool is_directory) { 279 | if (parent == NULL || path == NULL || path[0] == '\0' || parent->data == NULL) { 280 | return NULL; 281 | } 282 | 283 | // Are we only selecting directories? 284 | nk_console_file_data* data = (nk_console_file_data*)nk_console_file_button_get_file_widget(parent)->data; 285 | if (is_directory == nk_false && data->select_directory == nk_true) { 286 | return NULL; 287 | } 288 | 289 | int len = nk_strlen(path); 290 | 291 | // Ignore the current directory. 292 | if (len == 1 && path[0] == '.') { 293 | return NULL; 294 | } 295 | else if (len == 2 && path[0] == '.' && path[1] == '.') { 296 | // Ignore the parent directory. 297 | return NULL; 298 | } 299 | 300 | // Add the button. 301 | nk_console* button = nk_console_button(parent, NULL); 302 | 303 | // Copy the path for the label, and register an event to destroy it. 304 | // TODO: file: Ensure UTF-8 compatibility. 305 | button->label = (const char*)NK_CONSOLE_MALLOC(nk_handle_id(0), NULL, (nk_size)(sizeof(char)) * (nk_size)(len + 1)); 306 | nk_console_add_event(button, NK_CONSOLE_EVENT_DESTROYED, &nk_console_file_free_entry); 307 | 308 | char* label = (char*)button->label; 309 | 310 | // Use the base name as the label. 311 | const char* basename = nk_console_file_basename(path); 312 | nk_size basename_len = (nk_size)nk_strlen(basename); 313 | NK_MEMCPY(label, basename, basename_len); 314 | label[basename_len] = '\0'; 315 | 316 | // Symbol 317 | if (is_directory == nk_true) { 318 | nk_console_button_set_symbol(button, NK_SYMBOL_TRIANGLE_RIGHT); 319 | } 320 | 321 | // Event 322 | nk_console_add_event(button, NK_CONSOLE_EVENT_CLICKED, &nk_console_file_entry_onclick); 323 | return button; 324 | } 325 | 326 | 327 | /** 328 | * Gets the length of the directory string of the given file path. 329 | */ 330 | static int nk_console_file_get_directory_len(const char* file_path) { 331 | if (file_path == NULL) { 332 | return 0; 333 | } 334 | int len = nk_strlen(file_path); 335 | for (int i = len - 1; i > 0; i--) { 336 | if (file_path[i] == '\\' || file_path[i] == '/') { 337 | return i; 338 | } 339 | } 340 | return 0; 341 | } 342 | 343 | /** 344 | * Fills the files array with the files in the current directory. 345 | */ 346 | NK_API void nk_console_file_refresh(nk_console* widget, void* user_data) { 347 | NK_UNUSED(user_data); 348 | widget = nk_console_file_button_get_file_widget(widget); 349 | if (widget == NULL || widget->data == NULL) { 350 | return; 351 | } 352 | 353 | nk_console_file_data* data = (nk_console_file_data*)widget->data; 354 | 355 | // Clear out all the current entries. 356 | nk_console_free_children(widget); 357 | 358 | // Add the back/cancel button 359 | nk_console* cancelButton = nk_console_button_onclick(widget, "Cancel", &nk_console_button_back); 360 | nk_console_button_set_symbol(cancelButton, NK_SYMBOL_X); 361 | 362 | // Show the Active directory. 363 | if (!data->select_directory) { 364 | // Active directory label 365 | nk_console* activeLabel = nk_console_label(widget, data->directory); 366 | activeLabel->alignment = NK_TEXT_CENTERED; 367 | } 368 | else { 369 | // Add the select directory button. 370 | nk_console* button = nk_console_file_add_entry(widget, data->directory, nk_true); 371 | nk_console_button_set_symbol(button, NK_SYMBOL_CIRCLE_SOLID); 372 | nk_console_set_tooltip(button, "Use this directory"); 373 | } 374 | 375 | // Add the parent directory button 376 | nk_console* parent_directory_button = nk_console_button_onclick(widget, "..", &nk_console_file_entry_onclick); 377 | nk_console_button_set_symbol(parent_directory_button, NK_SYMBOL_TRIANGLE_LEFT); 378 | nk_console_set_tooltip(parent_directory_button, "Navigate to the parent directory"); 379 | nk_console_set_active_widget(parent_directory_button); 380 | 381 | #ifdef NK_CONSOLE_FILE_ADD_FILES 382 | // Iterate through the files in the directory, and add them as entries. 383 | if (NK_CONSOLE_FILE_ADD_FILES(widget, data->directory) == nk_false) { 384 | nk_console_label(widget, "No files found.")->alignment = NK_TEXT_CENTERED; 385 | } 386 | #else 387 | // NK_CONSOLE_FILE_ADD_FILES is undefined, so back out. 388 | nk_console_show_message(widget, "Error: File system not available."); 389 | 390 | // Go back to the parent widget, and disable the widget. 391 | if (widget->parent != NULL) { 392 | widget->disabled = nk_true; 393 | nk_console_set_active_parent(widget->parent); 394 | } 395 | #endif 396 | } 397 | 398 | /** 399 | * Button callback for the main file button. 400 | */ 401 | static void nk_console_file_main_click(nk_console* button, void* user_data) { 402 | NK_UNUSED(user_data); 403 | if (button == NULL || button->data == NULL) { 404 | return; 405 | } 406 | 407 | nk_console* file = nk_console_file_button_get_file_widget(button); 408 | if (file == NULL || file->data == NULL) { 409 | return; 410 | } 411 | 412 | nk_console_file_data* data = (nk_console_file_data*)file->data; 413 | 414 | int directory_len = nk_console_file_get_directory_len(data->file_path_buffer); 415 | NK_MEMCPY(data->directory, data->file_path_buffer, (nk_size)directory_len); 416 | data->directory[directory_len] = '\0'; 417 | 418 | if (nk_strlen(data->directory) == 0) { 419 | // TODO: file: Make get current working directory function. 420 | data->directory[0] = '.'; 421 | data->directory[1] = '\0'; 422 | } 423 | 424 | // Set the active parent to the file widget, and refresh it after rendering everything else. 425 | nk_console_set_active_parent(file); 426 | nk_console_add_event(file, NK_CONSOLE_EVENT_POST_RENDER_ONCE, &nk_console_file_refresh); 427 | } 428 | 429 | NK_API void nk_console_file_set_file_user_data(nk_console* file, void* user_data) { 430 | file = nk_console_file_button_get_file_widget(file); 431 | if (file == NULL || file->data == NULL) { 432 | return; 433 | } 434 | nk_console_file_data* data = (nk_console_file_data*)file->data; 435 | data->file_user_data = user_data; 436 | } 437 | 438 | NK_API void* nk_console_file_get_file_user_data(nk_console* file) { 439 | file = nk_console_file_button_get_file_widget(file); 440 | if (file == NULL || file->data == NULL) { 441 | return NULL; 442 | } 443 | nk_console_file_data* data = (nk_console_file_data*)file->data; 444 | return data->file_user_data; 445 | } 446 | 447 | NK_API nk_console* nk_console_file(nk_console* parent, const char* label, char* file_path_buffer, int file_path_buffer_size) { 448 | if (parent == NULL || file_path_buffer == NULL || file_path_buffer_size <= 0) { 449 | return NULL; 450 | } 451 | 452 | // Create the widget data. 453 | nk_console_file_data* data = (nk_console_file_data*)NK_CONSOLE_MALLOC(nk_handle_id(0), NULL, sizeof(nk_console_file_data)); 454 | nk_zero(data, sizeof(nk_console_file_data)); 455 | 456 | data->file_path_buffer = file_path_buffer; 457 | data->file_path_buffer_size = file_path_buffer_size; 458 | 459 | nk_console* widget = nk_console_label(parent, label); 460 | widget->type = NK_CONSOLE_FILE; 461 | widget->columns = label == NULL ? 1 : 2; 462 | widget->render = nk_console_file_render; 463 | widget->selectable = nk_true; 464 | widget->data = data; 465 | 466 | nk_console_add_event(widget, NK_CONSOLE_EVENT_CLICKED, &nk_console_file_main_click); 467 | return widget; 468 | } 469 | 470 | NK_API nk_console* nk_console_dir(nk_console* parent, const char* label, char* dir_buffer, int dir_buffer_size) { 471 | nk_console* widget = nk_console_file(parent, label, dir_buffer, dir_buffer_size); 472 | if (widget == NULL) { 473 | return NULL; 474 | } 475 | 476 | nk_console_file_data* data = (nk_console_file_data*)widget->data; 477 | data->select_directory = nk_true; 478 | 479 | return widget; 480 | } 481 | 482 | #if defined(__cplusplus) 483 | } 484 | #endif 485 | 486 | #endif // NK_CONSOLE_FILE_IMPLEMENTATION_ONCE 487 | #endif // NK_CONSOLE_IMPLEMENTATION 488 | -------------------------------------------------------------------------------- /nuklear_console_file_system.h: -------------------------------------------------------------------------------- 1 | /** 2 | * File System for nuklear_console. 3 | * 4 | * Configuration: 5 | * - NK_CONSOLE_FILE_ADD_FILES: Callback which is used to add files to the file widget, matching the signature of nk_console_file_add_files_tinydir(). 6 | * - NK_CONSOLE_ENABLE_TINYDIR: Use the tinydir library to list files in a directory. https://github.com/cxong/tinydir 7 | * - NK_CONSOLE_ENABLE_RAYLIB or RAYLIB_VERSION: When defined, will use raylib to enumerate the files. https://github.com/RobLoach/raylib-nuklear 8 | * - When no file system is enabled, clicking on the Select File button will show an error message. 9 | */ 10 | 11 | #ifndef NK_CONSOLE_FILE_SYSTEM_H__ 12 | #define NK_CONSOLE_FILE_SYSTEM_H__ 13 | 14 | #if defined(__cplusplus) 15 | extern "C" { 16 | #endif 17 | 18 | // Nothing. 19 | 20 | #if defined(__cplusplus) 21 | } 22 | #endif 23 | 24 | #endif // NK_CONSOLE_FILE_SYSTEM_H__ 25 | 26 | #if defined(NK_CONSOLE_IMPLEMENTATION) && !defined(NK_CONSOLE_HEADER_ONLY) 27 | #ifndef NK_CONSOLE_FILE_SYSTEM_IMPLEMENTATION_ONCE 28 | #define NK_CONSOLE_FILE_SYSTEM_IMPLEMENTATION_ONCE 29 | 30 | #if defined(__cplusplus) 31 | extern "C" { 32 | #endif 33 | 34 | // TODO: Allow disabling tinydir 35 | #ifndef NK_CONSOLE_FILE_ADD_FILES 36 | #ifdef NK_CONSOLE_ENABLE_TINYDIR 37 | 38 | #ifndef NK_CONSOLE_FILE_ADD_FILES_TINYDIR_H 39 | #define NK_CONSOLE_FILE_ADD_FILES_TINYDIR_H "vendor/tinydir/tinydir.h" 40 | #endif // NK_CONSOLE_FILE_ADD_FILES_TINYDIR_H 41 | 42 | #ifndef NK_CONSOLE_FILE_ADD_FILES_TINYDIR_SKIP 43 | 44 | // tinydir uses the same memory function signatures as cvector. 45 | #ifndef _TINYDIR_MALLOC 46 | #define _TINYDIR_MALLOC cvector_clib_malloc 47 | #endif // _TINYDIR_MALLOC 48 | #ifndef _TINYDIR_FREE 49 | #define _TINYDIR_FREE cvector_clib_free 50 | #endif // _TINYDIR_FREE 51 | 52 | #include NK_CONSOLE_FILE_ADD_FILES_TINYDIR_H 53 | #endif // NK_CONSOLE_FILE_ADD_FILES_TINYDIR_SKIP 54 | 55 | /** 56 | * Iterate through the files in the given directory, and add the contents as widgets. 57 | * 58 | * @param parent The file widget. 59 | * @param directory The directory to enumerate. 60 | * 61 | * @return True if there were entries added. 62 | * 63 | * @see nk_console_file_add_entry() 64 | */ 65 | static nk_bool nk_console_file_add_files_tinydir(nk_console* parent, const char* directory) { 66 | if (parent == NULL || directory == NULL) { 67 | return nk_false; 68 | } 69 | 70 | tinydir_dir dir; 71 | if (tinydir_open_sorted(&dir, directory) == -1) { 72 | return nk_false; 73 | } 74 | 75 | nk_bool result = nk_false; 76 | 77 | // Iterate through the files and add each entry. 78 | for (size_t i = 0; i < dir.n_files; i++) { 79 | tinydir_file file; 80 | tinydir_readfile_n(&dir, &file, i); 81 | if (nk_console_file_add_entry(parent, file.name, file.is_dir == 0 ? nk_false : nk_true) != NULL) { 82 | result = nk_true; 83 | } 84 | } 85 | 86 | // Close the directory. 87 | tinydir_close(&dir); 88 | 89 | return result; 90 | } 91 | 92 | // Tell the file widget to use the tinydir file system. 93 | #define NK_CONSOLE_FILE_ADD_FILES nk_console_file_add_files_tinydir 94 | 95 | // Raylib support 96 | #elif defined(NK_CONSOLE_ENABLE_RAYLIB) || defined(RAYLIB_VERSION) 97 | 98 | /** 99 | * nuklear_console_file callback to iterate through a directory and ad all entries from the given path. 100 | * 101 | * @param console The parent files widget. 102 | * @param path The path to enumerate. 103 | * 104 | * @return True if there were entries that were added. 105 | * 106 | * @see nk_console_file_add_entry() 107 | */ 108 | static nk_bool nk_console_file_add_files_raylib(nk_console* console, const char* path) { 109 | FilePathList filePathList = LoadDirectoryFiles(path); 110 | 111 | nk_bool result = nk_false; 112 | 113 | // Directories 114 | for (int i = 0; i < filePathList.count; i++) { 115 | if (DirectoryExists(filePathList.paths[i])) { 116 | if (nk_console_file_add_entry(console, filePathList.paths[i], nk_true) != NULL) { 117 | result = nk_true; 118 | } 119 | } 120 | } 121 | 122 | // Files 123 | for (int i = 0; i < filePathList.count; i++) { 124 | if (FileExists(filePathList.paths[i]) && !DirectoryExists(filePathList.paths[i])) { 125 | if (nk_console_file_add_entry(console, filePathList.paths[i], nk_false) != NULL) { 126 | result = nk_true; 127 | } 128 | } 129 | } 130 | 131 | UnloadDirectoryFiles(filePathList); 132 | 133 | return result; 134 | } 135 | 136 | // Tell the file widget to use the raylib file system. 137 | #define NK_CONSOLE_FILE_ADD_FILES nk_console_file_add_files_raylib 138 | 139 | #endif // NK_CONSOLE_ENABLE_TINYDIR 140 | #endif // NK_CONSOLE_FILE_ADD_FILES 141 | 142 | #if defined(__cplusplus) 143 | } 144 | #endif 145 | 146 | #endif // NK_CONSOLE_FILE_SYSTEM_IMPLEMENTATION_ONCE 147 | #endif // NK_CONSOLE_IMPLEMENTATION 148 | -------------------------------------------------------------------------------- /nuklear_console_image.h: -------------------------------------------------------------------------------- 1 | #ifndef NK_CONSOLE_IMAGE_H__ 2 | #define NK_CONSOLE_IMAGE_H__ 3 | 4 | typedef struct nk_console_image_data { 5 | struct nk_image image; 6 | struct nk_color color; 7 | } nk_console_image_data; 8 | 9 | #if defined(__cplusplus) 10 | extern "C" { 11 | #endif 12 | 13 | NK_API nk_console* nk_console_image(nk_console* parent, struct nk_image image); 14 | NK_API nk_console* nk_console_image_color(nk_console* parent, struct nk_image image, struct nk_color color); 15 | NK_API struct nk_rect nk_console_image_render(nk_console* widget); 16 | 17 | NK_API void nk_console_image_set_image(nk_console* widget, struct nk_image image); 18 | NK_API struct nk_image nk_console_image_get_image(nk_console* widget); 19 | 20 | NK_API void nk_console_image_set_color(nk_console* widget, struct nk_color color); 21 | NK_API struct nk_color nk_console_image_get_color(nk_console* widget); 22 | 23 | #if defined(__cplusplus) 24 | } 25 | #endif 26 | 27 | #endif // NK_CONSOLE_IMAGE_H__ 28 | 29 | #if defined(NK_CONSOLE_IMPLEMENTATION) && !defined(NK_CONSOLE_HEADER_ONLY) 30 | #ifndef NK_CONSOLE_IMAGE_IMPLEMENTATION_ONCE 31 | #define NK_CONSOLE_IMAGE_IMPLEMENTATION_ONCE 32 | 33 | #if defined(__cplusplus) 34 | extern "C" { 35 | #endif 36 | 37 | NK_API void nk_console_image_set_image(nk_console* widget, struct nk_image image) { 38 | if (widget == NULL || widget->data == NULL) { 39 | return; 40 | } 41 | nk_console_image_data* data = (nk_console_image_data*)widget->data; 42 | data->image = image; 43 | } 44 | 45 | NK_API struct nk_image nk_console_image_get_image(nk_console* widget) { 46 | if (widget == NULL || widget->data == NULL) { 47 | struct nk_image output = {0}; 48 | return output; 49 | } 50 | nk_console_image_data* data = (nk_console_image_data*)widget->data; 51 | return data->image; 52 | } 53 | 54 | NK_API void nk_console_image_set_color(nk_console* widget, struct nk_color color) { 55 | if (widget == NULL || widget->data == NULL) { 56 | return; 57 | } 58 | nk_console_image_data* data = (nk_console_image_data*)widget->data; 59 | data->color = color; 60 | } 61 | 62 | NK_API struct nk_color nk_console_image_get_color(nk_console* widget) { 63 | if (widget == NULL || widget->data == NULL) { 64 | struct nk_color output = {0}; 65 | return output; 66 | } 67 | nk_console_image_data* data = (nk_console_image_data*)widget->data; 68 | return data->color; 69 | } 70 | 71 | NK_API struct nk_rect nk_console_image_render(nk_console* widget) { 72 | nk_console_image_data* data = (nk_console_image_data*)widget->data; 73 | if (data == NULL) { 74 | return nk_rect(0, 0, 0, 0); 75 | } 76 | 77 | nk_console_layout_widget(widget); 78 | 79 | struct nk_rect widget_bounds = nk_layout_widget_bounds(widget->ctx); 80 | 81 | // Toggle it as disabled if needed. 82 | if (widget->disabled || 83 | (widget->selectable && nk_console_get_active_widget(widget) != widget)) { 84 | nk_widget_disable_begin(widget->ctx); 85 | } 86 | 87 | nk_image_color(widget->ctx, data->image, data->color); 88 | 89 | // Release the disabled state if needed. 90 | if (widget->disabled || 91 | (widget->selectable && nk_console_get_active_widget(widget) != widget)) { 92 | nk_widget_disable_end(widget->ctx); 93 | } 94 | 95 | if (nk_console_is_active_widget(widget)) { 96 | nk_console_check_up_down(widget, widget_bounds); 97 | nk_console_check_tooltip(widget); 98 | } 99 | 100 | return widget_bounds; 101 | } 102 | 103 | NK_API nk_console* nk_console_image(nk_console* parent, struct nk_image image) { 104 | struct nk_color white = {255, 255, 255, 255}; 105 | return nk_console_image_color(parent, image, white); 106 | } 107 | 108 | NK_API 109 | nk_console* nk_console_image_color(nk_console* parent, struct nk_image image, struct nk_color color) { 110 | nk_console_image_data* data = 111 | (nk_console_image_data*)NK_CONSOLE_MALLOC(nk_handle_id(0), NULL, sizeof(*data)); 112 | data->image = image; 113 | data->color = color; 114 | 115 | nk_console* img = nk_console_label(parent, NULL); 116 | img->type = NK_CONSOLE_IMAGE; 117 | img->data = (void*)data; 118 | img->render = nk_console_image_render; 119 | return img; 120 | } 121 | 122 | #if defined(__cplusplus) 123 | } 124 | #endif 125 | 126 | #endif // NK_CONSOLE_IMAGE_IMPLEMENTATION_ONCE 127 | #endif // NK_CONSOLE_IMPLEMENTATION 128 | -------------------------------------------------------------------------------- /nuklear_console_input.h: -------------------------------------------------------------------------------- 1 | #ifndef NK_CONSOLE_INPUT_H__ 2 | #define NK_CONSOLE_INPUT_H__ 3 | 4 | /** 5 | * Data specifically used for the input widget. 6 | * 7 | * @see nk_console_input() 8 | */ 9 | typedef struct nk_console_input_data { 10 | struct nk_console_button_data button_data; /** Inherited from button */ 11 | int gamepad_number; /** The gamepad number of which to expect input from. Provied -1 for any gamepad. */ 12 | int* out_gamepad_number; /** A pointer for where to store the gamepad number the button is associated with. */ 13 | enum nk_gamepad_button* out_gamepad_button; /** A pointer to where to store the gamepad button. */ 14 | float timer; /** A countdown timer to prompt the user with. @see NK_CONSOLE_INPUT_TIMER */ 15 | } nk_console_input_data; 16 | 17 | #if defined(__cplusplus) 18 | extern "C" { 19 | #endif 20 | 21 | /** 22 | * Create a new input widget to get a gamepad button. 23 | * 24 | * @param parent The parent console of where to add the widget. 25 | * @param label The label to display. 26 | * @param gamepad_number The gamepad number to expect input from. Provide -1 for any gamepad. 27 | * @param out_gamepad_number When the user enters a button, this is where the gamepad number that was used to press the button. 28 | * @param out_gamepad_button When the user enters a button, this is where the gamepad button will be stored. 29 | * 30 | * @todo Input: Add Keyboard support? 31 | * 32 | * @return The new input widget. 33 | */ 34 | NK_API nk_console* nk_console_input(nk_console* parent, const char* label, int gamepad_number, int* out_gamepad_number, enum nk_gamepad_button* out_gamepad_button); 35 | NK_API struct nk_rect nk_console_input_render(nk_console* widget); 36 | 37 | #if defined(__cplusplus) 38 | } 39 | #endif 40 | 41 | #endif // NK_CONSOLE_INPUT_H__ 42 | 43 | #if defined(NK_CONSOLE_IMPLEMENTATION) && !defined(NK_CONSOLE_HEADER_ONLY) 44 | #ifndef NK_CONSOLE_INPUT_IMPLEMENTATION_ONCE 45 | #define NK_CONSOLE_INPUT_IMPLEMENTATION_ONCE 46 | 47 | #ifndef NK_CONSOLE_INPUT_TIMER 48 | /** 49 | * The amount of time to wait for input before timing out, in seconds. 50 | */ 51 | #define NK_CONSOLE_INPUT_TIMER 6.0f 52 | #endif 53 | 54 | #if defined(__cplusplus) 55 | extern "C" { 56 | #endif 57 | 58 | /** 59 | * Get the name of a gamepad button. 60 | * 61 | * @param button The button to get the name of. 62 | * 63 | * @return The name of the button. 64 | */ 65 | static const char* nk_console_input_button_name(enum nk_gamepad_button button) { 66 | switch (button) { 67 | case NK_GAMEPAD_BUTTON_INVALID: return ""; 68 | case NK_GAMEPAD_BUTTON_A: return "A"; 69 | case NK_GAMEPAD_BUTTON_B: return "B"; 70 | case NK_GAMEPAD_BUTTON_X: return "X"; 71 | case NK_GAMEPAD_BUTTON_Y: return "Y"; 72 | case NK_GAMEPAD_BUTTON_LB: return "Left Bumper"; 73 | case NK_GAMEPAD_BUTTON_RB: return "Right Bumper"; 74 | case NK_GAMEPAD_BUTTON_BACK: return "Back"; 75 | case NK_GAMEPAD_BUTTON_START: return "Start"; 76 | case NK_GAMEPAD_BUTTON_UP: return "Up"; 77 | case NK_GAMEPAD_BUTTON_DOWN: return "Down"; 78 | case NK_GAMEPAD_BUTTON_LEFT: return "Left"; 79 | case NK_GAMEPAD_BUTTON_RIGHT: return "Right"; 80 | default: return "Unknown"; 81 | } 82 | } 83 | 84 | NK_API struct nk_rect nk_console_input_render(nk_console* console) { 85 | if (console == NULL || console->data == NULL) { 86 | return nk_rect(0, 0, 0, 0); 87 | } 88 | nk_console_input_data* data = (nk_console_input_data*)console->data; 89 | if (data->out_gamepad_button == NULL) { 90 | return nk_rect(0, 0, 0, 0); 91 | } 92 | 93 | // Set up the layout 94 | nk_console_layout_widget(console); 95 | 96 | // Display the label 97 | if (console->label != NULL && console->label[0] != '\0') { 98 | if (!nk_console_is_active_widget(console)) { 99 | nk_widget_disable_begin(console->ctx); 100 | } 101 | if (console->label_length > 0) { 102 | nk_text(console->ctx, console->label, console->label_length, NK_TEXT_LEFT); 103 | } 104 | else { 105 | nk_label(console->ctx, console->label, NK_TEXT_LEFT); 106 | } 107 | if (!nk_console_is_active_widget(console)) { 108 | nk_widget_disable_end(console->ctx); 109 | } 110 | } 111 | 112 | // Display the mocked button 113 | switch (*data->out_gamepad_button) { 114 | case NK_GAMEPAD_BUTTON_UP: 115 | nk_console_button_set_symbol(console, NK_SYMBOL_TRIANGLE_UP); 116 | break; 117 | case NK_GAMEPAD_BUTTON_DOWN: 118 | nk_console_button_set_symbol(console, NK_SYMBOL_TRIANGLE_DOWN); 119 | break; 120 | case NK_GAMEPAD_BUTTON_LEFT: 121 | nk_console_button_set_symbol(console, NK_SYMBOL_TRIANGLE_LEFT); 122 | break; 123 | case NK_GAMEPAD_BUTTON_RIGHT: 124 | nk_console_button_set_symbol(console, NK_SYMBOL_TRIANGLE_RIGHT); 125 | break; 126 | case NK_GAMEPAD_BUTTON_START: 127 | nk_console_button_set_symbol(console, NK_SYMBOL_PLUS); 128 | break; 129 | case NK_GAMEPAD_BUTTON_BACK: 130 | nk_console_button_set_symbol(console, NK_SYMBOL_MINUS); 131 | break; 132 | default: 133 | nk_console_button_set_symbol(console, NK_SYMBOL_NONE); 134 | break; 135 | } 136 | 137 | // Switch the values to have the widget display as a button. 138 | int swap_columns = console->columns; 139 | const char* swap_label = console->label; 140 | int swap_label_length = console->label_length; 141 | console->columns = 0; 142 | console->label = nk_console_input_button_name(*data->out_gamepad_button); 143 | console->label_length = 0; 144 | struct nk_rect widget_bounds = nk_console_button_render(console); 145 | console->columns = swap_columns; 146 | console->label = swap_label; 147 | console->label_length = swap_label_length; 148 | 149 | return widget_bounds; 150 | } 151 | 152 | /** 153 | * Render the "Press a Button" prompt. 154 | * 155 | * @param console The console to render the prompt for. 156 | * 157 | * @return An empty rectangle, because there isn't a widget to interact with. 158 | */ 159 | static struct nk_rect nk_console_input_active_render(nk_console* console) { 160 | if (console == NULL) { 161 | return nk_rect(0, 0, 0, 0); 162 | } 163 | 164 | // Get the parent input widget. 165 | nk_console* input = console->parent; 166 | nk_console_input_data* data = (nk_console_input_data*)input->data; 167 | if (data == NULL) { 168 | return nk_rect(0, 0, 0, 0); 169 | } 170 | nk_console* top = nk_console_get_top(console); 171 | 172 | // Set up the layout 173 | nk_console_layout_widget(console); 174 | 175 | // Display the label for the input 176 | if (input->label != NULL && input->label[0] != '\0') { 177 | if (input->label_length > 0) { 178 | nk_text(console->ctx, input->label, input->label_length, NK_TEXT_CENTERED); 179 | } 180 | else { 181 | nk_label(console->ctx, input->label, NK_TEXT_CENTERED); 182 | } 183 | } 184 | 185 | // Give a timer to the user. 186 | data->timer += console->ctx->delta_time_seconds; 187 | nk_bool finished = nk_false; 188 | 189 | // Handle the timeout 190 | if (data->timer >= NK_CONSOLE_INPUT_TIMER) { 191 | finished = nk_true; 192 | } 193 | 194 | // Only display the timer if delta time is provided. 195 | if (data->timer > 0.0f) { 196 | // Display a progressbar, scaling the time to milliseconds. 197 | nk_prog(console->ctx, (size_t)(data->timer * 1000), (size_t)(NK_CONSOLE_INPUT_TIMER * 1000), nk_false); 198 | } 199 | 200 | // Blinking press a button instructions 201 | if (((int)(data->timer * 2)) % 2 == 0) { 202 | nk_label(console->ctx, "Press a Button", NK_TEXT_CENTERED); 203 | } 204 | 205 | // Check for input. 206 | nk_console_top_data* top_data = (nk_console_top_data*)top->data; 207 | if (top_data->input_processed == nk_false) { 208 | // Gamepad button pressed. 209 | if (nk_gamepad_any_button_pressed((struct nk_gamepads*)nk_console_get_gamepads(top), data->gamepad_number, data->out_gamepad_number, data->out_gamepad_button)) { 210 | // Trigger the onchange event and exit. 211 | nk_console_trigger_event(input, NK_CONSOLE_EVENT_CHANGED); 212 | finished = nk_true; 213 | } 214 | // Any other input. 215 | else if (nk_input_is_key_pressed(&console->ctx->input, NK_KEY_BACKSPACE) || nk_input_is_key_pressed(&console->ctx->input, NK_KEY_ENTER) || nk_input_is_mouse_pressed(&console->ctx->input, NK_BUTTON_LEFT) || nk_input_is_mouse_pressed(&console->ctx->input, NK_BUTTON_RIGHT)) { 216 | finished = nk_true; 217 | } 218 | } 219 | 220 | if (finished == nk_true) { 221 | top_data->input_processed = nk_true; 222 | data->timer = 0.0f; 223 | nk_console_button_back(console, NULL); 224 | } 225 | 226 | return nk_rect(0, 0, 0, 0); 227 | } 228 | 229 | NK_API nk_console* nk_console_input(nk_console* parent, const char* label, int gamepad_number, int* out_gamepad_number, enum nk_gamepad_button* out_gamepad_button) { 230 | if (parent == NULL) { 231 | return NULL; 232 | } 233 | 234 | // Create the widget data. 235 | nk_console_input_data* data = (nk_console_input_data*)NK_CONSOLE_MALLOC(nk_handle_id(0), NULL, sizeof(nk_console_input_data)); 236 | nk_zero(data, sizeof(nk_console_input_data)); 237 | data->gamepad_number = gamepad_number; 238 | data->out_gamepad_number = out_gamepad_number; 239 | data->out_gamepad_button = out_gamepad_button; 240 | 241 | nk_console* widget = nk_console_label(parent, label); 242 | widget->type = NK_CONSOLE_INPUT; 243 | widget->columns = label == NULL ? 1 : 2; 244 | widget->render = nk_console_input_render; 245 | widget->selectable = nk_true; 246 | widget->data = data; 247 | 248 | // Set up the input state child. 249 | nk_console* active_state = nk_console_label(widget, NULL); 250 | active_state->type = NK_CONSOLE_INPUT_ACTIVE; 251 | active_state->render = nk_console_input_active_render; 252 | 253 | return widget; 254 | } 255 | 256 | #if defined(__cplusplus) 257 | } 258 | #endif 259 | 260 | #endif // NK_CONSOLE_INPUT_IMPLEMENTATION_ONCE 261 | #endif // NK_CONSOLE_IMPLEMENTATION 262 | -------------------------------------------------------------------------------- /nuklear_console_knob.h: -------------------------------------------------------------------------------- 1 | #ifndef NK_CONSOLE_KNOB_H__ 2 | #define NK_CONSOLE_KNOB_H__ 3 | 4 | #include "nuklear_console_property.h" 5 | 6 | /** 7 | * Data for Property and Slider widgets. 8 | */ 9 | typedef struct nk_console_knob_data { 10 | nk_console_property_data property; /* Inherited from property */ 11 | enum nk_heading zero_direction; 12 | float dead_zone_degrees; 13 | } nk_console_knob_data; 14 | 15 | #if defined(__cplusplus) 16 | extern "C" { 17 | #endif 18 | 19 | /** 20 | * Creates a Knob widget using an integer value. 21 | */ 22 | NK_API nk_console* nk_console_knob_int(nk_console* parent, const char* label, int min, int* val, int max, int step, float inc_per_pixel); 23 | 24 | /** 25 | * Creates a Knob widget using a float value. 26 | */ 27 | NK_API nk_console* nk_console_knob_float(nk_console* parent, const char* label, float min, float* val, float max, float step, float inc_per_pixel); 28 | 29 | #if defined(__cplusplus) 30 | } 31 | #endif 32 | 33 | #endif // NK_CONSOLE_KNOB_H__ 34 | 35 | #if defined(NK_CONSOLE_IMPLEMENTATION) && !defined(NK_CONSOLE_HEADER_ONLY) 36 | #ifndef NK_CONSOLE_KNOB_IMPLEMENTATION_ONCE 37 | #define NK_CONSOLE_KNOB_IMPLEMENTATION_ONCE 38 | 39 | #if defined(__cplusplus) 40 | extern "C" { 41 | #endif 42 | 43 | NK_API nk_console* nk_console_knob_int(nk_console* parent, const char* label, int min, int* val, int max, int step, float inc_per_pixel) { 44 | // Create the property data. 45 | nk_console_knob_data* data = (nk_console_knob_data*)NK_CONSOLE_MALLOC(nk_handle_id(0), NULL, sizeof(nk_console_knob_data)); 46 | nk_zero(data, sizeof(nk_console_knob_data)); 47 | data->property.min_int = min; 48 | data->property.val_int = val; 49 | data->property.max_int = max; 50 | data->property.step_int = step; 51 | data->property.inc_per_pixel = inc_per_pixel; 52 | 53 | data->zero_direction = NK_DOWN; 54 | data->dead_zone_degrees = 60.0f; 55 | 56 | nk_console* widget = nk_console_label(parent, label); 57 | widget->render = &nk_console_property_render; 58 | widget->type = NK_CONSOLE_KNOB_INT; 59 | widget->selectable = nk_true; 60 | widget->data = (void*)data; 61 | widget->columns = label != NULL ? 2 : 1; 62 | 63 | if (val != NULL) { 64 | if (*val < min) { 65 | *val = min; 66 | } 67 | else if (*val > max) { 68 | *val = max; 69 | } 70 | } 71 | 72 | return widget; 73 | } 74 | 75 | NK_API nk_console* nk_console_knob_float(nk_console* parent, const char* label, float min, float* val, float max, float step, float inc_per_pixel) { 76 | nk_console* widget = nk_console_knob_int(parent, label, 0, NULL, 0, 0, inc_per_pixel); 77 | nk_console_knob_data* data = (nk_console_knob_data*)widget->data; 78 | widget->type = NK_CONSOLE_KNOB_FLOAT; 79 | data->property.min_float = min; 80 | data->property.val_float = val; 81 | data->property.max_float = max; 82 | data->property.step_float = step; 83 | 84 | if (val != NULL) { 85 | if (*val < min) { 86 | *val = min; 87 | } 88 | else if (*val > max) { 89 | *val = max; 90 | } 91 | } 92 | 93 | return widget; 94 | } 95 | 96 | #if defined(__cplusplus) 97 | } 98 | #endif 99 | 100 | #endif // NK_CONSOLE_KNOB_IMPLEMENTATION_ONCE 101 | #endif // NK_CONSOLE_IMPLEMENTATION 102 | -------------------------------------------------------------------------------- /nuklear_console_label.h: -------------------------------------------------------------------------------- 1 | #ifndef NK_CONSOLE_LABEL_H__ 2 | #define NK_CONSOLE_LABEL_H__ 3 | 4 | #if defined(__cplusplus) 5 | extern "C" { 6 | #endif 7 | 8 | NK_API nk_console* nk_console_label(nk_console* parent, const char* text); 9 | NK_API struct nk_rect nk_console_label_render(nk_console* widget); 10 | 11 | #if defined(__cplusplus) 12 | } 13 | #endif 14 | 15 | #endif // NK_CONSOLE_LABEL_H__ 16 | 17 | #if defined(NK_CONSOLE_IMPLEMENTATION) && !defined(NK_CONSOLE_HEADER_ONLY) 18 | #ifndef NK_CONSOLE_LABEL_IMPLEMENTATION_ONCE 19 | #define NK_CONSOLE_LABEL_IMPLEMENTATION_ONCE 20 | 21 | #if defined(__cplusplus) 22 | extern "C" { 23 | #endif 24 | 25 | NK_API struct nk_rect nk_console_label_render(nk_console* widget) { 26 | if (nk_strlen(widget->label) <= 0) { 27 | return nk_rect(0, 0, 0, 0); 28 | } 29 | 30 | nk_console_layout_widget(widget); 31 | 32 | nk_bool selected = nk_false; 33 | if (!widget->disabled && widget->selectable) { 34 | selected = nk_console_is_active_widget(widget); 35 | } 36 | 37 | // Toggle it as disabled if needed. 38 | if (widget->disabled || (widget->selectable && !selected)) { 39 | nk_widget_disable_begin(widget->ctx); 40 | } 41 | 42 | // Display the label, considering the alignment. 43 | if (widget->alignment == NK_TEXT_LEFT) { 44 | nk_label_wrap(widget->ctx, widget->label); 45 | } 46 | else { 47 | nk_label(widget->ctx, widget->label, widget->alignment); 48 | } 49 | 50 | // Release the disabled state if needed. 51 | if (widget->disabled || (widget->selectable && !selected)) { 52 | nk_widget_disable_end(widget->ctx); 53 | } 54 | 55 | // Since labels don't really have widget bounds, we get the bounds after the label is displayed as a work-around. 56 | struct nk_rect widget_bounds = nk_layout_widget_bounds(widget->ctx); 57 | 58 | // Allow switching up/down in widgets 59 | if (selected) { 60 | if (nk_console_button_pushed(widget, NK_GAMEPAD_BUTTON_A) || nk_input_mouse_clicked(&widget->ctx->input, NK_BUTTON_LEFT, widget_bounds)) { 61 | nk_console_trigger_event(widget, NK_CONSOLE_EVENT_CLICKED); 62 | } 63 | else { 64 | nk_console_check_up_down(widget, widget_bounds); 65 | } 66 | nk_console_check_tooltip(widget); 67 | } 68 | 69 | return widget_bounds; 70 | } 71 | 72 | NK_API nk_console* nk_console_label(nk_console* parent, const char* text) { 73 | if (parent == NULL) { 74 | return NULL; 75 | } 76 | 77 | nk_handle handle; 78 | nk_console* label = (nk_console*)nk_console_malloc(handle, NULL, sizeof(nk_console)); 79 | nk_zero(label, sizeof(nk_console)); 80 | label->ctx = parent->ctx; 81 | label->alignment = NK_TEXT_ALIGN_CENTERED; 82 | label->type = NK_CONSOLE_LABEL; 83 | label->label = text; 84 | label->parent = parent; 85 | label->alignment = NK_TEXT_LEFT; 86 | label->columns = 1; 87 | label->render = nk_console_label_render; 88 | label->visible = nk_true; 89 | nk_console_add_child(parent, label); 90 | return label; 91 | } 92 | 93 | #if defined(__cplusplus) 94 | } 95 | #endif 96 | 97 | #endif // NK_CONSOLE_LABEL_IMPLEMENTATION_ONCE 98 | #endif // NK_CONSOLE_IMPLEMENTATION 99 | -------------------------------------------------------------------------------- /nuklear_console_message.h: -------------------------------------------------------------------------------- 1 | #ifndef NK_CONSOLE_MESSAGE_H__ 2 | #define NK_CONSOLE_MESSAGE_H__ 3 | 4 | #if defined(__cplusplus) 5 | extern "C" { 6 | #endif 7 | 8 | /** 9 | * Displays a notification message on the screen. 10 | * 11 | * @param console The active console system. 12 | * @param text The text to display. 13 | */ 14 | NK_API void nk_console_show_message(nk_console* console, const char* text); 15 | 16 | #if defined(__cplusplus) 17 | } 18 | #endif 19 | 20 | #endif // NK_CONSOLE_MESSAGE_H__ 21 | 22 | #if defined(NK_CONSOLE_IMPLEMENTATION) && !defined(NK_CONSOLE_HEADER_ONLY) 23 | #ifndef NK_CONSOLE_MESSAGE_IMPLEMENTATION_ONCE 24 | #define NK_CONSOLE_MESSAGE_IMPLEMENTATION_ONCE 25 | 26 | #if defined(__cplusplus) 27 | extern "C" { 28 | #endif 29 | 30 | #ifndef NK_CONSOLE_MESSAGE_DURATION 31 | /** 32 | * A float determining how many seconds messages should be shown for. 33 | */ 34 | #define NK_CONSOLE_MESSAGE_DURATION 4.0f 35 | #endif // NK_CONSOLE_MESSAGE_DURATION 36 | 37 | NK_API void nk_console_show_message(nk_console* console, const char* text) { 38 | if (console == NULL || text == NULL || text[0] == '\0') { 39 | return; 40 | } 41 | 42 | nk_console_top_data* data = (nk_console_top_data*)(nk_console_get_top(console)->data); 43 | if (data == NULL) { 44 | return; 45 | } 46 | 47 | // Create the new message. 48 | nk_console_message message = {0}; 49 | message.duration = NK_CONSOLE_MESSAGE_DURATION; 50 | 51 | // Copy the string. 52 | int len = nk_strlen(text); 53 | if (len > 255) { 54 | len = 255; 55 | } 56 | NK_MEMCPY(message.text, text, (nk_size)len); 57 | message.text[len] = '\0'; // Make sure it's null-terminated 58 | 59 | // Add the new message to the message queue. 60 | cvector_push_back(data->messages, message); 61 | } 62 | 63 | NK_API void nk_console_message_render(nk_console* console, nk_console_message* message) { 64 | if (message == NULL || console->data == NULL) { 65 | return; 66 | } 67 | 68 | // Retrieve style sizes. 69 | nk_console_top_data* data = (nk_console_top_data*)console->data; 70 | struct nk_context* ctx = console->ctx; 71 | struct nk_vec2 padding = ctx->style.window.padding; 72 | float border = ctx->style.window.border; 73 | float text_height = (ctx->style.font->height + padding.y); 74 | 75 | // Backup the mouse information as we'll be mocking it with a tooltip. 76 | struct nk_vec2 mouse_pos = ctx->input.mouse.pos; 77 | 78 | // Display the tooltip at the bottom of the window, by manipulating the mouse position 79 | struct nk_rect bounds = data->message_bounds.w == 0 ? nk_window_get_bounds(ctx) : data->message_bounds; 80 | bounds.w -= border; 81 | ctx->input.mouse.pos.x = bounds.x; 82 | ctx->input.mouse.pos.y = bounds.y + bounds.h - text_height - padding.y * 2 - border * 2.0f; 83 | 84 | // Animations can be applied if delta time is available. 85 | if (ctx->delta_time_seconds > 0) { 86 | // TODO(RobLoach): Apply easing to the message animation. 87 | if (message->duration <= 1.0f) { 88 | ctx->input.mouse.pos.y += (int)((1.0f - message->duration) * (text_height + padding.y * 2 + border * 2.0f)); 89 | } 90 | else if (message->duration >= NK_CONSOLE_MESSAGE_DURATION - 1.0f) { 91 | ctx->input.mouse.pos.y += (int)((message->duration - NK_CONSOLE_MESSAGE_DURATION + 1.0f) * (text_height + padding.y * 2 + border * 2.0f)); 92 | } 93 | } 94 | 95 | // Display the tooltip where the mocked mouse is. 96 | if (nk_tooltip_begin(ctx, (float)bounds.w)) { 97 | nk_layout_row_dynamic(ctx, text_height, 1); 98 | nk_label(ctx, message->text, NK_TEXT_LEFT); 99 | nk_tooltip_end(ctx); 100 | } 101 | 102 | // Restore the mouse x/y positions. 103 | ctx->input.mouse.pos = mouse_pos; 104 | } 105 | 106 | NK_API void nk_console_render_message(nk_console* console) { 107 | nk_console_top_data* data = (nk_console_top_data*)console->data; 108 | if (data->messages == NULL || cvector_size(data->messages) == 0) { 109 | return; 110 | } 111 | 112 | // Loop through all messages and display the first one. 113 | nk_bool clear_all = nk_true; 114 | nk_console_message* end = (nk_console_message*)cvector_end(data->messages); 115 | for (nk_console_message* it = (nk_console_message*)cvector_begin(data->messages); it != end; it++) { 116 | // Skip messages that have already been shown. 117 | if (it->duration <= 0.0f) { 118 | continue; 119 | } 120 | 121 | // Show only one message at a time. 122 | if (console->ctx->delta_time_seconds > 0) { 123 | it->duration -= console->ctx->delta_time_seconds; 124 | } 125 | // If animations arn't an option, allow dismissing the message. 126 | else if (nk_console_button_pushed(console, NK_GAMEPAD_BUTTON_B)) { 127 | data->input_processed = nk_true; 128 | it->duration = 0.0f; 129 | } 130 | 131 | clear_all = nk_false; 132 | nk_console_message_render(console, it); 133 | break; 134 | } 135 | 136 | if (clear_all) { 137 | cvector_clear(data->messages); 138 | } 139 | } 140 | 141 | #if defined(__cplusplus) 142 | } 143 | #endif 144 | 145 | #endif // NK_CONSOLE_MESSAGE_IMPLEMENTATION_ONCE 146 | #endif // NK_CONSOLE_IMPLEMENTATION 147 | -------------------------------------------------------------------------------- /nuklear_console_progress.h: -------------------------------------------------------------------------------- 1 | #ifndef NK_CONSOLE_PROGRESS_H__ 2 | #define NK_CONSOLE_PROGRESS_H__ 3 | 4 | typedef struct nk_console_progress_data { 5 | nk_size max_size; 6 | nk_size* value_size; 7 | } nk_console_progress_data; 8 | 9 | #if defined(__cplusplus) 10 | extern "C" { 11 | #endif 12 | 13 | NK_API nk_console* nk_console_progress(nk_console* parent, const char* text, nk_size* current, nk_size max); 14 | NK_API struct nk_rect nk_console_progress_render(nk_console* console); 15 | 16 | #if defined(__cplusplus) 17 | } 18 | #endif 19 | 20 | #endif // NK_CONSOLE_PROGRESS_H__ 21 | 22 | #if defined(NK_CONSOLE_IMPLEMENTATION) && !defined(NK_CONSOLE_HEADER_ONLY) 23 | #ifndef NK_CONSOLE_PROGRESS_IMPLEMENTATION_ONCE 24 | #define NK_CONSOLE_PROGRESS_IMPLEMENTATION_ONCE 25 | 26 | #if defined(__cplusplus) 27 | extern "C" { 28 | #endif 29 | 30 | NK_API nk_console* nk_console_progress(nk_console* parent, const char* text, nk_size* current, nk_size max) { 31 | NK_ASSERT(current != NULL); 32 | NK_ASSERT(max > 0); 33 | 34 | // Create the widget data. 35 | nk_console_progress_data* data = (nk_console_progress_data*)NK_CONSOLE_MALLOC(nk_handle_id(0), NULL, sizeof(nk_console_progress_data)); 36 | nk_zero(data, sizeof(nk_console_progress_data)); 37 | 38 | nk_console* progress = nk_console_label(parent, text); 39 | progress->render = nk_console_progress_render; 40 | progress->type = NK_CONSOLE_PROGRESS; 41 | progress->selectable = nk_true; 42 | data->value_size = current; 43 | data->max_size = max; 44 | progress->columns = text != NULL ? 2 : 1; 45 | progress->data = (void*)data; 46 | if (*current > max) { 47 | *current = max; 48 | } 49 | return progress; 50 | } 51 | 52 | NK_API struct nk_rect nk_console_progress_render(nk_console* console) { 53 | nk_console_progress_data* data = (nk_console_progress_data*)console->data; 54 | if (data == NULL) { 55 | return nk_rect(0, 0, 0, 0); 56 | } 57 | 58 | nk_console_layout_widget(console); 59 | 60 | nk_console* top = nk_console_get_top(console); 61 | 62 | // Allow changing the value. 63 | nk_bool active = nk_false; 64 | if (!console->disabled && nk_console_is_active_widget(console)) { 65 | nk_console_top_data* top_data = (nk_console_top_data*)top->data; 66 | if (!top_data->input_processed) { 67 | if (nk_console_button_pushed(top, NK_GAMEPAD_BUTTON_LEFT)) { 68 | if (data->value_size != NULL && *data->value_size > 0) { 69 | *data->value_size = *data->value_size - 1; 70 | nk_console_trigger_event(console, NK_CONSOLE_EVENT_CHANGED); 71 | } 72 | active = nk_true; 73 | top_data->input_processed = nk_true; 74 | } 75 | else if (nk_console_button_pushed(top, NK_GAMEPAD_BUTTON_RIGHT)) { 76 | if (data->value_size != NULL && *data->value_size < data->max_size) { 77 | *data->value_size = *data->value_size + 1; 78 | nk_console_trigger_event(console, NK_CONSOLE_EVENT_CHANGED); 79 | } 80 | active = nk_true; 81 | top_data->input_processed = nk_true; 82 | } 83 | } 84 | } 85 | 86 | // Display the label 87 | if (nk_strlen(console->label) > 0) { 88 | if (!nk_console_is_active_widget(console)) { 89 | nk_widget_disable_begin(console->ctx); 90 | } 91 | nk_label(console->ctx, console->label, NK_TEXT_LEFT); 92 | if (!nk_console_is_active_widget(console)) { 93 | nk_widget_disable_end(console->ctx); 94 | } 95 | } 96 | 97 | struct nk_rect widget_bounds = nk_layout_widget_bounds(console->ctx); 98 | 99 | if (console->disabled) { 100 | nk_widget_disable_begin(console->ctx); 101 | } 102 | 103 | // Progress 104 | struct nk_style_item cursor_normal = console->ctx->style.progress.cursor_normal; 105 | struct nk_style_item cursor_hover = console->ctx->style.progress.cursor_hover; 106 | struct nk_style_item cursor_active = console->ctx->style.progress.cursor_active; 107 | if (nk_console_is_active_widget(console)) { 108 | if (active) { 109 | console->ctx->style.progress.cursor_normal = cursor_active; 110 | } 111 | else { 112 | console->ctx->style.progress.cursor_normal = cursor_active; 113 | } 114 | } 115 | 116 | // Display the widget 117 | if (nk_progress(console->ctx, data->value_size, data->max_size, nk_true)) { 118 | nk_console_trigger_event(console, NK_CONSOLE_EVENT_CHANGED); 119 | } 120 | 121 | // Restore the styles 122 | console->ctx->style.progress.cursor_normal = cursor_normal; 123 | console->ctx->style.progress.cursor_hover = cursor_hover; 124 | console->ctx->style.progress.cursor_active = cursor_active; 125 | 126 | if (console->disabled) { 127 | nk_widget_disable_end(console->ctx); 128 | } 129 | 130 | // Allow switching up/down in widgets 131 | if (nk_console_is_active_widget(console)) { 132 | nk_console_check_up_down(console, widget_bounds); 133 | nk_console_check_tooltip(console); 134 | } 135 | 136 | return widget_bounds; 137 | } 138 | 139 | #if defined(__cplusplus) 140 | } 141 | #endif 142 | 143 | #endif // NK_CONSOLE_PROGRESS_IMPLEMENTATION_ONCE 144 | #endif // NK_CONSOLE_IMPLEMENTATION 145 | -------------------------------------------------------------------------------- /nuklear_console_property.h: -------------------------------------------------------------------------------- 1 | #ifndef NK_CONSOLE_PROPERTY_H__ 2 | #define NK_CONSOLE_PROPERTY_H__ 3 | 4 | /** 5 | * Data for Property and Slider widgets. 6 | */ 7 | typedef struct nk_console_property_data { 8 | int min_int; /** The minimum value, represented as an integer. */ 9 | int max_int; /** The maximum value, represented as an integer. */ 10 | int step_int; /** How much each step should increment. */ 11 | float min_float; /** The minimum value, represented as a float. */ 12 | float max_float; /** The maximum value, represented as a float. */ 13 | float step_float; /** How much each step should increment. */ 14 | float inc_per_pixel; /** The increment per pixel value as a float. */ 15 | int* val_int; /** Pointer to the integer value. */ 16 | float* val_float; /** Pointer to the float value. */ 17 | } nk_console_property_data; 18 | 19 | #if defined(__cplusplus) 20 | extern "C" { 21 | #endif 22 | 23 | NK_API nk_console* nk_console_property_int(nk_console* parent, const char* label, int min, int* val, int max, int step, float inc_per_pixel); 24 | NK_API nk_console* nk_console_property_float(nk_console* parent, const char* label, float min, float* val, float max, float step, float inc_per_pixel); 25 | NK_API nk_console* nk_console_slider_int(nk_console* parent, const char* label, int min, int* val, int max, int step); 26 | NK_API nk_console* nk_console_slider_float(nk_console* parent, const char* label, float min, float* val, float max, float step); 27 | NK_API struct nk_rect nk_console_property_render(nk_console* console); 28 | 29 | #if defined(__cplusplus) 30 | } 31 | #endif 32 | 33 | #endif // NK_CONSOLE_PROPERTY_H__ 34 | 35 | #if defined(NK_CONSOLE_IMPLEMENTATION) && !defined(NK_CONSOLE_HEADER_ONLY) 36 | #ifndef NK_CONSOLE_PROPERTY_IMPLEMENTATION_ONCE 37 | #define NK_CONSOLE_PROPERTY_IMPLEMENTATION_ONCE 38 | 39 | #if defined(__cplusplus) 40 | extern "C" { 41 | #endif 42 | 43 | NK_API struct nk_rect nk_console_property_render(nk_console* console) { 44 | nk_console_property_data* data = (nk_console_property_data*)console->data; 45 | if (data == NULL) { 46 | return nk_rect(0, 0, 0, 0); 47 | } 48 | 49 | switch (console->type) { 50 | case NK_CONSOLE_PROPERTY_INT: 51 | case NK_CONSOLE_SLIDER_INT: 52 | case NK_CONSOLE_KNOB_INT: 53 | if (data->val_int == NULL) { 54 | return nk_rect(0, 0, 0, 0); 55 | } 56 | break; 57 | case NK_CONSOLE_PROPERTY_FLOAT: 58 | case NK_CONSOLE_SLIDER_FLOAT: 59 | case NK_CONSOLE_KNOB_FLOAT: 60 | if (data->val_float == NULL) { 61 | return nk_rect(0, 0, 0, 0); 62 | } 63 | break; 64 | default: 65 | return nk_rect(0, 0, 0, 0); 66 | } 67 | 68 | nk_console_layout_widget(console); 69 | 70 | nk_console* top = nk_console_get_top(console); 71 | 72 | // Allow changing the value with left/right 73 | if (!console->disabled && nk_console_is_active_widget(console)) { 74 | nk_console_top_data* top_data = (nk_console_top_data*)top->data; 75 | if (!top_data->input_processed) { 76 | if (nk_console_button_pushed(top, NK_GAMEPAD_BUTTON_LEFT)) { 77 | switch (console->type) { 78 | case NK_CONSOLE_SLIDER_INT: 79 | case NK_CONSOLE_PROPERTY_INT: 80 | case NK_CONSOLE_KNOB_INT: 81 | *data->val_int = *data->val_int - data->step_int; 82 | if (*data->val_int < data->min_int) { 83 | *data->val_int = data->min_int; 84 | } 85 | break; 86 | case NK_CONSOLE_SLIDER_FLOAT: 87 | case NK_CONSOLE_PROPERTY_FLOAT: 88 | case NK_CONSOLE_KNOB_FLOAT: 89 | *data->val_float = *data->val_float - data->step_float; 90 | if (*data->val_float < data->min_float) { 91 | *data->val_float = data->min_float; 92 | } 93 | break; 94 | default: 95 | // Nothing. 96 | break; 97 | } 98 | nk_console_trigger_event(console, NK_CONSOLE_EVENT_CHANGED); 99 | top_data->input_processed = nk_true; 100 | } 101 | else if (nk_console_button_pushed(top, NK_GAMEPAD_BUTTON_RIGHT)) { 102 | switch (console->type) { 103 | case NK_CONSOLE_SLIDER_INT: 104 | case NK_CONSOLE_PROPERTY_INT: 105 | case NK_CONSOLE_KNOB_INT: 106 | *data->val_int = *data->val_int + data->step_int; 107 | if (*data->val_int > data->max_int) { 108 | *data->val_int = data->max_int; 109 | } 110 | break; 111 | case NK_CONSOLE_SLIDER_FLOAT: 112 | case NK_CONSOLE_PROPERTY_FLOAT: 113 | case NK_CONSOLE_KNOB_FLOAT: 114 | *data->val_float = *data->val_float + data->step_float; 115 | if (*data->val_float > data->max_float) { 116 | *data->val_float = data->max_float; 117 | } 118 | break; 119 | default: 120 | // Nothing 121 | break; 122 | } 123 | nk_console_trigger_event(console, NK_CONSOLE_EVENT_CHANGED); 124 | top_data->input_processed = nk_true; 125 | } 126 | } 127 | } 128 | 129 | // Style 130 | enum nk_symbol_type left = console->ctx->style.property.sym_left; 131 | enum nk_symbol_type right = console->ctx->style.property.sym_right; 132 | struct nk_color bar_normal = console->ctx->style.slider.bar_normal; 133 | struct nk_style_item cursor_normal = console->ctx->style.slider.cursor_normal; 134 | struct nk_color knob_normal = console->ctx->style.knob.knob_normal; 135 | struct nk_color knob_border_color = console->ctx->style.knob.knob_border_color; 136 | struct nk_color border_color = console->ctx->style.knob.border_color; 137 | 138 | if (!nk_console_is_active_widget(console)) { 139 | console->ctx->style.property.sym_left = NK_SYMBOL_NONE; 140 | console->ctx->style.property.sym_right = NK_SYMBOL_NONE; 141 | } 142 | else { 143 | console->ctx->style.slider.bar_normal = console->ctx->style.slider.bar_hover; 144 | console->ctx->style.slider.cursor_normal = console->ctx->style.slider.cursor_hover; 145 | console->ctx->style.knob.knob_normal = console->ctx->style.knob.cursor_hover; 146 | console->ctx->style.knob.knob_border_color = console->ctx->style.knob.cursor_hover; 147 | console->ctx->style.knob.border_color = console->ctx->style.knob.cursor_hover; 148 | } 149 | 150 | // Display the label 151 | if (nk_strlen(console->label) > 0) { 152 | if (!nk_console_is_active_widget(console)) { 153 | nk_widget_disable_begin(console->ctx); 154 | } 155 | nk_label(console->ctx, console->label, NK_TEXT_LEFT); 156 | if (!nk_console_is_active_widget(console)) { 157 | nk_widget_disable_end(console->ctx); 158 | } 159 | } 160 | 161 | struct nk_rect widget_bounds = nk_layout_widget_bounds(console->ctx); 162 | 163 | if (console->disabled) { 164 | nk_widget_disable_begin(console->ctx); 165 | } 166 | 167 | // Display the widget 168 | int original_val_int = data->val_int != NULL ? *data->val_int : 0; 169 | float original_val_float = data->val_float != NULL ? *data->val_float : 0; 170 | 171 | switch (console->type) { 172 | case NK_CONSOLE_PROPERTY_INT: { 173 | char name[NK_MAX_NUMBER_BUFFER]; 174 | NK_MEMCPY(name + 2, console->label, (nk_size)(nk_strlen(console->label) + 1)); 175 | name[0] = '#'; 176 | name[1] = '#'; 177 | nk_property_int(console->ctx, name, data->min_int, data->val_int, data->max_int, data->step_int, data->inc_per_pixel); 178 | } break; 179 | case NK_CONSOLE_PROPERTY_FLOAT: { 180 | char name[NK_MAX_NUMBER_BUFFER]; 181 | NK_MEMCPY(name + 2, console->label, (nk_size)(nk_strlen(console->label) + 1)); 182 | name[0] = '#'; 183 | name[1] = '#'; 184 | nk_property_float(console->ctx, name, data->min_float, data->val_float, data->max_float, data->step_float, data->inc_per_pixel); 185 | } break; 186 | case NK_CONSOLE_SLIDER_INT: 187 | nk_slider_int(console->ctx, data->min_int, data->val_int, data->max_int, data->step_int); 188 | break; 189 | case NK_CONSOLE_SLIDER_FLOAT: 190 | nk_slider_float(console->ctx, data->min_float, data->val_float, data->max_float, data->step_float); 191 | break; 192 | case NK_CONSOLE_KNOB_FLOAT: { 193 | nk_console_knob_data* knob_data = (nk_console_knob_data*)console->data; 194 | nk_knob_float(console->ctx, data->min_float, data->val_float, data->max_float, data->step_float, knob_data->zero_direction, knob_data->dead_zone_degrees); 195 | } break; 196 | case NK_CONSOLE_KNOB_INT: { 197 | nk_console_knob_data* knob_data = (nk_console_knob_data*)console->data; 198 | nk_knob_int(console->ctx, data->min_int, data->val_int, data->max_int, data->step_int, knob_data->zero_direction, knob_data->dead_zone_degrees); 199 | } break; 200 | default: 201 | // Nothing 202 | break; 203 | } 204 | 205 | // Invoke the onchange callback if needed. 206 | if (original_val_int != ((data->val_int == NULL) ? 0 : *data->val_int) || original_val_float != ((data->val_float == NULL) ? 0 : *data->val_float)) { 207 | nk_console_trigger_event(console, NK_CONSOLE_EVENT_CHANGED); 208 | } 209 | 210 | // Style Restoration 211 | if (!nk_console_is_active_widget(console)) { 212 | console->ctx->style.property.sym_left = left; 213 | console->ctx->style.property.sym_right = right; 214 | } 215 | else { 216 | console->ctx->style.slider.bar_normal = bar_normal; 217 | console->ctx->style.slider.cursor_normal = cursor_normal; 218 | console->ctx->style.knob.knob_normal = knob_normal; 219 | console->ctx->style.knob.knob_border_color = knob_border_color; 220 | console->ctx->style.knob.border_color = border_color; 221 | } 222 | 223 | if (console->disabled) { 224 | nk_widget_disable_end(console->ctx); 225 | } 226 | 227 | // Allow switching up/down in widgets 228 | if (nk_console_is_active_widget(console)) { 229 | nk_console_check_up_down(console, widget_bounds); 230 | nk_console_check_tooltip(console); 231 | } 232 | 233 | return widget_bounds; 234 | } 235 | 236 | NK_API nk_console* nk_console_property_int(nk_console* parent, const char* label, int min, int* val, int max, int step, float inc_per_pixel) { 237 | // Create the property data. 238 | nk_console_property_data* data = (nk_console_property_data*)NK_CONSOLE_MALLOC(nk_handle_id(0), NULL, sizeof(nk_console_property_data)); 239 | nk_zero(data, sizeof(nk_console_property_data)); 240 | data->min_int = min; 241 | data->val_int = val; 242 | data->max_int = max; 243 | data->step_int = step; 244 | data->inc_per_pixel = inc_per_pixel; 245 | 246 | nk_console* widget = nk_console_label(parent, label); 247 | widget->render = &nk_console_property_render; 248 | widget->type = NK_CONSOLE_PROPERTY_INT; 249 | widget->selectable = nk_true; 250 | widget->data = (void*)data; 251 | widget->columns = label != NULL ? 2 : 1; 252 | 253 | if (val != NULL) { 254 | if (*val < min) { 255 | *val = min; 256 | } 257 | else if (*val > max) { 258 | *val = max; 259 | } 260 | } 261 | 262 | return widget; 263 | } 264 | 265 | NK_API nk_console* nk_console_property_float(nk_console* parent, const char* label, float min, float* val, float max, float step, float inc_per_pixel) { 266 | nk_console* widget = nk_console_property_int(parent, label, 0, NULL, 0, 0, inc_per_pixel); 267 | nk_console_property_data* data = (nk_console_property_data*)widget->data; 268 | widget->type = NK_CONSOLE_PROPERTY_FLOAT; 269 | data->min_float = min; 270 | data->val_float = val; 271 | data->max_float = max; 272 | data->step_float = step; 273 | 274 | if (val != NULL) { 275 | if (*val < min) { 276 | *val = min; 277 | } 278 | else if (*val > max) { 279 | *val = max; 280 | } 281 | } 282 | return widget; 283 | } 284 | 285 | NK_API nk_console* nk_console_slider_int(nk_console* parent, const char* label, int min, int* val, int max, int step) { 286 | nk_console* widget = nk_console_property_int(parent, label, min, val, max, step, 0); 287 | widget->type = NK_CONSOLE_SLIDER_INT; 288 | return widget; 289 | } 290 | 291 | NK_API nk_console* nk_console_slider_float(nk_console* parent, const char* label, float min, float* val, float max, float step) { 292 | nk_console* widget = nk_console_property_float(parent, label, min, val, max, step, 0); 293 | widget->type = NK_CONSOLE_SLIDER_FLOAT; 294 | return widget; 295 | } 296 | 297 | #if defined(__cplusplus) 298 | } 299 | #endif 300 | 301 | #endif // NK_CONSOLE_PROPERTY_IMPLEMENTATION_ONCE 302 | #endif // NK_CONSOLE_IMPLEMENTATION 303 | -------------------------------------------------------------------------------- /nuklear_console_radio.h: -------------------------------------------------------------------------------- 1 | #ifndef NK_CONSOLE_RADIO_H__ 2 | #define NK_CONSOLE_RADIO_H__ 3 | 4 | /** 5 | * Data for the radio button. 6 | * 7 | * @see nk_console_radio() 8 | */ 9 | typedef struct nk_console_radio_data { 10 | int* selected; // The selected index for the radio button within the radio group. 11 | } nk_console_radio_data; 12 | 13 | #if defined(__cplusplus) 14 | extern "C" { 15 | #endif 16 | 17 | /** 18 | * Creates a radio button. Use the same selected pointer to indicate the same radio buttons within the radio group. 19 | * 20 | * @param parent The parent widget. 21 | * @param label The label for the radio button. 22 | * @param selected The selected index for the radio button. Provide the same selected index for all radio buttons in the group. 23 | * @return The radio button widget. 24 | */ 25 | NK_API nk_console* nk_console_radio(nk_console* parent, const char* label, int* selected); 26 | 27 | /** 28 | * Renders the radio button. 29 | * 30 | * @param widget The radio button widget. 31 | * @return The bounds of the radio button. 32 | */ 33 | NK_API struct nk_rect nk_console_radio_render(nk_console* widget); 34 | 35 | /** 36 | * Returns true if the radio button is the selected radio in the group. 37 | * 38 | * @param widget The radio button widget. 39 | * @return True if the radio button is selected. 40 | */ 41 | NK_API nk_bool nk_console_radio_is_selected(nk_console* widget); 42 | 43 | /** 44 | * Returns the index of the radio button in the group. 45 | * 46 | * @param widget The radio button widget. 47 | * @return The index of the radio button. 48 | */ 49 | NK_API int nk_console_radio_index(nk_console* widget); 50 | 51 | #if defined(__cplusplus) 52 | } 53 | #endif 54 | 55 | #endif // NK_CONSOLE_RADIO_H__ 56 | 57 | #if defined(NK_CONSOLE_IMPLEMENTATION) && !defined(NK_CONSOLE_HEADER_ONLY) 58 | #ifndef NK_CONSOLE_RADIO_IMPLEMENTATION_ONCE 59 | #define NK_CONSOLE_RADIO_IMPLEMENTATION_ONCE 60 | 61 | #if defined(__cplusplus) 62 | extern "C" { 63 | #endif 64 | 65 | NK_API int nk_console_radio_index(nk_console* widget) { 66 | if (widget == NULL || widget->data == NULL || widget->parent == NULL || widget->parent->children == NULL) { 67 | return -1; 68 | } 69 | 70 | nk_console_radio_data* data = (nk_console_radio_data*)widget->data; 71 | if (data->selected == NULL) { 72 | return -1; 73 | } 74 | 75 | int index = 0; 76 | for (size_t i = 0; i < cvector_size(widget->parent->children); i++) { 77 | nk_console* child = widget->parent->children[i]; 78 | if (child->type == NK_CONSOLE_RADIO && child->data != NULL) { 79 | nk_console_radio_data* child_data = (nk_console_radio_data*)child->data; 80 | if (data->selected == child_data->selected) { 81 | if (widget == child) { 82 | return index; 83 | } 84 | index++; 85 | } 86 | } 87 | } 88 | 89 | return -1; 90 | } 91 | 92 | NK_API nk_bool nk_console_radio_is_selected(nk_console* widget) { 93 | if (widget == NULL || widget->data == NULL) { 94 | return nk_false; 95 | } 96 | int index = nk_console_radio_index(widget); 97 | if (index < 0) { 98 | return nk_false; 99 | } 100 | nk_console_radio_data* data = (nk_console_radio_data*)widget->data; 101 | if (data->selected == NULL) { 102 | return nk_false; 103 | } 104 | return *data->selected == index; 105 | } 106 | 107 | NK_API struct nk_rect nk_console_radio_render(nk_console* widget) { 108 | if (widget == NULL || widget->label == NULL || widget->data == NULL) { 109 | return nk_recti(0, 0, 0, 0); 110 | } 111 | 112 | nk_console* top = nk_console_get_top(widget); 113 | nk_console_top_data* top_data = (nk_console_top_data*)top->data; 114 | nk_console_radio_data* data = (nk_console_radio_data*)widget->data; 115 | 116 | if (data->selected == NULL) { 117 | return nk_recti(0, 0, 0, 0); 118 | } 119 | 120 | // Set up the layout 121 | nk_console_layout_widget(widget); 122 | 123 | // Grab some initial contexts for the radio button 124 | int current_value = *data->selected; 125 | nk_bool active = nk_console_selectable(widget) && nk_console_is_active_widget(widget); 126 | int index = nk_console_radio_index(widget); 127 | nk_bool selected = nk_console_radio_is_selected(widget); 128 | 129 | // Allow changing the radio value. 130 | if (active && !selected && !top_data->input_processed) { 131 | if (nk_console_button_pushed(top, NK_GAMEPAD_BUTTON_A)) { 132 | *data->selected = index; 133 | selected = nk_true; 134 | top_data->input_processed = nk_true; 135 | nk_console_trigger_event(widget, NK_CONSOLE_EVENT_CLICKED); 136 | } 137 | } 138 | 139 | // Release the disabled state if needed. 140 | if (widget->disabled) { 141 | nk_widget_disable_begin(widget->ctx); 142 | } 143 | 144 | // Style 145 | struct nk_style_item style = widget->ctx->style.option.normal; 146 | if (active) { 147 | if (selected) { 148 | widget->ctx->style.option.normal = widget->ctx->style.option.active; 149 | } 150 | else { 151 | widget->ctx->style.option.normal = widget->ctx->style.option.hover; 152 | } 153 | } 154 | 155 | // Display the ratio button 156 | nk_bool radio_active = selected; 157 | if (widget->alignment != 0) { 158 | nk_flags text_alignment = 0; 159 | nk_flags widget_alignment = 0; 160 | switch (widget->alignment) { 161 | case NK_TEXT_LEFT: 162 | text_alignment = NK_TEXT_ALIGN_LEFT; 163 | widget_alignment = NK_WIDGET_ALIGN_RIGHT; 164 | break; 165 | case NK_TEXT_RIGHT: 166 | text_alignment = NK_TEXT_ALIGN_RIGHT; 167 | widget_alignment = NK_WIDGET_ALIGN_LEFT; 168 | break; 169 | case NK_TEXT_CENTERED: 170 | text_alignment = NK_TEXT_ALIGN_CENTERED; 171 | widget_alignment = NK_WIDGET_ALIGN_CENTERED; 172 | break; 173 | } 174 | 175 | if (widget->label_length > 0) { 176 | nk_radio_text_align(widget->ctx, widget->label, widget->label_length, &radio_active, widget_alignment, text_alignment); 177 | } 178 | else { 179 | nk_radio_label_align(widget->ctx, widget->label, &radio_active, widget_alignment, text_alignment); 180 | } 181 | } 182 | else { 183 | if (widget->label_length > 0) { 184 | nk_radio_text(widget->ctx, widget->label, widget->label_length, &radio_active); 185 | } 186 | else { 187 | nk_radio_label(widget->ctx, widget->label, &radio_active); 188 | } 189 | } 190 | 191 | if (radio_active == nk_true && radio_active != selected) { 192 | *data->selected = index; 193 | nk_console_trigger_event(widget, NK_CONSOLE_EVENT_CLICKED); 194 | top_data->input_processed = nk_true; 195 | } 196 | 197 | // Restore styles 198 | widget->ctx->style.option.normal = style; 199 | 200 | // Release the disabled state if needed. 201 | if (widget->disabled) { 202 | nk_widget_disable_end(widget->ctx); 203 | } 204 | 205 | // Trigger the value changed event 206 | if (current_value != *data->selected) { 207 | nk_console_trigger_event(widget, NK_CONSOLE_EVENT_CHANGED); 208 | } 209 | 210 | // Since labels don't really have widget bounds, we get the bounds after the label is displayed as a work-around. 211 | struct nk_rect widget_bounds = nk_layout_widget_bounds(widget->ctx); 212 | 213 | // Allow switching up/down in widgets 214 | if (active) { 215 | nk_console_check_up_down(widget, widget_bounds); 216 | nk_console_check_tooltip(widget); 217 | } 218 | 219 | return widget_bounds; 220 | } 221 | 222 | NK_API nk_console* nk_console_radio(nk_console* parent, const char* label, int* selected) { 223 | if (parent == NULL || selected == NULL) { 224 | return NULL; 225 | } 226 | 227 | nk_console_radio_data* data = (nk_console_radio_data*)NK_CONSOLE_MALLOC(nk_handle_id(0), NULL, sizeof(nk_console_radio_data)); 228 | nk_zero(data, sizeof(nk_console_radio_data)); 229 | data->selected = selected; 230 | 231 | nk_console* widget = nk_console_label(parent, label); 232 | widget->type = NK_CONSOLE_RADIO; 233 | widget->selectable = nk_true; 234 | widget->columns = 1; 235 | widget->data = data; 236 | widget->render = nk_console_radio_render; 237 | widget->alignment = 0; 238 | return widget; 239 | } 240 | 241 | #if defined(__cplusplus) 242 | } 243 | #endif 244 | 245 | #endif // NK_CONSOLE_RADIO_IMPLEMENTATION_ONCE 246 | #endif // NK_CONSOLE_IMPLEMENTATION 247 | -------------------------------------------------------------------------------- /nuklear_console_row.h: -------------------------------------------------------------------------------- 1 | #ifndef NK_CONSOLE_ROW_H__ 2 | #define NK_CONSOLE_ROW_H__ 3 | 4 | typedef struct nk_console_row_data { 5 | int activeChild; 6 | nk_bool widgets_added; /** Indicates if all the row's widgets have been added. */ 7 | } nk_console_row_data; 8 | 9 | #if defined(__cplusplus) 10 | extern "C" { 11 | #endif 12 | 13 | /** 14 | * Begin a new row in the console. 15 | * 16 | * @param parent The parent of which to create the new row. 17 | * @return The new row. 18 | */ 19 | NK_API nk_console* nk_console_row_begin(nk_console* parent); 20 | 21 | /** 22 | * End the current row in the console. 23 | * 24 | * @param row The row to end. 25 | */ 26 | NK_API void nk_console_row_end(nk_console* row); 27 | NK_API struct nk_rect nk_console_row_render(nk_console* console); 28 | 29 | #if defined(__cplusplus) 30 | } 31 | #endif 32 | 33 | #endif // NK_CONSOLE_ROW_H__ 34 | 35 | #if defined(NK_CONSOLE_IMPLEMENTATION) && !defined(NK_CONSOLE_HEADER_ONLY) 36 | #ifndef NK_CONSOLE_ROW_IMPLEMENTATION_ONCE 37 | #define NK_CONSOLE_ROW_IMPLEMENTATION_ONCE 38 | 39 | #if defined(__cplusplus) 40 | extern "C" { 41 | #endif 42 | 43 | static nk_console* nk_console_row_active_child(nk_console* row) { 44 | if (row == NULL || row->data == NULL) { 45 | return NULL; 46 | } 47 | 48 | int row_children_size = (int)cvector_size(row->children); 49 | if (row_children_size == 0) { 50 | return NULL; 51 | } 52 | 53 | nk_console_row_data* data = (nk_console_row_data*)row->data; 54 | if (data->activeChild < 0) { 55 | data->activeChild = 0; 56 | } 57 | else if (data->activeChild >= row_children_size) { 58 | data->activeChild = row_children_size - 1; 59 | } 60 | 61 | return row->children[data->activeChild]; 62 | } 63 | 64 | // Find the index of the next selectable child by either going right (direction 65 | // = 1) or left (direction = -1). Returns the index of the currently active 66 | // child if no other selectable child is found. 67 | static int nk_console_row_next_selectable_child(nk_console* row, int direction) { 68 | nk_console_row_data* data = (nk_console_row_data*)row->data; 69 | int numChildren = (int)cvector_size(row->children); 70 | for (int i = data->activeChild + direction; i >= 0 && i < numChildren; i += direction) { 71 | if (row->children[i]->selectable) { 72 | return i; 73 | } 74 | } 75 | 76 | return data->activeChild; 77 | } 78 | 79 | /** 80 | * Pick the nearest selectable child to the currently active child. 81 | * 82 | * @param row The row to pick the nearest selectable child from. 83 | * @return True or false if a new selectable child was found. 84 | */ 85 | static nk_bool nk_console_row_pick_nearest_selectable_child(nk_console* row) { 86 | // Ensure there is actually a possible active child. 87 | if (row == NULL || row->data == NULL || nk_console_row_active_child(row) == NULL) { 88 | return nk_false; 89 | } 90 | 91 | nk_console_row_data* data = (nk_console_row_data*)row->data; 92 | int amount = (int)cvector_size(row->children); 93 | for (int i = 1; i < amount; ++i) { 94 | if (data->activeChild + i < amount && row->children[data->activeChild + i]->selectable) { 95 | data->activeChild += i; 96 | return nk_true; 97 | } 98 | if (data->activeChild - i >= 0 && row->children[data->activeChild - i]->selectable) { 99 | data->activeChild -= i; 100 | return nk_true; 101 | } 102 | } 103 | 104 | return nk_false; 105 | } 106 | 107 | NK_API nk_console* nk_console_row_begin(nk_console* parent) { 108 | // Create the row data. 109 | nk_console_row_data* data = (nk_console_row_data*)NK_CONSOLE_MALLOC(nk_handle_id(0), NULL, sizeof(nk_console_row_data)); 110 | nk_zero(data, sizeof(nk_console_row_data)); 111 | 112 | // Create the row. 113 | nk_console* row = nk_console_label(parent, NULL); 114 | row->data = data; 115 | row->alignment = NK_TEXT_ALIGN_CENTERED; 116 | row->type = NK_CONSOLE_ROW; 117 | row->render = nk_console_row_render; 118 | row->columns = 0; 119 | row->selectable = nk_false; 120 | return row; 121 | } 122 | 123 | NK_API void nk_console_row_end(nk_console* row) { 124 | // Make sure there is a row to end that has available children. 125 | nk_console* active_child = nk_console_row_active_child(row); 126 | if (active_child == NULL) { 127 | return; 128 | } 129 | 130 | // Set up the row data based on the available children. 131 | row->columns = 0; 132 | row->selectable = nk_false; 133 | row->height = 0; 134 | int numChildren = (int)cvector_size(row->children); 135 | for (int i = 0; i < numChildren; ++i) { 136 | nk_console* child = row->children[i]; 137 | 138 | // This row is selectable if there's at least one selectable child. 139 | row->selectable |= child->selectable; 140 | 141 | // The row's height is taken from the tallest child. 142 | if (child->height > row->height) { 143 | row->height = child->height; 144 | } 145 | 146 | // Calculate the maximum amount of columns that are in the row 147 | row->columns += child->columns; 148 | } 149 | 150 | // Make sure we start on a selectable child by default. 151 | if (!active_child->selectable) { 152 | nk_console_row_pick_nearest_selectable_child(row); 153 | } 154 | 155 | nk_console_row_data* data = (nk_console_row_data*)row->data; 156 | data->widgets_added = nk_true; 157 | } 158 | 159 | static void nk_console_row_check_left_right(nk_console* row, nk_console* top) { 160 | nk_console_row_data* data = (nk_console_row_data*)row->data; 161 | nk_console_top_data* top_data = (nk_console_top_data*)top->data; 162 | if (top_data->input_processed) { 163 | return; 164 | } 165 | 166 | // Left 167 | if (nk_console_button_pushed(top, NK_GAMEPAD_BUTTON_LEFT)) { 168 | data->activeChild = nk_console_row_next_selectable_child(row, -1); 169 | top_data->input_processed = nk_true; 170 | } 171 | // Right 172 | else if (nk_console_button_pushed(top, NK_GAMEPAD_BUTTON_RIGHT)) { 173 | data->activeChild = nk_console_row_next_selectable_child(row, 1); 174 | top_data->input_processed = nk_true; 175 | } 176 | } 177 | 178 | NK_API struct nk_rect nk_console_row_render(nk_console* console) { 179 | if (console == NULL || console->data == NULL) { 180 | return nk_rect(0, 0, 0, 0); 181 | } 182 | 183 | nk_console_row_data* data = (nk_console_row_data*)console->data; 184 | nk_console* top = nk_console_get_top(console); 185 | nk_console_top_data* top_data = (nk_console_top_data*)top->data; 186 | 187 | // Rows use the advanced layout system to render their children. 188 | nk_layout_row_begin(console->ctx, NK_DYNAMIC, (float)console->height, console->columns); 189 | 190 | struct nk_rect widget_bounds = nk_layout_widget_bounds(console->ctx); 191 | 192 | // Consume mouse movement before children have a chance to. 193 | int numChildren = (int)cvector_size(console->children); 194 | struct nk_input* input = &console->ctx->input; 195 | if (console->selectable && top_data->input_processed == nk_false && 196 | widget_bounds.w > 0 && nk_input_is_mouse_moved(input) && 197 | nk_input_is_mouse_hovering_rect(input, widget_bounds)) { 198 | // First, make sure that the active widget is the row. 199 | nk_console_set_active_widget(console); 200 | 201 | // Find the child that the mouse is hovering over. 202 | if (console->columns > 0) { 203 | float x_percent = (input->mouse.pos.x - widget_bounds.x) / widget_bounds.w; 204 | float column_width_percent = 0.0f; 205 | for (int i = 0; i < numChildren; i++) { 206 | int widget_columns = console->children[i]->columns; 207 | if (widget_columns <= 0) { 208 | widget_columns = 1; 209 | } 210 | column_width_percent += (float)widget_columns / (float)console->columns; 211 | if (x_percent < column_width_percent) { 212 | data->activeChild = i; 213 | break; 214 | } 215 | } 216 | } 217 | 218 | // Ensure the new active child is actually selectable. 219 | if (!nk_console_row_active_child(console)->selectable) { 220 | nk_console_row_pick_nearest_selectable_child(console); 221 | } 222 | } 223 | 224 | // Consume directional input before children have a chance to. 225 | if (nk_console_is_active_widget(console)) { 226 | nk_console_row_check_left_right(console, top); 227 | nk_console_check_up_down(console, widget_bounds); 228 | nk_console* active = nk_console_get_active_widget(console); 229 | 230 | // Attempt to accurately move vertically if the new widget is also a row. 231 | if (active != NULL && active != console && active->type == NK_CONSOLE_ROW && active->data != NULL) { 232 | nk_console_row_data* active_data = (nk_console_row_data*)active->data; 233 | 234 | // Find the desired destination based on the current row child widths. 235 | float desired_destination_percent = 0.0f; 236 | for (int i = 0; i < data->activeChild; i++) { 237 | desired_destination_percent += (float)console->children[i]->columns / (float)console->columns; 238 | } 239 | 240 | // Determine the new active child based on the desired destination percent. 241 | float x = 0.0f; 242 | int active_children_size = (int)cvector_size(active->children); 243 | for (active_data->activeChild = 0; active_data->activeChild < active_children_size; active_data->activeChild++) { 244 | x += (float)active->children[active_data->activeChild]->columns / (float)active->columns; 245 | if (x > desired_destination_percent) { 246 | break; 247 | } 248 | } 249 | 250 | // Ensure we switched to a selectable child. 251 | nk_console* new_active_child = nk_console_row_active_child(active); 252 | if (new_active_child != NULL && !new_active_child->selectable) { 253 | nk_console_row_pick_nearest_selectable_child(active); 254 | } 255 | } 256 | } 257 | 258 | // Set the active widget to the active child of the row. 259 | if (!console->disabled && nk_console_is_active_widget(console) && numChildren > 0) { 260 | console->activeWidget = nk_console_row_active_child(console); 261 | } 262 | 263 | // Render all the children 264 | for (int i = 0; i < numChildren; ++i) { 265 | nk_console* child = console->children[i]; 266 | if (child->render != NULL && child->visible == nk_true) { 267 | // If the row is disabled, then temporarily disable the child when rendering. 268 | if (console->disabled) { 269 | nk_bool child_disabled = child->disabled; 270 | child->disabled = nk_true; 271 | child->render(child); 272 | child->disabled = child_disabled; 273 | } 274 | else { 275 | child->render(child); 276 | } 277 | } 278 | } 279 | 280 | console->activeWidget = NULL; 281 | 282 | // Finished rendering the row, so complete the row layout. 283 | nk_layout_row_end(console->ctx); 284 | 285 | return widget_bounds; 286 | } 287 | 288 | #if defined(__cplusplus) 289 | } 290 | #endif 291 | 292 | #endif // NK_CONSOLE_ROW_IMPLEMENTATION_ONCE 293 | #endif // NK_CONSOLE_IMPLEMENTATION 294 | -------------------------------------------------------------------------------- /nuklear_console_spacing.h: -------------------------------------------------------------------------------- 1 | #ifndef NK_CONSOLE_SPACING_H__ 2 | #define NK_CONSOLE_SPACING_H__ 3 | 4 | #if defined(__cplusplus) 5 | extern "C" { 6 | #endif 7 | 8 | NK_API nk_console* nk_console_spacing(nk_console* parent, int cols); 9 | NK_API struct nk_rect nk_console_spacing_render(nk_console* widget); 10 | 11 | #if defined(__cplusplus) 12 | } 13 | #endif 14 | 15 | #endif // NK_CONSOLE_SPACING_H__ 16 | 17 | #if defined(NK_CONSOLE_IMPLEMENTATION) && !defined(NK_CONSOLE_HEADER_ONLY) 18 | #ifndef NK_CONSOLE_SPACING_IMPLEMENTATION_ONCE 19 | #define NK_CONSOLE_SPACING_IMPLEMENTATION_ONCE 20 | 21 | #if defined(__cplusplus) 22 | extern "C" { 23 | #endif 24 | 25 | NK_API struct nk_rect nk_console_spacing_render(nk_console* widget) { 26 | if (widget == NULL) { 27 | return nk_rect(0, 0, 0, 0); 28 | } 29 | 30 | // Set up the layout for the widget. 31 | nk_console_layout_widget(widget); 32 | 33 | // Render the spacing. 34 | nk_spacing(widget->ctx, widget->columns); 35 | 36 | return nk_rect(0, 0, 0, 0); 37 | } 38 | 39 | NK_API nk_console* nk_console_spacing(nk_console* parent, int cols) { 40 | nk_console* spacing = nk_console_label(parent, NULL); 41 | spacing->type = NK_CONSOLE_SPACING; 42 | spacing->columns = cols; 43 | spacing->render = nk_console_spacing_render; 44 | spacing->disabled = nk_true; 45 | spacing->selectable = nk_false; 46 | return spacing; 47 | } 48 | 49 | #if defined(__cplusplus) 50 | } 51 | #endif 52 | 53 | #endif // NK_CONSOLE_SPACING_IMPLEMENTATION_ONCE 54 | #endif // NK_CONSOLE_IMPLEMENTATION 55 | -------------------------------------------------------------------------------- /nuklear_console_textedit.h: -------------------------------------------------------------------------------- 1 | #ifndef NK_CONSOLE_TEXTEDIT_H__ 2 | #define NK_CONSOLE_TEXTEDIT_H__ 3 | 4 | typedef struct nk_console_textedit_data { 5 | nk_console_button_data button; // Inherited from button. 6 | char* buffer; 7 | int buffer_size; 8 | nk_bool shift; 9 | } nk_console_textedit_data; 10 | 11 | #if defined(__cplusplus) 12 | extern "C" { 13 | #endif 14 | 15 | /** 16 | * Adds a textedit on-screen keyboard widget to the console given parent. 17 | * 18 | * @param parent The parent widget to add the textedit to. 19 | * @param buffer The buffer to store the textedit data. 20 | * @param buffer_size The size of the given buffer. 21 | * @return The created textedit widget. 22 | */ 23 | NK_API nk_console* nk_console_textedit(nk_console* parent, const char* label, char* buffer, int buffer_size); 24 | NK_API struct nk_rect nk_console_textedit_render(nk_console* console); 25 | NK_API void nk_console_textedit_button_main_click(nk_console* button, void* user_data); 26 | NK_API void nk_console_textedit_button_back_click(nk_console* button, void* user_data); 27 | NK_API void nk_console_textedit_key_click(nk_console* key, void* user_data); 28 | 29 | #if defined(__cplusplus) 30 | } 31 | #endif 32 | 33 | #endif // NK_CONSOLE_TEXTEDIT_H__ 34 | 35 | #if defined(NK_CONSOLE_IMPLEMENTATION) && !defined(NK_CONSOLE_HEADER_ONLY) 36 | #ifndef NK_CONSOLE_TEXTEDIT_IMPLEMENTATION_ONCE 37 | #define NK_CONSOLE_TEXTEDIT_IMPLEMENTATION_ONCE 38 | 39 | #if defined(__cplusplus) 40 | extern "C" { 41 | #endif 42 | 43 | static void nk_console_textedit_free_children(nk_console* textedit, void* user_data) { 44 | if (textedit == NULL) { 45 | return; 46 | } 47 | NK_UNUSED(user_data); 48 | nk_console_free_children(textedit); 49 | } 50 | 51 | /** 52 | * Handle the click event for textedit's children items. 53 | */ 54 | NK_API void nk_console_textedit_button_back_click(nk_console* button, void* user_data) { 55 | if (button == NULL) { 56 | return; 57 | } 58 | NK_UNUSED(user_data); 59 | 60 | // Make sure we're not going back to a row. 61 | if (button->parent->type == NK_CONSOLE_ROW) { 62 | button = button->parent; 63 | } 64 | 65 | // Invoke the back button behavior on the button. 66 | nk_console_button_back(button, NULL); 67 | 68 | // Clear out all the children for the textedit, after it finishes rendering. 69 | nk_console_add_event(button->parent, NK_CONSOLE_EVENT_POST_RENDER_ONCE, &nk_console_textedit_free_children); 70 | } 71 | 72 | NK_API void nk_console_textedit_key_click(nk_console* key, void* user_data) { 73 | NK_UNUSED(user_data); 74 | if (key == NULL) { 75 | return; 76 | } 77 | 78 | // Find the textedit widget, and make sure it's not the row. 79 | nk_console* textedit = key->parent; 80 | if (textedit->type == NK_CONSOLE_ROW) { 81 | textedit = textedit->parent; 82 | } 83 | NK_ASSERT(textedit != NULL && textedit->type == NK_CONSOLE_TEXTEDIT); 84 | 85 | // Get the textedit data. 86 | nk_console_textedit_data* data = (nk_console_textedit_data*)textedit->data; 87 | 88 | // Handle the key press 89 | enum nk_symbol_type symbol = nk_console_button_get_symbol(key); 90 | switch (symbol) { 91 | // Shift 92 | case NK_SYMBOL_TRIANGLE_UP: 93 | case NK_SYMBOL_TRIANGLE_UP_OUTLINE: { 94 | data->shift = !data->shift; 95 | nk_console_button_set_symbol(key, symbol == NK_SYMBOL_TRIANGLE_UP ? NK_SYMBOL_TRIANGLE_UP_OUTLINE : NK_SYMBOL_TRIANGLE_UP); 96 | 97 | // Replace all labels of the parent buttons with shifted characters. 98 | // TODO: Clean up shifting the key labels so that it's more dynamic. 99 | int textedit_children_size = (int)cvector_size(textedit->children); 100 | for (int x = 0; x < textedit_children_size; ++x) { 101 | nk_console* child = textedit->children[x]; 102 | if (child->type == NK_CONSOLE_ROW) { 103 | int child_children_size = (int)cvector_size(child->children); 104 | for (int i = 0; i < child_children_size; ++i) { 105 | nk_console* activeButton = child->children[i]; 106 | if (activeButton->type == NK_CONSOLE_BUTTON) { 107 | const char* label = activeButton->label; 108 | if (label != NULL && nk_strlen(label) == 1) { 109 | char old_char = label[0]; 110 | switch (old_char) { 111 | case 'a': label = "A"; break; 112 | case 'b': label = "B"; break; 113 | case 'c': label = "C"; break; 114 | case 'd': label = "D"; break; 115 | case 'e': label = "E"; break; 116 | case 'f': label = "F"; break; 117 | case 'g': label = "G"; break; 118 | case 'h': label = "H"; break; 119 | case 'i': label = "I"; break; 120 | case 'j': label = "J"; break; 121 | case 'k': label = "K"; break; 122 | case 'l': label = "L"; break; 123 | case 'm': label = "M"; break; 124 | case 'n': label = "N"; break; 125 | case 'o': label = "O"; break; 126 | case 'p': label = "P"; break; 127 | case 'q': label = "Q"; break; 128 | case 'r': label = "R"; break; 129 | case 's': label = "S"; break; 130 | case 't': label = "T"; break; 131 | case 'u': label = "U"; break; 132 | case 'v': label = "V"; break; 133 | case 'w': label = "W"; break; 134 | case 'x': label = "X"; break; 135 | case 'y': label = "Y"; break; 136 | case 'z': label = "Z"; break; 137 | case 'A': label = "a"; break; 138 | case 'B': label = "b"; break; 139 | case 'C': label = "c"; break; 140 | case 'D': label = "d"; break; 141 | case 'E': label = "e"; break; 142 | case 'F': label = "f"; break; 143 | case 'G': label = "g"; break; 144 | case 'H': label = "h"; break; 145 | case 'I': label = "i"; break; 146 | case 'J': label = "j"; break; 147 | case 'K': label = "k"; break; 148 | case 'L': label = "l"; break; 149 | case 'M': label = "m"; break; 150 | case 'N': label = "n"; break; 151 | case 'O': label = "o"; break; 152 | case 'P': label = "p"; break; 153 | case 'Q': label = "q"; break; 154 | case 'R': label = "r"; break; 155 | case 'S': label = "s"; break; 156 | case 'T': label = "t"; break; 157 | case 'U': label = "u"; break; 158 | case 'V': label = "v"; break; 159 | case 'W': label = "w"; break; 160 | case 'X': label = "x"; break; 161 | case 'Y': label = "y"; break; 162 | case 'Z': label = "z"; break; 163 | 164 | // Symbols 165 | case '>': label = "."; break; 166 | case '.': label = ">"; break; 167 | case '<': label = ","; break; 168 | case ',': label = "<"; break; 169 | 170 | // Numbers 171 | case '1': label = "!"; break; 172 | case '2': label = "@"; break; 173 | case '3': label = "#"; break; 174 | case '4': label = "$"; break; 175 | case '5': label = "%"; break; 176 | case '6': label = "^"; break; 177 | case '7': label = "&"; break; 178 | case '8': label = "*"; break; 179 | case '9': label = "("; break; 180 | case '0': label = ")"; break; 181 | case '!': label = "1"; break; 182 | case '@': label = "2"; break; 183 | case '#': label = "3"; break; 184 | case '$': label = "4"; break; 185 | case '%': label = "5"; break; 186 | case '^': label = "6"; break; 187 | case '&': label = "7"; break; 188 | case '*': label = "8"; break; 189 | case '(': label = "9"; break; 190 | case ')': label = "0"; break; 191 | } 192 | nk_console_set_label(activeButton, label, 1); 193 | } 194 | } 195 | } 196 | } 197 | } 198 | } break; 199 | 200 | // Backspace 201 | case NK_SYMBOL_TRIANGLE_LEFT: { 202 | int len = nk_strlen(data->buffer); 203 | if (len > 0) { 204 | data->buffer[len - 1] = '\0'; 205 | nk_console_trigger_event(textedit, NK_CONSOLE_EVENT_CHANGED); 206 | } 207 | } break; 208 | 209 | // Space 210 | case NK_SYMBOL_RECT_SOLID: { 211 | int len = nk_strlen(data->buffer); 212 | if (len < data->buffer_size - 1) { 213 | data->buffer[len] = ' '; 214 | data->buffer[len + 1] = '\0'; 215 | nk_console_trigger_event(textedit, NK_CONSOLE_EVENT_CHANGED); 216 | } 217 | } break; 218 | 219 | // Any key character 220 | case NK_SYMBOL_NONE: { 221 | // Add the character to the buffer. 222 | int len = nk_strlen(data->buffer); 223 | if (len < data->buffer_size - 1) { 224 | data->buffer[len] = key->label[0]; 225 | data->buffer[len + 1] = '\0'; 226 | nk_console_trigger_event(textedit, NK_CONSOLE_EVENT_CHANGED); 227 | } 228 | } break; 229 | 230 | default: 231 | break; 232 | } 233 | } 234 | 235 | /** 236 | * Handle the click event for the main button for the textedit. 237 | * 238 | * @see nk_console_textedit 239 | * @internal 240 | */ 241 | NK_API void nk_console_textedit_button_main_click(nk_console* button, void* user_data) { 242 | NK_UNUSED(user_data); 243 | if (button == NULL || button->data == NULL) { 244 | return; 245 | } 246 | 247 | // Make sure there aren't any children to recreate the keyboard. 248 | nk_console_free_children(button); 249 | 250 | nk_console_textedit_data* data = (nk_console_textedit_data*)button->data; 251 | nk_console* key; 252 | 253 | nk_console_textedit_text(button); 254 | 255 | // TODO: Add option for UTF-8 keys with nk_glyph. 256 | 257 | // First row: 1 - 0 258 | nk_console* row = nk_console_row_begin(button); 259 | { 260 | nk_console_button_onclick(row, data->shift ? "!" : "1", &nk_console_textedit_key_click); 261 | nk_console_button_onclick(row, data->shift ? "@" : "2", &nk_console_textedit_key_click); 262 | nk_console_button_onclick(row, data->shift ? "#" : "3", &nk_console_textedit_key_click); 263 | nk_console_button_onclick(row, data->shift ? "$" : "4", &nk_console_textedit_key_click); 264 | nk_console_button_onclick(row, data->shift ? "%" : "5", &nk_console_textedit_key_click); 265 | nk_console_button_onclick(row, data->shift ? "^" : "6", &nk_console_textedit_key_click); 266 | nk_console_button_onclick(row, data->shift ? "&" : "7", &nk_console_textedit_key_click); 267 | nk_console_button_onclick(row, data->shift ? "*" : "8", &nk_console_textedit_key_click); 268 | nk_console_button_onclick(row, data->shift ? "(" : "9", &nk_console_textedit_key_click); 269 | nk_console_button_onclick(row, data->shift ? ")" : "0", &nk_console_textedit_key_click); 270 | } 271 | nk_console_row_end(row); 272 | 273 | // Second row: Q - P 274 | row = nk_console_row_begin(button); 275 | { 276 | nk_console_button_onclick(row, data->shift ? "Q" : "q", &nk_console_textedit_key_click); 277 | nk_console_button_onclick(row, data->shift ? "W" : "w", &nk_console_textedit_key_click); 278 | nk_console_button_onclick(row, data->shift ? "E" : "e", &nk_console_textedit_key_click); 279 | nk_console_button_onclick(row, data->shift ? "R" : "r", &nk_console_textedit_key_click); 280 | nk_console_button_onclick(row, data->shift ? "T" : "t", &nk_console_textedit_key_click); 281 | nk_console_button_onclick(row, data->shift ? "Y" : "y", &nk_console_textedit_key_click); 282 | nk_console_button_onclick(row, data->shift ? "U" : "u", &nk_console_textedit_key_click); 283 | nk_console_button_onclick(row, data->shift ? "I" : "i", &nk_console_textedit_key_click); 284 | nk_console_button_onclick(row, data->shift ? "O" : "o", &nk_console_textedit_key_click); 285 | nk_console_button_onclick(row, data->shift ? "P" : "p", &nk_console_textedit_key_click); 286 | } 287 | nk_console_row_end(row); 288 | 289 | // Third row: A - L 290 | row = nk_console_row_begin(button); 291 | { 292 | nk_console_button_onclick(row, data->shift ? "A" : "a", &nk_console_textedit_key_click); 293 | nk_console_button_onclick(row, data->shift ? "S" : "s", &nk_console_textedit_key_click); 294 | nk_console_button_onclick(row, data->shift ? "D" : "d", &nk_console_textedit_key_click); 295 | nk_console_button_onclick(row, data->shift ? "F" : "f", &nk_console_textedit_key_click); 296 | nk_console_button_onclick(row, data->shift ? "G" : "g", &nk_console_textedit_key_click); 297 | nk_console_button_onclick(row, data->shift ? "H" : "h", &nk_console_textedit_key_click); 298 | nk_console_button_onclick(row, data->shift ? "J" : "j", &nk_console_textedit_key_click); 299 | nk_console_button_onclick(row, data->shift ? "K" : "k", &nk_console_textedit_key_click); 300 | nk_console_button_onclick(row, data->shift ? "L" : "l", &nk_console_textedit_key_click); 301 | nk_console_button_onclick(row, data->shift ? ">" : ".", &nk_console_textedit_key_click); 302 | } 303 | nk_console_row_end(row); 304 | 305 | // Fourth row: Z - M 306 | row = nk_console_row_begin(button); 307 | { 308 | key = nk_console_button_onclick(row, NULL, &nk_console_textedit_key_click); 309 | if (data->shift) { 310 | nk_console_button_set_symbol(key, NK_SYMBOL_TRIANGLE_UP); 311 | } 312 | else { 313 | nk_console_button_set_symbol(key, NK_SYMBOL_TRIANGLE_UP_OUTLINE); 314 | } 315 | nk_console_button_onclick(row, data->shift ? "Z" : "z", &nk_console_textedit_key_click); 316 | nk_console_button_onclick(row, data->shift ? "X" : "x", &nk_console_textedit_key_click); 317 | nk_console_button_onclick(row, data->shift ? "C" : "c", &nk_console_textedit_key_click); 318 | nk_console_button_onclick(row, data->shift ? "V" : "v", &nk_console_textedit_key_click); 319 | nk_console_button_onclick(row, data->shift ? "B" : "b", &nk_console_textedit_key_click); 320 | nk_console_button_onclick(row, data->shift ? "N" : "n", &nk_console_textedit_key_click); 321 | nk_console_button_onclick(row, data->shift ? "M" : "m", &nk_console_textedit_key_click); 322 | nk_console_button_onclick(row, data->shift ? "<" : ",", &nk_console_textedit_key_click); 323 | key = nk_console_button_onclick(row, NULL, &nk_console_textedit_key_click); // Backspace 324 | nk_console_button_set_symbol(key, NK_SYMBOL_TRIANGLE_LEFT); 325 | } 326 | nk_console_row_end(row); 327 | 328 | // Fifth row: Space and Back 329 | row = nk_console_row_begin(button); 330 | { 331 | // Space 332 | key = nk_console_button_onclick(row, NULL, &nk_console_textedit_key_click); // Space 333 | key->columns = 3; 334 | nk_console_button_set_symbol(key, NK_SYMBOL_RECT_SOLID); 335 | 336 | // Back 337 | // TODO: textedit: Replace "Back" with a RETURN symbol? 338 | key = nk_console_button_onclick(row, "Back", &nk_console_textedit_button_back_click); 339 | key->columns = 1; 340 | } 341 | nk_console_row_end(row); 342 | 343 | // Make the onscreen keyboard the active widget. 344 | nk_console_set_active_parent(button); 345 | } 346 | 347 | NK_API nk_console* nk_console_textedit(nk_console* parent, const char* label, char* buffer, int buffer_size) { 348 | // Create the widget data. 349 | nk_console_textedit_data* data = (nk_console_textedit_data*)NK_CONSOLE_MALLOC(nk_handle_id(0), NULL, sizeof(nk_console_textedit_data)); 350 | nk_zero(data, sizeof(nk_console_textedit_data)); 351 | 352 | // Create the textedit widget. 353 | nk_console* textedit = nk_console_label(parent, label); 354 | textedit->type = NK_CONSOLE_TEXTEDIT; 355 | textedit->selectable = nk_true; 356 | data->buffer = buffer; 357 | data->buffer_size = buffer_size; 358 | textedit->columns = label != NULL ? 2 : 1; 359 | textedit->render = nk_console_textedit_render; 360 | textedit->data = data; 361 | 362 | nk_console_add_event(textedit, NK_CONSOLE_EVENT_CLICKED, &nk_console_textedit_button_main_click); 363 | 364 | return textedit; 365 | } 366 | 367 | NK_API struct nk_rect nk_console_textedit_render(nk_console* console) { 368 | nk_console_textedit_data* data = (nk_console_textedit_data*)console->data; 369 | if (data == NULL) { 370 | return nk_rect(0, 0, 0, 0); 371 | } 372 | 373 | nk_console_layout_widget(console); 374 | 375 | // Display the label 376 | if (console->label != NULL && nk_strlen(console->label) > 0) { 377 | nk_bool active = nk_console_is_active_widget(console); 378 | if (!active) { 379 | nk_widget_disable_begin(console->ctx); 380 | } 381 | nk_label(console->ctx, console->label, NK_TEXT_LEFT); 382 | if (!active) { 383 | nk_widget_disable_end(console->ctx); 384 | } 385 | } 386 | 387 | // Display the mocked textedit button 388 | int swap_columns = console->columns; 389 | console->columns = 0; // We use 0 as w'ere not making a new row. 390 | const char* swap_label = console->label; 391 | int swap_label_length = console->label_length; 392 | 393 | console->label = data->buffer; 394 | console->label_length = console->label == NULL ? 0 : nk_strlen(console->label); 395 | if (console->label_length > 10) { 396 | console->label_length = 10; 397 | } 398 | 399 | struct nk_rect widget_bounds = nk_console_button_render(console); 400 | 401 | // Restore the previous values for the textedit widget. 402 | console->columns = swap_columns; 403 | console->label = swap_label; 404 | console->label_length = swap_label_length; 405 | 406 | return widget_bounds; 407 | } 408 | 409 | #if defined(__cplusplus) 410 | } 411 | #endif 412 | 413 | #endif // NK_CONSOLE_TEXTEDIT_IMPLEMENTATION_ONCE 414 | #endif // NK_CONSOLE_IMPLEMENTATION 415 | -------------------------------------------------------------------------------- /nuklear_console_textedit_text.h: -------------------------------------------------------------------------------- 1 | #ifndef NK_CONSOLE_TEXTEDIT_TEXT_H__ 2 | #define NK_CONSOLE_TEXTEDIT_TEXT_H__ 3 | 4 | #if defined(__cplusplus) 5 | extern "C" { 6 | #endif 7 | 8 | /** 9 | * A TextEdit Text widget is a text field that is displayed as part of the on-screen keyboard. 10 | * 11 | * @see nk_console_textedit() 12 | * @internal 13 | */ 14 | NK_API nk_console* nk_console_textedit_text(nk_console* textedit); 15 | NK_API struct nk_rect nk_console_textedit_text_render(nk_console* widget); 16 | 17 | #if defined(__cplusplus) 18 | } 19 | #endif 20 | 21 | #endif // NK_CONSOLE_TEXTEDIT_TEXT_H__ 22 | 23 | #if defined(NK_CONSOLE_IMPLEMENTATION) && !defined(NK_CONSOLE_HEADER_ONLY) 24 | #ifndef NK_CONSOLE_TEXTEDIT_TEXT_IMPLEMENTATION_ONCE 25 | #define NK_CONSOLE_TEXTEDIT_TEXT_IMPLEMENTATION_ONCE 26 | 27 | #if defined(__cplusplus) 28 | extern "C" { 29 | #endif 30 | 31 | NK_API struct nk_rect nk_console_textedit_text_render(nk_console* widget) { 32 | if (widget == NULL) { 33 | return nk_rect(0, 0, 0, 0); 34 | } 35 | 36 | struct nk_rect widget_bounds = nk_layout_widget_bounds(widget->ctx); 37 | 38 | nk_console_layout_widget(widget); 39 | 40 | nk_console* textedit = widget->parent; 41 | nk_console_textedit_data* data = (nk_console_textedit_data*)textedit->data; 42 | nk_console_top_data* top_data = (nk_console_top_data*)nk_console_get_top(widget)->data; 43 | 44 | // Process checking the up/down switching in widgets before processing showing the widget itself 45 | if (nk_console_is_active_widget(widget) && top_data->input_processed == nk_false) { 46 | // Allow using ENTER to go back 47 | if (nk_console_button_pushed(widget, NK_GAMEPAD_BUTTON_A)) { 48 | top_data->input_processed = nk_true; 49 | nk_console_textedit_button_back_click(widget, NULL); 50 | return nk_rect(0, 0, 0, 0); 51 | } 52 | // Allow changing up/down only if they're not pressing backspace 53 | else if (!nk_input_is_key_pressed(&widget->ctx->input, NK_KEY_BACKSPACE)) { 54 | nk_console_check_up_down(widget, widget_bounds); 55 | } 56 | 57 | // Display the tooltip for the textedit. 58 | nk_console_check_tooltip(textedit); 59 | } 60 | 61 | if (widget->disabled) { 62 | nk_widget_disable_begin(widget->ctx); 63 | } 64 | 65 | // Display the text edit 66 | if (nk_console_is_active_widget(widget)) { 67 | nk_edit_focus(widget->ctx, NK_EDIT_FIELD); 68 | } 69 | else { 70 | nk_edit_unfocus(widget->ctx); 71 | } 72 | 73 | // TODO: textedit_text: Add an option to change the filter. 74 | // TODO: textedit_text: Trigger the onchange event when the text changes. 75 | int buffer_strlen = data->buffer != NULL ? (int)nk_strlen(data->buffer) : 0; 76 | nk_edit_string_zero_terminated(widget->ctx, NK_EDIT_FIELD, data->buffer, data->buffer_size, nk_filter_ascii); 77 | if (buffer_strlen != (int)nk_strlen(data->buffer)) { 78 | nk_console_trigger_event(textedit, NK_CONSOLE_EVENT_CHANGED); 79 | } 80 | 81 | if (widget->disabled) { 82 | nk_widget_disable_end(widget->ctx); 83 | } 84 | 85 | return widget_bounds; 86 | } 87 | 88 | NK_API nk_console* nk_console_textedit_text(nk_console* parent) { 89 | nk_console* textedit_text = nk_console_label(parent, parent->label); 90 | textedit_text->type = NK_CONSOLE_TEXTEDIT_TEXT; 91 | textedit_text->parent = parent; 92 | textedit_text->alignment = NK_TEXT_LEFT; 93 | textedit_text->columns = 1; 94 | textedit_text->selectable = nk_true; 95 | textedit_text->render = nk_console_textedit_text_render; 96 | return textedit_text; 97 | } 98 | 99 | #if defined(__cplusplus) 100 | } 101 | #endif 102 | 103 | #endif // NK_CONSOLE_TEXTEDIT_TEXT_IMPLEMENTATION_ONCE 104 | #endif // NK_CONSOLE_IMPLEMENTATION 105 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # pntr 2 | find_package(pntr QUIET) 3 | if (NOT pntr) 4 | include(FetchContent) 5 | FetchContent_Declare( 6 | pntr 7 | GIT_REPOSITORY https://github.com/RobLoach/pntr.git 8 | GIT_TAG 6fff5d4 9 | ) 10 | FetchContent_GetProperties(pntr) 11 | if (NOT pntr) # Have we downloaded raylib yet? 12 | set(FETCHCONTENT_QUIET NO) 13 | FetchContent_Populate(pntr) 14 | add_subdirectory(${pntr_SOURCE_DIR} ${pntr_BINARY_DIR}) 15 | endif() 16 | endif() 17 | 18 | # pntr_nuklear 19 | find_package(pntr_nuklear QUIET) 20 | if (NOT pntr_nuklear) 21 | include(FetchContent) 22 | FetchContent_Declare( 23 | pntr_nuklear 24 | GIT_REPOSITORY https://github.com/RobLoach/pntr_nuklear.git 25 | GIT_TAG d87c940 26 | ) 27 | FetchContent_GetProperties(pntr_nuklear) 28 | if (NOT pntr_nuklear_POPULATED) # Have we downloaded raylib yet? 29 | set(FETCHCONTENT_QUIET NO) 30 | FetchContent_Populate(pntr_nuklear) 31 | add_subdirectory(${pntr_nuklear_SOURCE_DIR} ${pntr_nuklear_BINARY_DIR}) 32 | endif() 33 | endif() 34 | 35 | set(CTEST_OUTPUT_ON_FAILURE TRUE) 36 | 37 | # nuklear_console_test 38 | add_executable(nuklear_console_test nuklear_console_test.c) 39 | target_compile_options(nuklear_console_test PRIVATE -Wall -Wextra -Wconversion -Wsign-conversion) 40 | target_link_libraries(nuklear_console_test PUBLIC 41 | nuklear_console 42 | nuklear_gamepad 43 | pntr 44 | pntr_nuklear 45 | ) 46 | 47 | # Copy the resources 48 | file(GLOB resources resources/*) 49 | set(test_resources) 50 | list(APPEND test_resources ${resources}) 51 | file(COPY ${test_resources} DESTINATION "resources/") 52 | 53 | # Set up the test 54 | list(APPEND CMAKE_CTEST_ARGUMENTS "--output-on-failure") 55 | add_test(NAME nuklear_console_test 56 | COMMAND nuklear_console_test 57 | ) 58 | -------------------------------------------------------------------------------- /test/nuklear_console_test.c: -------------------------------------------------------------------------------- 1 | // This test suite uses pntr_nuklear to render the Nuklear Context to an image. 2 | // https://github.com/RobLoach/pntr_nuklear 3 | 4 | #include 5 | #include 6 | 7 | #define PNTR_ENABLE_DEFAULT_FONT 8 | #define PNTR_IMPLEMENTATION 9 | #include "pntr.h" 10 | 11 | #define NK_INCLUDE_FIXED_TYPES 12 | #define NK_INCLUDE_STANDARD_VARARGS 13 | #define NK_INCLUDE_DEFAULT_ALLOCATOR 14 | #define PNTR_NUKLEAR_IMPLEMENTATION 15 | #include "pntr_nuklear.h" 16 | 17 | #define NK_GAMEPAD_NONE 18 | #define NK_GAMEPAD_IMPLEMENTATION 19 | #include "nuklear_gamepad.h" 20 | 21 | #define NK_CONSOLE_IMPLEMENTATION 22 | #include "nuklear_console.h" 23 | 24 | int main() { 25 | // Load Nuklear 26 | pntr_font* font = pntr_load_font_default(); 27 | assert(font != NULL); 28 | struct nk_context* ctx = pntr_load_nuklear(font); 29 | assert(ctx != NULL); 30 | 31 | // nk_console_init() 32 | struct nk_console* console = nk_console_init(ctx); 33 | assert(console != NULL); 34 | 35 | // Gamepad 36 | struct nk_gamepads gamepads; 37 | assert(nk_gamepad_init(&gamepads, ctx, NULL) == nk_true); 38 | nk_console_set_gamepads(console, &gamepads); 39 | 40 | // nk_console_label() 41 | nk_console* label = nk_console_label(console, "Simple label."); 42 | assert(label != NULL); 43 | 44 | // nk_console_button() 45 | nk_console* button = nk_console_button(console, "Button"); 46 | assert(button != NULL); 47 | 48 | // nk_console_checkbox() 49 | nk_bool checkbox_value = nk_true; 50 | nk_console* checkbox = nk_console_checkbox(console, "Checkbox", &checkbox_value); 51 | assert(checkbox != NULL); 52 | 53 | // nk_console_spacing() 54 | nk_console_label(console, "Spacing:"); 55 | nk_console* spacing = nk_console_spacing(console, 3); 56 | assert(spacing != NULL); 57 | 58 | // nk_console_progress() 59 | nk_size process_value = 20; 60 | nk_console* progress = nk_console_progress(console, "Progress Bar", &process_value, 100); 61 | assert(progress != NULL); 62 | 63 | // nk_console_input() 64 | int gamepad_number = 0; 65 | enum nk_gamepad_button gamepad_button = NK_GAMEPAD_BUTTON_LB; 66 | nk_console* input = nk_console_input(console, "Input Button", -1, &gamepad_number, &gamepad_button); 67 | assert(input != NULL); 68 | 69 | // nk_console_combobox() 70 | int value_combobox = 3; 71 | nk_console* combobox = nk_console_combobox(console, "ComboBox", "Fists;Chainsaw;Pistol;Shotgun;Chaingun", ';', &value_combobox); 72 | assert(combobox != NULL); 73 | 74 | // nk_console_property_int/float() 75 | int property_int_test = 10; 76 | float property_float_test = 0.5f; 77 | nk_console* property_int = nk_console_property_int(console, "Property Int", 10, &property_int_test, 30, 1, 1); 78 | nk_console* property_float = nk_console_property_float(console, "Property Float", 0.0f, &property_float_test, 2.0f, 0.1f, 1); 79 | assert(property_int != NULL); 80 | assert(property_float != NULL); 81 | 82 | // slider_float/int() 83 | float slider_float_test = 1.0f; 84 | int slider_int_test = 15; 85 | nk_console* slider_float = nk_console_slider_float(console, "Slider Float", 0.0f, &slider_float_test, 2.0f, 0.1f); 86 | nk_console* slider_int = nk_console_slider_int(console, "Slider Int", 0, &slider_int_test, 20, 1); 87 | assert(slider_float != NULL); 88 | assert(slider_int != NULL); 89 | 90 | // nk_console_textedit() 91 | static const int textedit_buffer_size = 256; 92 | static char textedit_buffer[256] = "brianwatling"; 93 | nk_console* textedit = nk_console_textedit(console, "Textedit", textedit_buffer, textedit_buffer_size); 94 | assert(textedit != NULL); 95 | 96 | // nk_console_color() 97 | struct nk_colorf color_value = {0.31f, 1.0f, 0.48f, 1.0f}; 98 | nk_console* color = nk_console_color(console, "Color", &color_value, NK_RGBA); 99 | assert(color != NULL); 100 | 101 | // nk_console_file() 102 | static const int file_path_buffer_size = 256; 103 | static char file_path_buffer[256] = ""; 104 | nk_console* file = nk_console_file(console, "File", file_path_buffer, file_path_buffer_size); 105 | assert(file != NULL); 106 | 107 | // nk_console_image() 108 | pntr_image* image_value = pntr_load_image("resources/image.png"); 109 | assert(image_value != NULL); 110 | nk_console* image = nk_console_image(console, pntr_image_nk(image_value)); 111 | nk_console_set_height(image, image_value->height); 112 | assert(image != NULL); 113 | 114 | // nk_console_show_message() 115 | nk_console_show_message(console, "This is an info message"); 116 | 117 | // nk_console_row() 118 | nk_console* row = nk_console_row_begin(console); 119 | assert(row != NULL); 120 | nk_console_label(row, "Row Column 1")->alignment = NK_TEXT_CENTERED; 121 | nk_console_label(row, "Row Column 2")->alignment = NK_TEXT_CENTERED; 122 | nk_console_row_end(row); 123 | 124 | // nk_console_selectable() 125 | nk_console* widget = nk_console_label(console, "Selectable"); 126 | widget->selectable = nk_false; 127 | widget->disabled = nk_false; 128 | assert(nk_console_selectable(widget) == nk_false); 129 | widget->selectable = nk_true; 130 | assert(nk_console_selectable(widget) == nk_true); 131 | widget->disabled = nk_true; 132 | assert(nk_console_selectable(widget) == nk_false); 133 | widget->selectable = nk_true; 134 | widget->disabled = nk_false; 135 | widget->visible = nk_true; 136 | assert(nk_console_selectable(widget) == nk_true); 137 | widget->visible = nk_false; 138 | assert(nk_console_selectable(widget) == nk_false); 139 | 140 | // Create the screen buffer 141 | pntr_image* screen = pntr_new_image(300, 800); 142 | assert(screen != NULL); 143 | 144 | // nk_console_rener_window() 145 | nk_console_render_window(console, "nuklear_console_test", nk_rect(0, 0, (float)screen->width, (float)screen->height), NK_WINDOW_TITLE); 146 | 147 | // Draw the nuklear context on the screen. 148 | pntr_draw_nuklear(screen, ctx); 149 | 150 | // Save the output test image. 151 | pntr_save_image(screen, "nuklear_console_test.png"); 152 | 153 | // Unload 154 | nk_console_free(console); 155 | pntr_unload_nuklear(ctx); 156 | pntr_unload_image(image_value); 157 | pntr_unload_image(screen); 158 | pntr_unload_font(font); 159 | 160 | printf("nuklear_console_test: Tests passed.\n"); 161 | 162 | return 0; 163 | } 164 | -------------------------------------------------------------------------------- /test/resources/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/nuklear_console/e6001e75d0f237356865bb4306cc97d174640d1d/test/resources/image.png --------------------------------------------------------------------------------