├── .clang-format ├── .clang-tidy ├── .codespellrc ├── .gdlintrc ├── .gitattributes ├── .github └── workflows │ └── master.yml ├── .gitignore ├── .gitmodules ├── .markdownlint.yaml ├── LICENSE.md ├── README.md ├── SConstruct ├── demo ├── addons │ └── native_dialogs │ │ ├── icons │ │ ├── native_accept_dialog.svg │ │ ├── native_accept_dialog.svg.import │ │ ├── native_confirmation_dialog.svg │ │ ├── native_confirmation_dialog.svg.import │ │ ├── native_file_dialog.svg │ │ ├── native_file_dialog.svg.import │ │ ├── native_notification.svg │ │ └── native_notification.svg.import │ │ ├── native_dialogs.gdextension │ │ ├── plugin.cfg │ │ └── plugin.gd ├── icon.png ├── icon.png.import ├── icons │ ├── checked.png │ ├── checked.png.import │ ├── unchecked.png │ └── unchecked.png.import ├── main.tscn ├── main_theme.tres ├── project.godot └── scenes │ ├── native_accept_dialog.gd │ ├── native_accept_dialog.tscn │ ├── native_confirmation_dialog.gd │ ├── native_confirmation_dialog.tscn │ ├── native_file_dialog.gd │ ├── native_file_dialog.tscn │ ├── native_notification.gd │ └── native_notification.tscn ├── doc ├── images │ ├── code_usage.png │ ├── editor_usage.png │ ├── native_accept_dialog.png │ ├── native_confirmation_dialog.png │ ├── native_dialogs_banner.png │ ├── native_dialogs_icon.png │ ├── native_file_dialog.png │ ├── native_notification.png │ └── windows_example.png ├── native_accept_dialog.md ├── native_confirmation_dialog.md ├── native_file_dialog.md └── native_notification.md └── src ├── native_accept_dialog.cpp ├── native_accept_dialog.h ├── native_confirmation_dialog.cpp ├── native_confirmation_dialog.h ├── native_file_dialog.cpp ├── native_file_dialog.h ├── native_notification.cpp ├── native_notification.h ├── register_types.cpp └── register_types.h /.clang-format: -------------------------------------------------------------------------------- 1 | # From Godot Engine's repository 2 | # Commented out parameters are those with the same value as base LLVM style. 3 | # We can uncomment them if we want to change their value, or enforce the 4 | # chosen value in case the base style changes (last sync: Clang 14.0). 5 | --- 6 | ### General config, applies to all languages ### 7 | BasedOnStyle: LLVM 8 | AccessModifierOffset: -4 9 | AlignAfterOpenBracket: DontAlign 10 | # AlignArrayOfStructures: None 11 | # AlignConsecutiveMacros: None 12 | # AlignConsecutiveAssignments: None 13 | # AlignConsecutiveBitFields: None 14 | # AlignConsecutiveDeclarations: None 15 | # AlignEscapedNewlines: Right 16 | AlignOperands: DontAlign 17 | AlignTrailingComments: false 18 | # AllowAllArgumentsOnNextLine: true 19 | AllowAllParametersOfDeclarationOnNextLine: false 20 | # AllowShortEnumsOnASingleLine: true 21 | # AllowShortBlocksOnASingleLine: Never 22 | # AllowShortCaseLabelsOnASingleLine: false 23 | # AllowShortFunctionsOnASingleLine: All 24 | # AllowShortLambdasOnASingleLine: All 25 | # AllowShortIfStatementsOnASingleLine: Never 26 | # AllowShortLoopsOnASingleLine: false 27 | # AlwaysBreakAfterDefinitionReturnType: None 28 | # AlwaysBreakAfterReturnType: None 29 | # AlwaysBreakBeforeMultilineStrings: false 30 | # AlwaysBreakTemplateDeclarations: MultiLine 31 | # AttributeMacros: 32 | # - __capability 33 | # BinPackArguments: true 34 | # BinPackParameters: true 35 | # BraceWrapping: 36 | # AfterCaseLabel: false 37 | # AfterClass: false 38 | # AfterControlStatement: Never 39 | # AfterEnum: false 40 | # AfterFunction: false 41 | # AfterNamespace: false 42 | # AfterObjCDeclaration: false 43 | # AfterStruct: false 44 | # AfterUnion: false 45 | # AfterExternBlock: false 46 | # BeforeCatch: false 47 | # BeforeElse: false 48 | # BeforeLambdaBody: false 49 | # BeforeWhile: false 50 | # IndentBraces: false 51 | # SplitEmptyFunction: true 52 | # SplitEmptyRecord: true 53 | # SplitEmptyNamespace: true 54 | # BreakBeforeBinaryOperators: None 55 | # BreakBeforeConceptDeclarations: true 56 | # BreakBeforeBraces: Attach 57 | # BreakBeforeInheritanceComma: false 58 | # BreakInheritanceList: BeforeColon 59 | # BreakBeforeTernaryOperators: true 60 | # BreakConstructorInitializersBeforeComma: false 61 | BreakConstructorInitializers: AfterColon 62 | # BreakStringLiterals: true 63 | ColumnLimit: 0 64 | # CommentPragmas: '^ IWYU pragma:' 65 | # QualifierAlignment: Leave 66 | # CompactNamespaces: false 67 | ConstructorInitializerIndentWidth: 8 68 | ContinuationIndentWidth: 8 69 | Cpp11BracedListStyle: false 70 | # DeriveLineEnding: true 71 | # DerivePointerAlignment: false 72 | # DisableFormat: false 73 | # EmptyLineAfterAccessModifier: Never 74 | # EmptyLineBeforeAccessModifier: LogicalBlock 75 | # ExperimentalAutoDetectBinPacking: false 76 | # PackConstructorInitializers: BinPack 77 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 78 | # AllowAllConstructorInitializersOnNextLine: true 79 | # FixNamespaceComments: true 80 | # ForEachMacros: 81 | # - foreach 82 | # - Q_FOREACH 83 | # - BOOST_FOREACH 84 | # IfMacros: 85 | # - KJ_IF_MAYBE 86 | # IncludeBlocks: Preserve 87 | IncludeCategories: 88 | - Regex: '".*"' 89 | Priority: 1 90 | - Regex: '^<.*\.h>' 91 | Priority: 2 92 | - Regex: '^<.*' 93 | Priority: 3 94 | # IncludeIsMainRegex: '(Test)?$' 95 | # IncludeIsMainSourceRegex: '' 96 | # IndentAccessModifiers: false 97 | IndentCaseLabels: true 98 | # IndentCaseBlocks: false 99 | # IndentGotoLabels: true 100 | # IndentPPDirectives: None 101 | # IndentExternBlock: AfterExternBlock 102 | # IndentRequires: false 103 | IndentWidth: 4 104 | # IndentWrappedFunctionNames: false 105 | # InsertTrailingCommas: None 106 | # JavaScriptQuotes: Leave 107 | # JavaScriptWrapImports: true 108 | KeepEmptyLinesAtTheStartOfBlocks: false 109 | # LambdaBodyIndentation: Signature 110 | # MacroBlockBegin: '' 111 | # MacroBlockEnd: '' 112 | # MaxEmptyLinesToKeep: 1 113 | # NamespaceIndentation: None 114 | # PenaltyBreakAssignment: 2 115 | # PenaltyBreakBeforeFirstCallParameter: 19 116 | # PenaltyBreakComment: 300 117 | # PenaltyBreakFirstLessLess: 120 118 | # PenaltyBreakOpenParenthesis: 0 119 | # PenaltyBreakString: 1000 120 | # PenaltyBreakTemplateDeclaration: 10 121 | # PenaltyExcessCharacter: 1000000 122 | # PenaltyReturnTypeOnItsOwnLine: 60 123 | # PenaltyIndentedWhitespace: 0 124 | # PointerAlignment: Right 125 | # PPIndentWidth: -1 126 | # ReferenceAlignment: Pointer 127 | # ReflowComments: true 128 | # RemoveBracesLLVM: false 129 | # SeparateDefinitionBlocks: Leave 130 | # ShortNamespaceLines: 1 131 | # SortIncludes: CaseSensitive 132 | # SortJavaStaticImport: Before 133 | # SortUsingDeclarations: true 134 | # SpaceAfterCStyleCast: false 135 | # SpaceAfterLogicalNot: false 136 | # SpaceAfterTemplateKeyword: true 137 | # SpaceBeforeAssignmentOperators: true 138 | # SpaceBeforeCaseColon: false 139 | # SpaceBeforeCpp11BracedList: false 140 | # SpaceBeforeCtorInitializerColon: true 141 | # SpaceBeforeInheritanceColon: true 142 | # SpaceBeforeParens: ControlStatements 143 | # SpaceBeforeParensOptions: 144 | # AfterControlStatements: true 145 | # AfterForeachMacros: true 146 | # AfterFunctionDefinitionName: false 147 | # AfterFunctionDeclarationName: false 148 | # AfterIfMacros: true 149 | # AfterOverloadedOperator: false 150 | # BeforeNonEmptyParentheses: false 151 | # SpaceAroundPointerQualifiers: Default 152 | # SpaceBeforeRangeBasedForLoopColon: true 153 | # SpaceInEmptyBlock: false 154 | # SpaceInEmptyParentheses: false 155 | # SpacesBeforeTrailingComments: 1 156 | # SpacesInAngles: Never 157 | # SpacesInConditionalStatement: false 158 | # SpacesInContainerLiterals: true 159 | # SpacesInCStyleCastParentheses: false 160 | ## Godot TODO: We'll want to use a min of 1, but we need to see how to fix 161 | ## our comment capitalization at the same time. 162 | SpacesInLineCommentPrefix: 163 | Minimum: 0 164 | Maximum: -1 165 | # SpacesInParentheses: false 166 | # SpacesInSquareBrackets: false 167 | # SpaceBeforeSquareBrackets: false 168 | # BitFieldColonSpacing: Both 169 | # StatementAttributeLikeMacros: 170 | # - Q_EMIT 171 | # StatementMacros: 172 | # - Q_UNUSED 173 | # - QT_REQUIRE_VERSION 174 | TabWidth: 4 175 | # UseCRLF: false 176 | UseTab: Always 177 | # WhitespaceSensitiveMacros: 178 | # - STRINGIZE 179 | # - PP_STRINGIZE 180 | # - BOOST_PP_STRINGIZE 181 | # - NS_SWIFT_NAME 182 | # - CF_SWIFT_NAME 183 | --- 184 | ### C++ specific config ### 185 | Language: Cpp 186 | Standard: c++17 187 | --- 188 | ### ObjC specific config ### 189 | Language: ObjC 190 | # ObjCBinPackProtocolList: Auto 191 | ObjCBlockIndentWidth: 4 192 | # ObjCBreakBeforeNestedBlockParam: true 193 | # ObjCSpaceAfterProperty: false 194 | # ObjCSpaceBeforeProtocolList: true 195 | --- 196 | ### Java specific config ### 197 | Language: Java 198 | # BreakAfterJavaFieldAnnotations: false 199 | JavaImportGroups: ['org.godotengine', 'android', 'androidx', 'com.android', 'com.google', 'java', 'javax'] 200 | ... 201 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | # From Godot Engine's repository 2 | --- 3 | Checks: 'clang-diagnostic-*,clang-analyzer-*,-*,cppcoreguidelines-pro-type-member-init,modernize-redundant-void-arg,modernize-use-bool-literals,modernize-use-default-member-init,modernize-use-nullptr,readability-braces-around-statements,readability-redundant-member-init' 4 | WarningsAsErrors: '' 5 | HeaderFilterRegex: '' 6 | AnalyzeTemporaryDtors: false 7 | FormatStyle: none 8 | CheckOptions: 9 | - key: cert-dcl16-c.NewSuffixes 10 | value: 'L;LL;LU;LLU' 11 | - key: cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField 12 | value: '0' 13 | - key: cppcoreguidelines-explicit-virtual-functions.IgnoreDestructors 14 | value: '1' 15 | - key: cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic 16 | value: '1' 17 | - key: cppcoreguidelines-pro-type-member-init.IgnoreArrays 18 | value: '1' 19 | - key: cppcoreguidelines-pro-type-member-init.UseAssignment 20 | value: '1' 21 | - key: google-readability-function-size.StatementThreshold 22 | value: '800' 23 | - key: google-readability-namespace-comments.ShortNamespaceLines 24 | value: '10' 25 | - key: google-readability-namespace-comments.SpacesBeforeComments 26 | value: '2' 27 | - key: modernize-loop-convert.MaxCopySize 28 | value: '16' 29 | - key: modernize-loop-convert.MinConfidence 30 | value: reasonable 31 | - key: modernize-loop-convert.NamingStyle 32 | value: CamelCase 33 | - key: modernize-pass-by-value.IncludeStyle 34 | value: llvm 35 | - key: modernize-replace-auto-ptr.IncludeStyle 36 | value: llvm 37 | - key: modernize-use-bool-literals.IgnoreMacros 38 | value: '0' 39 | - key: modernize-use-default-member-init.IgnoreMacros 40 | value: '0' 41 | - key: modernize-use-default-member-init.UseAssignment 42 | value: '1' 43 | - key: modernize-use-nullptr.NullMacros 44 | value: 'NULL' 45 | - key: readability-braces-around-statements.ShortStatementLines 46 | value: '0' 47 | ... -------------------------------------------------------------------------------- /.codespellrc: -------------------------------------------------------------------------------- 1 | [codespell] 2 | skip = godot-cpp,portable-file-dialogs 3 | builtin = en-GB_to_en-US 4 | -------------------------------------------------------------------------------- /.gdlintrc: -------------------------------------------------------------------------------- 1 | class-definitions-order: 2 | - tools 3 | - classnames 4 | - extends 5 | - signals 6 | - enums 7 | - consts 8 | - exports 9 | - pubvars 10 | - prvvars 11 | - onreadypubvars 12 | - onreadyprvvars 13 | - others 14 | class-load-variable-name: (([A-Z][a-z0-9]*)+|_?[a-z][a-z0-9]*(_[a-z0-9]+)*) 15 | class-name: ([A-Z][a-z0-9]*)+ 16 | class-variable-name: _?[a-z][a-z0-9]*(_[a-z0-9]+)* 17 | comparison-with-itself: null 18 | constant-name: '[A-Z][A-Z0-9]*(_[A-Z0-9]+)*' 19 | disable: [] 20 | duplicated-load: null 21 | enum-element-name: '[A-Z][A-Z0-9]*(_[A-Z0-9]+)*' 22 | enum-name: ([A-Z][a-z0-9]*)+ 23 | excluded_directories: !!set 24 | .git: null 25 | expression-not-assigned: null 26 | function-argument-name: _?[a-z][a-z0-9]*(_[a-z0-9]+)* 27 | function-arguments-number: 10 28 | function-name: (_on_([A-Z][a-z0-9]*)+(_[a-z0-9]+)*|_?[a-z][a-z0-9]*(_[a-z0-9]+)*) 29 | function-preload-variable-name: ([A-Z][a-z0-9]*)+ 30 | function-variable-name: '[a-z][a-z0-9]*(_[a-z0-9]+)*' 31 | load-constant-name: (([A-Z][a-z0-9]*)+|[A-Z][A-Z0-9]*(_[A-Z0-9]+)*) 32 | loop-variable-name: _?[a-z][a-z0-9]*(_[a-z0-9]+)* 33 | max-file-lines: 1000 34 | max-line-length: 100 35 | max-public-methods: 20 36 | max-returns: 6 37 | mixed-tabs-and-spaces: null 38 | no-elif-return: null 39 | no-else-return: null 40 | private-method-call: null 41 | signal-name: '[a-z][a-z0-9]*(_[a-z0-9]+)*' 42 | sub-class-name: _?([A-Z][a-z0-9]*)+ 43 | tab-characters: 1 44 | trailing-whitespace: null 45 | unnecessary-pass: null 46 | unused-argument: null 47 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.c eol=lf 2 | *.cpp eol=lf 3 | *.gd eol=lf 4 | *.tscn eol=lf 5 | *.cfg eol=lf 6 | *.godot eol=lf 7 | *.tres eol=lf 8 | *.gdnlib eol=lf 9 | *.gdns eol=lf 10 | -------------------------------------------------------------------------------- /.github/workflows/master.yml: -------------------------------------------------------------------------------- 1 | name: 🌟 Master 2 | 3 | on: 4 | pull_request: 5 | branches: [master] 6 | 7 | concurrency: 8 | group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}}-macos 9 | cancel-in-progress: true 10 | 11 | jobs: 12 | gdscript: 13 | name: 💅 Linting / 🤖 GDScript 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v3 17 | - run: pip3 install "gdtoolkit==4.*" 18 | - run: bash -c 'for f in ./demo/**/*.gd; do gdlint "$f"; done' 19 | 20 | cplusplus: 21 | name: 💅 Linting / ➕ C++ 22 | runs-on: ubuntu-latest 23 | steps: 24 | - uses: actions/checkout@v3 25 | - uses: jidicula/clang-format-action@v4.9.0 26 | with: 27 | check-path: src 28 | 29 | markdown: 30 | name: 💅 Linting / 📝 Markdown 31 | runs-on: ubuntu-latest 32 | steps: 33 | - uses: actions/checkout@v3 34 | - uses: DavidAnson/markdownlint-cli2-action@v9 35 | with: 36 | globs: | 37 | README.md 38 | doc/**/*.md 39 | 40 | spelling: 41 | name: 💅 Linting / 👓 Spelling 42 | runs-on: ubuntu-latest 43 | steps: 44 | - uses: actions/checkout@v3 45 | - uses: codespell-project/actions-codespell@v1 46 | 47 | build: 48 | strategy: 49 | fail-fast: false 50 | matrix: 51 | include: 52 | - identifier: windows-debug 53 | os: windows-latest 54 | name: 🏗 Builds / 🪟 Windows Debug 55 | target: template_debug 56 | platform: windows 57 | arch: x86_64 58 | 59 | - identifier: windows-release 60 | os: windows-latest 61 | name: 🏗 Builds / 🪟 Windows Release 62 | target: template_release 63 | platform: windows 64 | arch: x86_64 65 | 66 | - identifier: macos-debug 67 | os: macos-latest 68 | name: 🏗 Builds / 🍎 macOS (universal) Debug 69 | target: template_debug 70 | platform: macos 71 | arch: universal 72 | 73 | - identifier: macos-release 74 | os: macos-latest 75 | name: 🏗 Builds / 🍎 macOS (universal) Release 76 | target: template_release 77 | platform: macos 78 | arch: universal 79 | 80 | - identifier: linux-debug 81 | os: ubuntu-latest 82 | name: 🏗 Builds / 🐧 Linux Debug 83 | runner: ubuntu-20.04 84 | target: template_debug 85 | platform: linux 86 | arch: x86_64 87 | 88 | - identifier: linux-release 89 | os: ubuntu-latest 90 | name: 🏗 Builds / 🐧 Linux Release 91 | runner: ubuntu-20.04 92 | target: template_release 93 | platform: linux 94 | arch: x86_64 95 | 96 | runs-on: ${{matrix.os}} 97 | name: ${{matrix.name}} 98 | steps: 99 | - name: Checkout project 100 | uses: actions/checkout@v3 101 | with: 102 | submodules: recursive 103 | 104 | - name: Set up Python 105 | uses: actions/setup-python@v4 106 | with: 107 | python-version: "3.x" 108 | 109 | - name: Set up SCons 110 | shell: bash 111 | run: | 112 | python -c "import sys; print(sys.version)" 113 | python -m pip install scons 114 | scons --version 115 | 116 | - name: Compile godot-cpp 117 | shell: sh 118 | run: | 119 | scons target='${{ matrix.target }}' platform='${{ matrix.platform }}' arch='${{ matrix.arch }}' 120 | working-directory: godot-cpp 121 | 122 | - name: Compile Extension 123 | shell: sh 124 | run: | 125 | scons target='${{ matrix.target }}' platform='${{ matrix.platform }}' arch='${{ matrix.arch }}' 126 | 127 | - name: Delete compilation files 128 | if: ${{ matrix.platform == 'windows' }} 129 | run: | 130 | Remove-Item demo/addons/native_dialogs/bin/* -Include *.exp,*.lib,*.pdb -Force 131 | 132 | - name: Upload binaries 133 | uses: actions/upload-artifact@v3 134 | with: 135 | name: binaries 136 | path: demo/addons/native_dialogs/bin/* 137 | 138 | package: 139 | name: 📦 Package 140 | needs: build 141 | runs-on: ubuntu-latest 142 | steps: 143 | - uses: actions/checkout@v3 144 | 145 | - name: Create addon 146 | run: | 147 | mkdir addons 148 | cp -R demo/addons/native_dialogs addons 149 | cp LICENSE.md addons/native_dialogs 150 | mkdir addons/native_dialogs/bin 151 | 152 | - name: Download binaries 153 | uses: actions/download-artifact@v3 154 | with: 155 | name: binaries 156 | path: addons/native_dialogs/bin 157 | 158 | - name: Create package 159 | run: | 160 | zip -r native_dialogs.zip addons 161 | 162 | - name: Upload addon 163 | uses: actions/upload-artifact@v3 164 | with: 165 | name: native_dialogs 166 | path: native_dialogs.zip 167 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | zconf.h 3 | zconf.h.included 4 | *.o 5 | *.os 6 | .import/ 7 | *.dblite 8 | *.exp 9 | *.lib 10 | *.obj 11 | *.TMP 12 | logs 13 | Thumbs.db 14 | **/bin/ 15 | export_presets.cfg 16 | .vscode 17 | .godot 18 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "godot-cpp"] 2 | path = godot-cpp 3 | url = https://github.com/godotengine/godot-cpp 4 | [submodule "portable-file-dialogs"] 5 | path = portable-file-dialogs 6 | url = https://github.com/samhocevar/portable-file-dialogs 7 | -------------------------------------------------------------------------------- /.markdownlint.yaml: -------------------------------------------------------------------------------- 1 | MD013: false 2 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Tomás Espejo Gómez 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Native Dialogs 2 | 3 | ![Native Dialogs](./doc/images/native_dialogs_banner.png) 4 | 5 | > **Warning** 6 | > The main branch of this repository now supports Godot 4.x. If you are looking for the Godot 3.x version, please check [this branch](https://github.com/98teg/NativeDialogs/tree/godot-3.x). 7 | 8 | ## Introduction 9 | 10 | Native Dialogs is a plugin for Godot that allows you to interact with OS-specific dialogs, such as notifications, messages and file dialogs. It adds four new nodes that wrap the functionality of the C++ Library [portable-file-dialogs](https://github.com/samhocevar/portable-file-dialogs). 11 | 12 | [`NativeAcceptDialog`](./doc/native_accept_dialog.md) is useful for small notifications to the user about an event. [`NativeConfirmationDialog`](./doc/native_confirmation_dialog.md) allows you to prompt the user about confirmation of actions. [`NativeNotification`](./doc/native_notification.md) would show the user a brief message and then fade away. And [`NativeFileDialog`](./doc/native_file_dialog.md) is used to choose files and directories in the filesystem. 13 | 14 | It supports the following operating systems: 15 | 16 | * Windows 17 | * MacOS 18 | * Linux 19 | 20 | Please note that this plugin follows [Godot's file paths format](https://docs.godotengine.org/en/stable/tutorials/io/data_paths.html), and it uses [`ProjectSettings`' `globalize_path` method](https://docs.godotengine.org/en/stable/classes/class_projectsettings.html?highlight=ProjectSettings#class-projectsettings-method-globalize-path) to resolve `user://`, and `res://`, so consult the documentation if you have any doubt. 21 | 22 | ## How to use it 23 | 24 | As previously said, this plugin adds four new nodes. To use any of these nodes' functionality, you first need to add them to the scene. Then, update any of their properties if necessary. You also need to connect their signals to any method if you want to process the user's input. Finally, call the node's function that shows the native dialog to the user. 25 | 26 | ### Using the editor 27 | 28 | Probably the more intuitive way of using this plugin: 29 | 30 | 1. Instantiate a Native Dialog's node in your scene. 31 | 2. Change any of its properties, if needed. 32 | 3. Connect its signal if you want to process the user's input. 33 | 4. Make sure to call the `show` or `send` method of the Native Dialog's node. For example, via a button's signal. 34 | 35 | ![Example using the editor](./doc/images/editor_usage.png) 36 | 37 | ### Using code 38 | 39 | If you want to instantiate nodes using GDScript, you may follow this example: 40 | 41 | 1. Instantiate the desired node. 42 | 2. Change any of its attributes. 43 | 3. Connect its corresponding signals to the methods that would process the user's input. 44 | 4. Add it to the scene. 45 | 5. Call the `show` or `send` method when you need to show the native dialog. 46 | 47 | ![Example using code](./doc/images/code_usage.png) 48 | 49 | ## How to build it 50 | 51 | Here is an overview on how to compile the plugin from source: 52 | 53 | 1. Check the [official Godot documentation](https://docs.godotengine.org/es/stable/development/compiling/index.html) on how to compile the engine. You won't need to compile it, but it would help you install the necessary dependencies on your target platform. 54 | 2. Clone this repository recursively: 55 | 56 | ```bash 57 | git clone --recursive https://github.com/98teg/NativeDialogs 58 | cd NativeDialogs 59 | ``` 60 | 61 | 3. Build the C++ bindings: 62 | 63 | ```bash 64 | cd godot-cpp 65 | scons 66 | cd .. 67 | ``` 68 | 69 | 4. Compile the plugin: 70 | 71 | ```bash 72 | scons 73 | ``` 74 | 75 | 5. If you have encountered any problems, check the [official Godot documentation](https://docs.godotengine.org/en/stable/tutorials/scripting/gdextension/gdextension_cpp_example.html) on how to build a GDExtension plugin. 76 | 77 | ## Classes documentation 78 | 79 | For more information about this plugin's classes, check the following documentation: 80 | 81 | * [`NativeAcceptDialog`](./doc/native_accept_dialog.md) 82 | * [`NativeConfirmationDialog`](./doc/native_confirmation_dialog.md) 83 | * [`NativeFileDialog`](./doc/native_file_dialog.md) 84 | * [`NativeNotification`](./doc/native_notification.md) 85 | -------------------------------------------------------------------------------- /SConstruct: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | env = SConscript("godot-cpp/SConstruct") 6 | 7 | # For the reference: 8 | # - CCFLAGS are compilation flags shared between C and C++ 9 | # - CFLAGS are for C-specific compilation flags 10 | # - CXXFLAGS are for C++-specific compilation flags 11 | # - CPPFLAGS are for pre-processor flags 12 | # - CPPDEFINES are for pre-processor defines 13 | # - LINKFLAGS are for linking flags 14 | 15 | # portable-file-dialogs 16 | env.Append(CPPPATH=["portable-file-dialogs"]) 17 | 18 | # tweak this if you want to use different folders, or more folders, to store your source code in. 19 | env.Append(CPPPATH=["src/"]) 20 | 21 | # windows' libs 22 | if env["platform"] == "windows": 23 | env.Append(LIBS=["User32", "Shell32"]) 24 | 25 | sources = Glob("src/*.cpp") 26 | 27 | if env["platform"] == "macos": 28 | library = env.SharedLibrary( 29 | "demo/addons/native_dialogs/bin/libnativedialogs.{}.{}.framework/libnativedialogs.{}.{}".format( 30 | env["platform"], env["target"], env["platform"], env["target"] 31 | ), 32 | source=sources, 33 | ) 34 | else: 35 | library = env.SharedLibrary( 36 | "demo/addons/native_dialogs/bin/libnativedialogs{}{}".format(env["suffix"], env["SHLIBSUFFIX"]), 37 | source=sources, 38 | ) 39 | 40 | Default(library) 41 | -------------------------------------------------------------------------------- /demo/addons/native_dialogs/icons/native_accept_dialog.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/addons/native_dialogs/icons/native_accept_dialog.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://byxb1khbmskho" 6 | path="res://.godot/imported/native_accept_dialog.svg-29bc0991aa5bf1244bb9d9d5fee5b403.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/native_dialogs/icons/native_accept_dialog.svg" 14 | dest_files=["res://.godot/imported/native_accept_dialog.svg-29bc0991aa5bf1244bb9d9d5fee5b403.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | svg/scale=1.0 36 | editor/scale_with_editor_scale=false 37 | editor/convert_colors_with_editor_theme=false 38 | -------------------------------------------------------------------------------- /demo/addons/native_dialogs/icons/native_confirmation_dialog.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/addons/native_dialogs/icons/native_confirmation_dialog.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://bqc06qvvvhcrs" 6 | path="res://.godot/imported/native_confirmation_dialog.svg-dc5b681628a3393fd579446b7fc6e9c2.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/native_dialogs/icons/native_confirmation_dialog.svg" 14 | dest_files=["res://.godot/imported/native_confirmation_dialog.svg-dc5b681628a3393fd579446b7fc6e9c2.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | svg/scale=1.0 36 | editor/scale_with_editor_scale=false 37 | editor/convert_colors_with_editor_theme=false 38 | -------------------------------------------------------------------------------- /demo/addons/native_dialogs/icons/native_file_dialog.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/addons/native_dialogs/icons/native_file_dialog.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://cw1rhuvsspl32" 6 | path="res://.godot/imported/native_file_dialog.svg-3682a30350fe181d2f98696f239d4dc8.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/native_dialogs/icons/native_file_dialog.svg" 14 | dest_files=["res://.godot/imported/native_file_dialog.svg-3682a30350fe181d2f98696f239d4dc8.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | svg/scale=1.0 36 | editor/scale_with_editor_scale=false 37 | editor/convert_colors_with_editor_theme=false 38 | -------------------------------------------------------------------------------- /demo/addons/native_dialogs/icons/native_notification.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/addons/native_dialogs/icons/native_notification.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://bfiq688swf5bg" 6 | path="res://.godot/imported/native_notification.svg-aeed5747dd1cc9332f06d2a8339dd62a.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/native_dialogs/icons/native_notification.svg" 14 | dest_files=["res://.godot/imported/native_notification.svg-aeed5747dd1cc9332f06d2a8339dd62a.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | svg/scale=1.0 36 | editor/scale_with_editor_scale=false 37 | editor/convert_colors_with_editor_theme=false 38 | -------------------------------------------------------------------------------- /demo/addons/native_dialogs/native_dialogs.gdextension: -------------------------------------------------------------------------------- 1 | [configuration] 2 | 3 | entry_symbol = "native_dialogs_init" 4 | compatibility_minimum = 4.1 5 | 6 | [icons] 7 | 8 | NativeAcceptDialog = "res://addons/native_dialogs/icons/native_accept_dialog.svg" 9 | NativeConfirmationDialog = "res://addons/native_dialogs/icons/native_confirmation_dialog.svg" 10 | NativeFileDialog = "res://addons/native_dialogs/icons/native_file_dialog.svg" 11 | NativeNotification = "res://addons/native_dialogs/icons/native_notification.svg" 12 | 13 | [libraries] 14 | 15 | macos.debug = "bin/libnativedialogs.macos.template_debug.framework" 16 | macos.release = "bin/libnativedialogs.macos.template_release.framework" 17 | windows.debug.x86_64 = "bin/libnativedialogs.windows.template_debug.x86_64.dll" 18 | windows.release.x86_64 = "bin/libnativedialogs.windows.template_release.x86_64.dll" 19 | linux.debug.x86_64 = "bin/libnativedialogs.linux.template_debug.x86_64.so" 20 | linux.release.x86_64 = "bin/libnativedialogs.linux.template_release.x86_64.so" 21 | -------------------------------------------------------------------------------- /demo/addons/native_dialogs/plugin.cfg: -------------------------------------------------------------------------------- 1 | [plugin] 2 | 3 | name="Native Dialogs" 4 | description="Native Dialogs is a plugin for Godot that allows you to interact with OS-specific dialogs, such as notifications, messages and file dialogs. " 5 | author="Teggy" 6 | version="2.2.1" 7 | script="plugin.gd" 8 | -------------------------------------------------------------------------------- /demo/addons/native_dialogs/plugin.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends EditorPlugin 3 | 4 | func _enter_tree(): 5 | pass 6 | 7 | func _exit_tree(): 8 | pass 9 | -------------------------------------------------------------------------------- /demo/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/98teg/NativeDialogs/e829043a4d13214ec83b7b9912b4c62a3c0e21b6/demo/icon.png -------------------------------------------------------------------------------- /demo/icon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://drmcwcrrum2ru" 6 | path="res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://icon.png" 14 | dest_files=["res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/lossy_quality=0.7 20 | compress/hdr_compression=1 21 | compress/bptc_ldr=0 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /demo/icons/checked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/98teg/NativeDialogs/e829043a4d13214ec83b7b9912b4c62a3c0e21b6/demo/icons/checked.png -------------------------------------------------------------------------------- /demo/icons/checked.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://dgyx2waty8f13" 6 | path="res://.godot/imported/checked.png-a23913dbb696ec9fd4de8dd7991ed9f0.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://icons/checked.png" 14 | dest_files=["res://.godot/imported/checked.png-a23913dbb696ec9fd4de8dd7991ed9f0.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/lossy_quality=0.7 20 | compress/hdr_compression=1 21 | compress/bptc_ldr=0 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /demo/icons/unchecked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/98teg/NativeDialogs/e829043a4d13214ec83b7b9912b4c62a3c0e21b6/demo/icons/unchecked.png -------------------------------------------------------------------------------- /demo/icons/unchecked.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://cx3dk4v48aupp" 6 | path="res://.godot/imported/unchecked.png-e61615c556436a910b4fbed448ab715b.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://icons/unchecked.png" 14 | dest_files=["res://.godot/imported/unchecked.png-e61615c556436a910b4fbed448ab715b.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/lossy_quality=0.7 20 | compress/hdr_compression=1 21 | compress/bptc_ldr=0 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /demo/main.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=8 format=3 uid="uid://dsceba4s677re"] 2 | 3 | [ext_resource type="PackedScene" uid="uid://cyowfuspidj6l" path="res://scenes/native_accept_dialog.tscn" id="1_r21be"] 4 | [ext_resource type="Theme" uid="uid://dghnjlpplxvrd" path="res://main_theme.tres" id="1_xfap8"] 5 | [ext_resource type="PackedScene" uid="uid://bahfxvu3r7io" path="res://scenes/native_confirmation_dialog.tscn" id="2_35kej"] 6 | [ext_resource type="PackedScene" uid="uid://b3lywl2to6wuh" path="res://scenes/native_notification.tscn" id="3_hnvye"] 7 | [ext_resource type="PackedScene" uid="uid://bfhf2x2p1f5fv" path="res://scenes/native_file_dialog.tscn" id="3_mcjmb"] 8 | 9 | [sub_resource type="Gradient" id="Gradient_su64u"] 10 | colors = PackedColorArray(0.376471, 0.631373, 0.882353, 1, 0.67451, 0.980392, 0.988235, 1) 11 | 12 | [sub_resource type="GradientTexture2D" id="GradientTexture2D_7jcan"] 13 | gradient = SubResource("Gradient_su64u") 14 | fill_to = Vector2(0, 1) 15 | 16 | [node name="Main" type="TextureRect"] 17 | anchors_preset = 15 18 | anchor_right = 1.0 19 | anchor_bottom = 1.0 20 | grow_horizontal = 2 21 | grow_vertical = 2 22 | texture = SubResource("GradientTexture2D_7jcan") 23 | 24 | [node name="MarginContainer" type="MarginContainer" parent="."] 25 | layout_mode = 0 26 | anchor_right = 1.0 27 | anchor_bottom = 1.0 28 | grow_horizontal = 2 29 | grow_vertical = 2 30 | theme = ExtResource("1_xfap8") 31 | theme_override_constants/margin_top = 15 32 | theme_override_constants/margin_right = 15 33 | theme_override_constants/margin_bottom = 15 34 | 35 | [node name="ScrollContainer" type="ScrollContainer" parent="MarginContainer"] 36 | layout_mode = 2 37 | size_flags_horizontal = 3 38 | size_flags_vertical = 3 39 | horizontal_scroll_mode = 0 40 | 41 | [node name="MarginContainer" type="MarginContainer" parent="MarginContainer/ScrollContainer"] 42 | layout_mode = 2 43 | size_flags_horizontal = 3 44 | theme_override_constants/margin_left = 60 45 | theme_override_constants/margin_top = 15 46 | theme_override_constants/margin_right = 45 47 | theme_override_constants/margin_bottom = 15 48 | 49 | [node name="HFlowContainer" type="HFlowContainer" parent="MarginContainer/ScrollContainer/MarginContainer"] 50 | layout_mode = 2 51 | theme_override_constants/h_separation = 45 52 | theme_override_constants/v_separation = 30 53 | alignment = 1 54 | 55 | [node name="NativeAcceptDialog" parent="MarginContainer/ScrollContainer/MarginContainer/HFlowContainer" instance=ExtResource("1_r21be")] 56 | custom_minimum_size = Vector2(720, 0) 57 | layout_mode = 2 58 | 59 | [node name="NativeConfirmationDialog" parent="MarginContainer/ScrollContainer/MarginContainer/HFlowContainer" instance=ExtResource("2_35kej")] 60 | custom_minimum_size = Vector2(720, 0) 61 | layout_mode = 2 62 | 63 | [node name="NativeFileDialog" parent="MarginContainer/ScrollContainer/MarginContainer/HFlowContainer" instance=ExtResource("3_mcjmb")] 64 | custom_minimum_size = Vector2(720, 0) 65 | layout_mode = 2 66 | 67 | [node name="NativeNotification" parent="MarginContainer/ScrollContainer/MarginContainer/HFlowContainer" instance=ExtResource("3_hnvye")] 68 | custom_minimum_size = Vector2(720, 0) 69 | layout_mode = 2 70 | -------------------------------------------------------------------------------- /demo/project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=5 10 | 11 | [application] 12 | 13 | config/name="Native Dialogs" 14 | run/main_scene="res://main.tscn" 15 | config/features=PackedStringArray("4.1") 16 | config/icon="res://icon.png" 17 | 18 | [debug] 19 | 20 | gdscript/warnings/shadowed_variable=0 21 | 22 | [editor_plugins] 23 | 24 | enabled=PackedStringArray("res://addons/native_dialogs/plugin.cfg") 25 | 26 | [rendering] 27 | 28 | environment/defaults/default_clear_color=Color(0.85098, 0.85098, 0.85098, 1) 29 | environment/default_environment="res://default_env.tres" 30 | -------------------------------------------------------------------------------- /demo/scenes/native_accept_dialog.gd: -------------------------------------------------------------------------------- 1 | extends PanelContainer 2 | 3 | 4 | @onready var native_accept_dialog: NativeAcceptDialog = %_NativeAcceptDialog 5 | @onready var text: TextEdit = %_Text 6 | @onready var result: LineEdit = %_Result 7 | 8 | 9 | func text_changed(): 10 | native_accept_dialog.set_text(text.text) 11 | 12 | 13 | func set_confirmed(): 14 | result.text = "Confirmed" 15 | 16 | 17 | func set_canceled(): 18 | result.text = "Canceled" 19 | -------------------------------------------------------------------------------- /demo/scenes/native_accept_dialog.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://cyowfuspidj6l"] 2 | 3 | [ext_resource type="Theme" uid="uid://dghnjlpplxvrd" path="res://main_theme.tres" id="1_1bdvd"] 4 | [ext_resource type="Script" path="res://scenes/native_accept_dialog.gd" id="1_wtpim"] 5 | 6 | [node name="NativeAcceptDialog" type="PanelContainer"] 7 | anchors_preset = 15 8 | anchor_right = 1.0 9 | anchor_bottom = 1.0 10 | grow_horizontal = 2 11 | grow_vertical = 2 12 | size_flags_vertical = 3 13 | theme = ExtResource("1_1bdvd") 14 | script = ExtResource("1_wtpim") 15 | 16 | [node name="_NativeAcceptDialog" type="NativeAcceptDialog" parent="."] 17 | unique_name_in_owner = true 18 | 19 | [node name="MarginContainer" type="MarginContainer" parent="."] 20 | layout_mode = 2 21 | theme_override_constants/margin_left = 90 22 | theme_override_constants/margin_top = 60 23 | theme_override_constants/margin_right = 90 24 | theme_override_constants/margin_bottom = 60 25 | 26 | [node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"] 27 | layout_mode = 2 28 | size_flags_horizontal = 3 29 | 30 | [node name="Label" type="Label" parent="MarginContainer/VBoxContainer"] 31 | layout_mode = 2 32 | theme_type_variation = &"HeaderSmall" 33 | text = "NativeAcceptDialog" 34 | 35 | [node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer"] 36 | layout_mode = 2 37 | 38 | [node name="VBoxContainer1" type="VBoxContainer" parent="MarginContainer/VBoxContainer/HBoxContainer"] 39 | custom_minimum_size = Vector2(150, 0) 40 | layout_mode = 2 41 | size_flags_horizontal = 3 42 | theme_override_constants/separation = 0 43 | 44 | [node name="Lable" type="Label" parent="MarginContainer/VBoxContainer/HBoxContainer/VBoxContainer1"] 45 | layout_mode = 2 46 | text = "Title" 47 | 48 | [node name="LineEdit" type="LineEdit" parent="MarginContainer/VBoxContainer/HBoxContainer/VBoxContainer1"] 49 | layout_mode = 2 50 | size_flags_horizontal = 3 51 | text = "Alert!" 52 | caret_blink = true 53 | caret_blink_interval = 0.5 54 | 55 | [node name="VBoxContainer2" type="VBoxContainer" parent="MarginContainer/VBoxContainer/HBoxContainer"] 56 | custom_minimum_size = Vector2(150, 0) 57 | layout_mode = 2 58 | theme_override_constants/separation = 0 59 | 60 | [node name="Label" type="Label" parent="MarginContainer/VBoxContainer/HBoxContainer/VBoxContainer2"] 61 | layout_mode = 2 62 | text = "Icon" 63 | 64 | [node name="OptionButton" type="OptionButton" parent="MarginContainer/VBoxContainer/HBoxContainer/VBoxContainer2"] 65 | layout_mode = 2 66 | item_count = 4 67 | selected = 1 68 | popup/item_0/text = "Info" 69 | popup/item_0/id = 0 70 | popup/item_1/text = "Warning" 71 | popup/item_1/id = 1 72 | popup/item_2/text = "Error" 73 | popup/item_2/id = 2 74 | popup/item_3/text = "Question" 75 | popup/item_3/id = 3 76 | 77 | [node name="VBoxContainer1" type="VBoxContainer" parent="MarginContainer/VBoxContainer"] 78 | layout_mode = 2 79 | theme_override_constants/separation = 0 80 | 81 | [node name="Label" type="Label" parent="MarginContainer/VBoxContainer/VBoxContainer1"] 82 | layout_mode = 2 83 | text = "Text" 84 | 85 | [node name="_Text" type="TextEdit" parent="MarginContainer/VBoxContainer/VBoxContainer1"] 86 | unique_name_in_owner = true 87 | custom_minimum_size = Vector2(0, 90) 88 | layout_mode = 2 89 | 90 | [node name="VBoxContainer2" type="VBoxContainer" parent="MarginContainer/VBoxContainer"] 91 | layout_mode = 2 92 | theme_override_constants/separation = 0 93 | 94 | [node name="Label" type="Label" parent="MarginContainer/VBoxContainer/VBoxContainer2"] 95 | layout_mode = 2 96 | text = "Result" 97 | 98 | [node name="_Result" type="LineEdit" parent="MarginContainer/VBoxContainer/VBoxContainer2"] 99 | unique_name_in_owner = true 100 | layout_mode = 2 101 | size_flags_horizontal = 3 102 | editable = false 103 | 104 | [node name="MarginContainer" type="MarginContainer" parent="MarginContainer/VBoxContainer"] 105 | layout_mode = 2 106 | theme_override_constants/margin_top = 15 107 | 108 | [node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer/MarginContainer"] 109 | layout_mode = 2 110 | theme_override_constants/separation = 15 111 | alignment = 2 112 | 113 | [node name="Button1" type="Button" parent="MarginContainer/VBoxContainer/MarginContainer/HBoxContainer"] 114 | layout_mode = 2 115 | size_flags_vertical = 4 116 | theme_type_variation = &"GhostButton" 117 | text = "Hide" 118 | 119 | [node name="Button2" type="Button" parent="MarginContainer/VBoxContainer/MarginContainer/HBoxContainer"] 120 | layout_mode = 2 121 | size_flags_vertical = 4 122 | text = "Show" 123 | 124 | [connection signal="canceled" from="_NativeAcceptDialog" to="." method="set_canceled"] 125 | [connection signal="confirmed" from="_NativeAcceptDialog" to="." method="set_confirmed"] 126 | [connection signal="text_changed" from="MarginContainer/VBoxContainer/HBoxContainer/VBoxContainer1/LineEdit" to="_NativeAcceptDialog" method="set_title"] 127 | [connection signal="item_selected" from="MarginContainer/VBoxContainer/HBoxContainer/VBoxContainer2/OptionButton" to="_NativeAcceptDialog" method="set_icon"] 128 | [connection signal="text_changed" from="MarginContainer/VBoxContainer/VBoxContainer1/_Text" to="." method="text_changed"] 129 | [connection signal="pressed" from="MarginContainer/VBoxContainer/MarginContainer/HBoxContainer/Button1" to="_NativeAcceptDialog" method="hide"] 130 | [connection signal="pressed" from="MarginContainer/VBoxContainer/MarginContainer/HBoxContainer/Button2" to="_NativeAcceptDialog" method="show"] 131 | -------------------------------------------------------------------------------- /demo/scenes/native_confirmation_dialog.gd: -------------------------------------------------------------------------------- 1 | extends PanelContainer 2 | 3 | 4 | @onready var native_confirmation_dialog: NativeConfirmationDialog = %_NativeConfirmationDialog 5 | @onready var text: TextEdit = %_Text 6 | @onready var result: LineEdit = %_Result 7 | 8 | 9 | func text_changed(): 10 | native_confirmation_dialog.set_text(text.text) 11 | 12 | 13 | func set_confirmed(): 14 | result.text = "Confirmed" 15 | 16 | 17 | func set_canceled(): 18 | result.text = "Canceled" 19 | -------------------------------------------------------------------------------- /demo/scenes/native_confirmation_dialog.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://bahfxvu3r7io"] 2 | 3 | [ext_resource type="Script" path="res://scenes/native_confirmation_dialog.gd" id="1_1ix4v"] 4 | [ext_resource type="Theme" uid="uid://dghnjlpplxvrd" path="res://main_theme.tres" id="1_skr1f"] 5 | 6 | [node name="NativeConfirmationDialog" type="PanelContainer"] 7 | anchors_preset = 15 8 | anchor_right = 1.0 9 | anchor_bottom = 1.0 10 | grow_horizontal = 2 11 | grow_vertical = 2 12 | size_flags_vertical = 3 13 | theme = ExtResource("1_skr1f") 14 | script = ExtResource("1_1ix4v") 15 | 16 | [node name="_NativeConfirmationDialog" type="NativeConfirmationDialog" parent="."] 17 | unique_name_in_owner = true 18 | 19 | [node name="MarginContainer" type="MarginContainer" parent="."] 20 | offset_right = 1152.0 21 | offset_bottom = 648.0 22 | theme_override_constants/margin_left = 90 23 | theme_override_constants/margin_top = 60 24 | theme_override_constants/margin_right = 90 25 | theme_override_constants/margin_bottom = 60 26 | 27 | [node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"] 28 | offset_left = 90.0 29 | offset_top = 60.0 30 | offset_right = 1062.0 31 | offset_bottom = 588.0 32 | size_flags_horizontal = 3 33 | 34 | [node name="Label" type="Label" parent="MarginContainer/VBoxContainer"] 35 | offset_right = 972.0 36 | offset_bottom = 31.0 37 | theme_type_variation = &"HeaderSmall" 38 | text = "NativeConfirmationDialog" 39 | 40 | [node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer"] 41 | offset_top = 46.0 42 | offset_right = 972.0 43 | offset_bottom = 104.0 44 | 45 | [node name="VBoxContainer1" type="VBoxContainer" parent="MarginContainer/VBoxContainer/HBoxContainer"] 46 | custom_minimum_size = Vector2(150, 0) 47 | offset_right = 612.0 48 | offset_bottom = 58.0 49 | size_flags_horizontal = 3 50 | theme_override_constants/separation = 0 51 | 52 | [node name="Lable" type="Label" parent="MarginContainer/VBoxContainer/HBoxContainer/VBoxContainer1"] 53 | offset_right = 612.0 54 | offset_bottom = 26.0 55 | text = "Title" 56 | 57 | [node name="LineEdit" type="LineEdit" parent="MarginContainer/VBoxContainer/HBoxContainer/VBoxContainer1"] 58 | offset_top = 26.0 59 | offset_right = 612.0 60 | offset_bottom = 58.0 61 | size_flags_horizontal = 3 62 | text = "Please Confirm..." 63 | caret_blink = true 64 | caret_blink_interval = 0.5 65 | 66 | [node name="VBoxContainer2" type="VBoxContainer" parent="MarginContainer/VBoxContainer/HBoxContainer"] 67 | custom_minimum_size = Vector2(150, 0) 68 | offset_left = 642.0 69 | offset_right = 792.0 70 | offset_bottom = 58.0 71 | theme_override_constants/separation = 0 72 | 73 | [node name="Label" type="Label" parent="MarginContainer/VBoxContainer/HBoxContainer/VBoxContainer2"] 74 | offset_right = 150.0 75 | offset_bottom = 26.0 76 | text = "Icon" 77 | 78 | [node name="OptionButton" type="OptionButton" parent="MarginContainer/VBoxContainer/HBoxContainer/VBoxContainer2"] 79 | offset_top = 26.0 80 | offset_right = 150.0 81 | offset_bottom = 58.0 82 | item_count = 4 83 | selected = 3 84 | popup/item_0/text = "Info" 85 | popup/item_0/id = 0 86 | popup/item_1/text = "Warning" 87 | popup/item_1/id = 1 88 | popup/item_2/text = "Error" 89 | popup/item_2/id = 2 90 | popup/item_3/text = "Question" 91 | popup/item_3/id = 3 92 | 93 | [node name="VBoxContainer3" type="VBoxContainer" parent="MarginContainer/VBoxContainer/HBoxContainer"] 94 | custom_minimum_size = Vector2(150, 0) 95 | offset_left = 822.0 96 | offset_right = 972.0 97 | offset_bottom = 58.0 98 | theme_override_constants/separation = 0 99 | 100 | [node name="Label" type="Label" parent="MarginContainer/VBoxContainer/HBoxContainer/VBoxContainer3"] 101 | offset_right = 150.0 102 | offset_bottom = 26.0 103 | text = "Buttons Texts" 104 | 105 | [node name="OptionButton" type="OptionButton" parent="MarginContainer/VBoxContainer/HBoxContainer/VBoxContainer3"] 106 | offset_top = 26.0 107 | offset_right = 150.0 108 | offset_bottom = 58.0 109 | item_count = 2 110 | selected = 0 111 | popup/item_0/text = "OK Cancel" 112 | popup/item_0/id = 0 113 | popup/item_1/text = "Yes No" 114 | popup/item_1/id = 1 115 | 116 | [node name="VBoxContainer1" type="VBoxContainer" parent="MarginContainer/VBoxContainer"] 117 | offset_top = 119.0 118 | offset_right = 972.0 119 | offset_bottom = 235.0 120 | theme_override_constants/separation = 0 121 | 122 | [node name="Label" type="Label" parent="MarginContainer/VBoxContainer/VBoxContainer1"] 123 | offset_right = 972.0 124 | offset_bottom = 26.0 125 | text = "Text" 126 | 127 | [node name="_Text" type="TextEdit" parent="MarginContainer/VBoxContainer/VBoxContainer1"] 128 | unique_name_in_owner = true 129 | custom_minimum_size = Vector2(0, 90) 130 | offset_top = 26.0 131 | offset_right = 972.0 132 | offset_bottom = 116.0 133 | 134 | [node name="VBoxContainer2" type="VBoxContainer" parent="MarginContainer/VBoxContainer"] 135 | offset_top = 250.0 136 | offset_right = 972.0 137 | offset_bottom = 308.0 138 | theme_override_constants/separation = 0 139 | 140 | [node name="Label" type="Label" parent="MarginContainer/VBoxContainer/VBoxContainer2"] 141 | offset_right = 972.0 142 | offset_bottom = 26.0 143 | text = "Result" 144 | 145 | [node name="_Result" type="LineEdit" parent="MarginContainer/VBoxContainer/VBoxContainer2"] 146 | unique_name_in_owner = true 147 | offset_top = 26.0 148 | offset_right = 972.0 149 | offset_bottom = 58.0 150 | size_flags_horizontal = 3 151 | editable = false 152 | 153 | [node name="MarginContainer" type="MarginContainer" parent="MarginContainer/VBoxContainer"] 154 | offset_top = 323.0 155 | offset_right = 972.0 156 | offset_bottom = 376.0 157 | theme_override_constants/margin_top = 15 158 | 159 | [node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer/MarginContainer"] 160 | offset_top = 15.0 161 | offset_right = 972.0 162 | offset_bottom = 53.0 163 | grow_horizontal = 2 164 | grow_vertical = 2 165 | theme_override_constants/separation = 15 166 | alignment = 2 167 | 168 | [node name="Button1" type="Button" parent="MarginContainer/VBoxContainer/MarginContainer/HBoxContainer"] 169 | offset_left = 779.0 170 | offset_right = 864.0 171 | offset_bottom = 38.0 172 | size_flags_vertical = 4 173 | theme_type_variation = &"GhostButton" 174 | text = "Hide" 175 | 176 | [node name="Button2" type="Button" parent="MarginContainer/VBoxContainer/MarginContainer/HBoxContainer"] 177 | offset_left = 879.0 178 | offset_right = 972.0 179 | offset_bottom = 38.0 180 | size_flags_vertical = 4 181 | text = "Show" 182 | 183 | [connection signal="canceled" from="_NativeConfirmationDialog" to="." method="set_canceled"] 184 | [connection signal="confirmed" from="_NativeConfirmationDialog" to="." method="set_confirmed"] 185 | [connection signal="text_changed" from="MarginContainer/VBoxContainer/HBoxContainer/VBoxContainer1/LineEdit" to="_NativeConfirmationDialog" method="set_title"] 186 | [connection signal="item_selected" from="MarginContainer/VBoxContainer/HBoxContainer/VBoxContainer2/OptionButton" to="_NativeConfirmationDialog" method="set_icon"] 187 | [connection signal="item_selected" from="MarginContainer/VBoxContainer/HBoxContainer/VBoxContainer3/OptionButton" to="_NativeConfirmationDialog" method="set_buttons_texts"] 188 | [connection signal="text_changed" from="MarginContainer/VBoxContainer/VBoxContainer1/_Text" to="." method="text_changed"] 189 | [connection signal="pressed" from="MarginContainer/VBoxContainer/MarginContainer/HBoxContainer/Button1" to="_NativeConfirmationDialog" method="hide"] 190 | [connection signal="pressed" from="MarginContainer/VBoxContainer/MarginContainer/HBoxContainer/Button2" to="_NativeConfirmationDialog" method="show"] 191 | -------------------------------------------------------------------------------- /demo/scenes/native_file_dialog.gd: -------------------------------------------------------------------------------- 1 | extends PanelContainer 2 | 3 | 4 | @onready var native_file_dialog: NativeFileDialog = %_NativeFileDialog 5 | @onready var title: LineEdit = %_Title 6 | @onready var overrides: CheckButton = %_Overrides 7 | @onready var filter: LineEdit = %_Filter 8 | @onready var description: LineEdit = %_Description 9 | @onready var filters: VBoxContainer = %_Filters 10 | @onready var result: LineEdit = %_Result 11 | 12 | 13 | func select_file_mode(file_mode): 14 | native_file_dialog.file_mode = file_mode 15 | 16 | override_title() 17 | 18 | 19 | func override_title(): 20 | native_file_dialog.mode_overrides_title = overrides.button_pressed 21 | 22 | if native_file_dialog.mode_overrides_title: 23 | title.editable = false 24 | title.text = native_file_dialog.title 25 | else: 26 | title.editable = true 27 | 28 | 29 | func add_filter(): 30 | native_file_dialog.add_filter(filter.text, description.text) 31 | 32 | filter.text = "" 33 | description.text = "" 34 | 35 | update_filters() 36 | 37 | 38 | func clear_filters(): 39 | native_file_dialog.clear_filters() 40 | 41 | update_filters() 42 | 43 | 44 | func update_filters(): 45 | for child in filters.get_children(): 46 | child.queue_free() 47 | 48 | for filter in native_file_dialog.filters: 49 | var label = Label.new() 50 | label.text = filter 51 | 52 | filters.add_child(label) 53 | 54 | 55 | func file_selected(paths: String): 56 | files_selected([paths]) 57 | 58 | 59 | func files_selected(paths: Array[String]): 60 | result.text = paths.reduce( 61 | func(paths, path): return path if paths == "" else paths + ", " + path, "" 62 | ) 63 | 64 | 65 | func dialog_closed(): 66 | result.text = "" 67 | -------------------------------------------------------------------------------- /demo/scenes/native_file_dialog.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=5 format=3 uid="uid://bfhf2x2p1f5fv"] 2 | 3 | [ext_resource type="Script" path="res://scenes/native_file_dialog.gd" id="1_4eq6a"] 4 | [ext_resource type="Theme" uid="uid://dghnjlpplxvrd" path="res://main_theme.tres" id="1_kvkvm"] 5 | 6 | [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_h5ge7"] 7 | content_margin_left = 10.0 8 | content_margin_right = 10.0 9 | content_margin_bottom = 6.0 10 | bg_color = Color(1, 1, 1, 1) 11 | border_width_left = 3 12 | border_width_top = 3 13 | border_width_right = 3 14 | border_width_bottom = 3 15 | border_color = Color(0.909804, 0.909804, 0.909804, 1) 16 | border_blend = true 17 | corner_radius_top_left = 5 18 | corner_radius_top_right = 5 19 | corner_radius_bottom_right = 5 20 | corner_radius_bottom_left = 5 21 | 22 | [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_cvo7c"] 23 | content_margin_left = 10.0 24 | content_margin_right = 10.0 25 | content_margin_bottom = 6.0 26 | bg_color = Color(1, 1, 1, 1) 27 | border_width_left = 3 28 | border_width_top = 3 29 | border_width_right = 3 30 | border_width_bottom = 3 31 | border_color = Color(0.909804, 0.909804, 0.909804, 1) 32 | border_blend = true 33 | corner_radius_top_left = 5 34 | corner_radius_top_right = 5 35 | corner_radius_bottom_right = 5 36 | corner_radius_bottom_left = 5 37 | 38 | [node name="NativeFileDialog" type="PanelContainer"] 39 | anchors_preset = 15 40 | anchor_right = 1.0 41 | anchor_bottom = 1.0 42 | grow_horizontal = 2 43 | grow_vertical = 2 44 | size_flags_vertical = 3 45 | theme = ExtResource("1_kvkvm") 46 | script = ExtResource("1_4eq6a") 47 | 48 | [node name="_NativeFileDialog" type="NativeFileDialog" parent="."] 49 | unique_name_in_owner = true 50 | 51 | [node name="MarginContainer" type="MarginContainer" parent="."] 52 | layout_mode = 2 53 | theme_override_constants/margin_left = 90 54 | theme_override_constants/margin_top = 60 55 | theme_override_constants/margin_right = 90 56 | theme_override_constants/margin_bottom = 60 57 | 58 | [node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"] 59 | layout_mode = 2 60 | size_flags_horizontal = 3 61 | 62 | [node name="Label" type="Label" parent="MarginContainer/VBoxContainer"] 63 | layout_mode = 2 64 | theme_type_variation = &"HeaderSmall" 65 | text = "NativeFileDialog" 66 | 67 | [node name="HBoxContainer1" type="HBoxContainer" parent="MarginContainer/VBoxContainer"] 68 | layout_mode = 2 69 | 70 | [node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/VBoxContainer/HBoxContainer1"] 71 | layout_mode = 2 72 | size_flags_horizontal = 3 73 | theme_override_constants/separation = 0 74 | 75 | [node name="Lable" type="Label" parent="MarginContainer/VBoxContainer/HBoxContainer1/VBoxContainer"] 76 | layout_mode = 2 77 | text = "Title" 78 | 79 | [node name="_Title" type="LineEdit" parent="MarginContainer/VBoxContainer/HBoxContainer1/VBoxContainer"] 80 | unique_name_in_owner = true 81 | layout_mode = 2 82 | size_flags_horizontal = 3 83 | text = "Save a File" 84 | editable = false 85 | caret_blink = true 86 | caret_blink_interval = 0.5 87 | 88 | [node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer/HBoxContainer1"] 89 | layout_mode = 2 90 | size_flags_vertical = 4 91 | theme_override_constants/separation = 0 92 | 93 | [node name="Label" type="Label" parent="MarginContainer/VBoxContainer/HBoxContainer1/HBoxContainer"] 94 | layout_mode = 2 95 | text = "Mode Overrides Title" 96 | 97 | [node name="_Overrides" type="CheckButton" parent="MarginContainer/VBoxContainer/HBoxContainer1/HBoxContainer"] 98 | unique_name_in_owner = true 99 | layout_mode = 2 100 | button_pressed = true 101 | 102 | [node name="HBoxContainer2" type="HBoxContainer" parent="MarginContainer/VBoxContainer"] 103 | layout_mode = 2 104 | 105 | [node name="VBoxContainer1" type="VBoxContainer" parent="MarginContainer/VBoxContainer/HBoxContainer2"] 106 | layout_mode = 2 107 | theme_override_constants/separation = 0 108 | 109 | [node name="Label" type="Label" parent="MarginContainer/VBoxContainer/HBoxContainer2/VBoxContainer1"] 110 | layout_mode = 2 111 | text = "File Mode" 112 | 113 | [node name="OptionButton" type="OptionButton" parent="MarginContainer/VBoxContainer/HBoxContainer2/VBoxContainer1"] 114 | layout_mode = 2 115 | item_count = 4 116 | selected = 3 117 | popup/item_0/text = "Open File" 118 | popup/item_0/id = 0 119 | popup/item_1/text = "Open Files" 120 | popup/item_1/id = 1 121 | popup/item_2/text = "Open Folder" 122 | popup/item_2/id = 2 123 | popup/item_3/text = "Save" 124 | popup/item_3/id = 3 125 | 126 | [node name="VBoxContainer2" type="VBoxContainer" parent="MarginContainer/VBoxContainer/HBoxContainer2"] 127 | layout_mode = 2 128 | theme_override_constants/separation = 0 129 | 130 | [node name="Label" type="Label" parent="MarginContainer/VBoxContainer/HBoxContainer2/VBoxContainer2"] 131 | layout_mode = 2 132 | text = "Access" 133 | 134 | [node name="OptionButton" type="OptionButton" parent="MarginContainer/VBoxContainer/HBoxContainer2/VBoxContainer2"] 135 | layout_mode = 2 136 | item_count = 3 137 | selected = 0 138 | popup/item_0/text = "Resources" 139 | popup/item_0/id = 0 140 | popup/item_1/text = "User Data" 141 | popup/item_1/id = 1 142 | popup/item_2/text = "File System" 143 | popup/item_2/id = 2 144 | 145 | [node name="VBoxContainer3" type="VBoxContainer" parent="MarginContainer/VBoxContainer/HBoxContainer2"] 146 | layout_mode = 2 147 | size_flags_horizontal = 3 148 | theme_override_constants/separation = 0 149 | 150 | [node name="Lable" type="Label" parent="MarginContainer/VBoxContainer/HBoxContainer2/VBoxContainer3"] 151 | layout_mode = 2 152 | text = "Root Subfolder" 153 | 154 | [node name="LineEdit" type="LineEdit" parent="MarginContainer/VBoxContainer/HBoxContainer2/VBoxContainer3"] 155 | layout_mode = 2 156 | size_flags_horizontal = 3 157 | caret_blink = true 158 | caret_blink_interval = 0.5 159 | 160 | [node name="VBoxContainer1" type="VBoxContainer" parent="MarginContainer/VBoxContainer"] 161 | layout_mode = 2 162 | theme_override_constants/separation = 0 163 | 164 | [node name="Label" type="Label" parent="MarginContainer/VBoxContainer/VBoxContainer1"] 165 | layout_mode = 2 166 | text = "Filters" 167 | 168 | [node name="PanelContainer" type="PanelContainer" parent="MarginContainer/VBoxContainer/VBoxContainer1"] 169 | layout_mode = 2 170 | theme_override_styles/panel = SubResource("StyleBoxFlat_h5ge7") 171 | 172 | [node name="MarginContainer" type="MarginContainer" parent="MarginContainer/VBoxContainer/VBoxContainer1/PanelContainer"] 173 | layout_mode = 2 174 | theme_override_constants/margin_left = 15 175 | theme_override_constants/margin_top = 15 176 | theme_override_constants/margin_right = 15 177 | theme_override_constants/margin_bottom = 15 178 | 179 | [node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/VBoxContainer/VBoxContainer1/PanelContainer/MarginContainer"] 180 | layout_mode = 2 181 | 182 | [node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer/VBoxContainer1/PanelContainer/MarginContainer/VBoxContainer"] 183 | layout_mode = 2 184 | 185 | [node name="VBoxContainer1" type="VBoxContainer" parent="MarginContainer/VBoxContainer/VBoxContainer1/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer"] 186 | layout_mode = 2 187 | size_flags_horizontal = 3 188 | theme_override_constants/separation = 0 189 | 190 | [node name="Label" type="Label" parent="MarginContainer/VBoxContainer/VBoxContainer1/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer/VBoxContainer1"] 191 | layout_mode = 2 192 | text = "Filter" 193 | 194 | [node name="_Filter" type="LineEdit" parent="MarginContainer/VBoxContainer/VBoxContainer1/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer/VBoxContainer1"] 195 | unique_name_in_owner = true 196 | layout_mode = 2 197 | size_flags_horizontal = 3 198 | 199 | [node name="VBoxContainer2" type="VBoxContainer" parent="MarginContainer/VBoxContainer/VBoxContainer1/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer"] 200 | layout_mode = 2 201 | size_flags_horizontal = 3 202 | theme_override_constants/separation = 0 203 | 204 | [node name="Label" type="Label" parent="MarginContainer/VBoxContainer/VBoxContainer1/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer/VBoxContainer2"] 205 | layout_mode = 2 206 | text = "Description" 207 | 208 | [node name="_Description" type="LineEdit" parent="MarginContainer/VBoxContainer/VBoxContainer1/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer/VBoxContainer2"] 209 | unique_name_in_owner = true 210 | layout_mode = 2 211 | size_flags_horizontal = 3 212 | 213 | [node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer/VBoxContainer1/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer"] 214 | layout_mode = 2 215 | theme_override_constants/separation = 15 216 | alignment = 2 217 | 218 | [node name="Button1" type="Button" parent="MarginContainer/VBoxContainer/VBoxContainer1/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer/HBoxContainer"] 219 | layout_mode = 2 220 | size_flags_vertical = 4 221 | text = "Add" 222 | 223 | [node name="Button2" type="Button" parent="MarginContainer/VBoxContainer/VBoxContainer1/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer/HBoxContainer"] 224 | layout_mode = 2 225 | size_flags_vertical = 4 226 | theme_type_variation = &"GhostButton" 227 | text = "Clear" 228 | 229 | [node name="PanelContainer" type="PanelContainer" parent="MarginContainer/VBoxContainer/VBoxContainer1/PanelContainer/MarginContainer/VBoxContainer"] 230 | layout_mode = 2 231 | size_flags_horizontal = 3 232 | size_flags_vertical = 3 233 | theme_override_styles/panel = SubResource("StyleBoxFlat_cvo7c") 234 | 235 | [node name="ScrollContainer" type="ScrollContainer" parent="MarginContainer/VBoxContainer/VBoxContainer1/PanelContainer/MarginContainer/VBoxContainer/PanelContainer"] 236 | custom_minimum_size = Vector2(0, 90) 237 | layout_mode = 2 238 | horizontal_scroll_mode = 0 239 | 240 | [node name="_Filters" type="VBoxContainer" parent="MarginContainer/VBoxContainer/VBoxContainer1/PanelContainer/MarginContainer/VBoxContainer/PanelContainer/ScrollContainer"] 241 | unique_name_in_owner = true 242 | layout_mode = 2 243 | theme_override_constants/separation = 0 244 | 245 | [node name="VBoxContainer2" type="VBoxContainer" parent="MarginContainer/VBoxContainer"] 246 | layout_mode = 2 247 | theme_override_constants/separation = 0 248 | 249 | [node name="Label" type="Label" parent="MarginContainer/VBoxContainer/VBoxContainer2"] 250 | layout_mode = 2 251 | text = "Result" 252 | 253 | [node name="_Result" type="LineEdit" parent="MarginContainer/VBoxContainer/VBoxContainer2"] 254 | unique_name_in_owner = true 255 | layout_mode = 2 256 | size_flags_horizontal = 3 257 | editable = false 258 | 259 | [node name="MarginContainer" type="MarginContainer" parent="MarginContainer/VBoxContainer"] 260 | layout_mode = 2 261 | theme_override_constants/margin_top = 15 262 | 263 | [node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer/MarginContainer"] 264 | layout_mode = 2 265 | theme_override_constants/separation = 15 266 | alignment = 2 267 | 268 | [node name="Button1" type="Button" parent="MarginContainer/VBoxContainer/MarginContainer/HBoxContainer"] 269 | layout_mode = 2 270 | size_flags_vertical = 4 271 | theme_type_variation = &"GhostButton" 272 | text = "Hide" 273 | 274 | [node name="Button2" type="Button" parent="MarginContainer/VBoxContainer/MarginContainer/HBoxContainer"] 275 | layout_mode = 2 276 | size_flags_vertical = 4 277 | text = "Show" 278 | 279 | [connection signal="canceled" from="_NativeFileDialog" to="." method="dialog_closed"] 280 | [connection signal="dir_selected" from="_NativeFileDialog" to="." method="file_selected"] 281 | [connection signal="file_selected" from="_NativeFileDialog" to="." method="file_selected"] 282 | [connection signal="files_selected" from="_NativeFileDialog" to="." method="files_selected"] 283 | [connection signal="text_changed" from="MarginContainer/VBoxContainer/HBoxContainer1/VBoxContainer/_Title" to="_NativeFileDialog" method="set_title"] 284 | [connection signal="pressed" from="MarginContainer/VBoxContainer/HBoxContainer1/HBoxContainer/_Overrides" to="." method="override_title"] 285 | [connection signal="item_selected" from="MarginContainer/VBoxContainer/HBoxContainer2/VBoxContainer1/OptionButton" to="." method="select_file_mode"] 286 | [connection signal="item_selected" from="MarginContainer/VBoxContainer/HBoxContainer2/VBoxContainer2/OptionButton" to="_NativeFileDialog" method="set_access"] 287 | [connection signal="text_changed" from="MarginContainer/VBoxContainer/HBoxContainer2/VBoxContainer3/LineEdit" to="_NativeFileDialog" method="set_root_subfolder"] 288 | [connection signal="pressed" from="MarginContainer/VBoxContainer/VBoxContainer1/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer/HBoxContainer/Button1" to="." method="add_filter"] 289 | [connection signal="pressed" from="MarginContainer/VBoxContainer/VBoxContainer1/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer/HBoxContainer/Button2" to="." method="clear_filters"] 290 | [connection signal="pressed" from="MarginContainer/VBoxContainer/MarginContainer/HBoxContainer/Button1" to="_NativeFileDialog" method="hide"] 291 | [connection signal="pressed" from="MarginContainer/VBoxContainer/MarginContainer/HBoxContainer/Button2" to="_NativeFileDialog" method="show"] 292 | -------------------------------------------------------------------------------- /demo/scenes/native_notification.gd: -------------------------------------------------------------------------------- 1 | extends PanelContainer 2 | 3 | 4 | @onready var native_notification: NativeNotification = %_NativeNotification 5 | @onready var text: TextEdit = %_Text 6 | 7 | 8 | func text_changed(): 9 | native_notification.set_text(text.text) 10 | -------------------------------------------------------------------------------- /demo/scenes/native_notification.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://b3lywl2to6wuh"] 2 | 3 | [ext_resource type="Script" path="res://scenes/native_notification.gd" id="1_dl8uo"] 4 | [ext_resource type="Theme" uid="uid://dghnjlpplxvrd" path="res://main_theme.tres" id="1_kx2dd"] 5 | 6 | [node name="NativeNotification" type="PanelContainer"] 7 | anchors_preset = 15 8 | anchor_right = 1.0 9 | anchor_bottom = 1.0 10 | grow_horizontal = 2 11 | grow_vertical = 2 12 | size_flags_vertical = 3 13 | theme = ExtResource("1_kx2dd") 14 | script = ExtResource("1_dl8uo") 15 | 16 | [node name="_NativeNotification" type="NativeNotification" parent="."] 17 | unique_name_in_owner = true 18 | 19 | [node name="MarginContainer" type="MarginContainer" parent="."] 20 | layout_mode = 2 21 | theme_override_constants/margin_left = 90 22 | theme_override_constants/margin_top = 60 23 | theme_override_constants/margin_right = 90 24 | theme_override_constants/margin_bottom = 60 25 | 26 | [node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"] 27 | layout_mode = 2 28 | 29 | [node name="Label" type="Label" parent="MarginContainer/VBoxContainer"] 30 | layout_mode = 2 31 | theme_type_variation = &"HeaderSmall" 32 | text = "NativeNotification" 33 | 34 | [node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/VBoxContainer"] 35 | layout_mode = 2 36 | size_flags_horizontal = 3 37 | alignment = 1 38 | 39 | [node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer/VBoxContainer"] 40 | layout_mode = 2 41 | 42 | [node name="VBoxContainer1" type="VBoxContainer" parent="MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer"] 43 | layout_mode = 2 44 | size_flags_horizontal = 3 45 | size_flags_stretch_ratio = 3.0 46 | theme_override_constants/separation = 0 47 | 48 | [node name="Lable" type="Label" parent="MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer/VBoxContainer1"] 49 | layout_mode = 2 50 | text = "Title" 51 | 52 | [node name="LineEdit" type="LineEdit" parent="MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer/VBoxContainer1"] 53 | layout_mode = 2 54 | size_flags_horizontal = 3 55 | text = "Alert!" 56 | caret_blink = true 57 | caret_blink_interval = 0.5 58 | 59 | [node name="VBoxContainer2" type="VBoxContainer" parent="MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer"] 60 | layout_mode = 2 61 | size_flags_horizontal = 3 62 | theme_override_constants/separation = 0 63 | 64 | [node name="Label" type="Label" parent="MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer/VBoxContainer2"] 65 | layout_mode = 2 66 | text = "Icon" 67 | 68 | [node name="OptionButton" type="OptionButton" parent="MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer/VBoxContainer2"] 69 | layout_mode = 2 70 | item_count = 3 71 | selected = 1 72 | popup/item_0/text = "Info" 73 | popup/item_0/id = 0 74 | popup/item_1/text = "Warning" 75 | popup/item_1/id = 1 76 | popup/item_2/text = "Error" 77 | popup/item_2/id = 2 78 | 79 | [node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/VBoxContainer/VBoxContainer"] 80 | layout_mode = 2 81 | theme_override_constants/separation = 0 82 | 83 | [node name="Label" type="Label" parent="MarginContainer/VBoxContainer/VBoxContainer/VBoxContainer"] 84 | layout_mode = 2 85 | text = "Text" 86 | 87 | [node name="_Text" type="TextEdit" parent="MarginContainer/VBoxContainer/VBoxContainer/VBoxContainer"] 88 | unique_name_in_owner = true 89 | custom_minimum_size = Vector2(0, 90) 90 | layout_mode = 2 91 | 92 | [node name="MarginContainer" type="MarginContainer" parent="MarginContainer/VBoxContainer"] 93 | layout_mode = 2 94 | theme_override_constants/margin_top = 15 95 | 96 | [node name="Button" type="Button" parent="MarginContainer/VBoxContainer/MarginContainer"] 97 | layout_mode = 2 98 | size_flags_horizontal = 8 99 | size_flags_vertical = 4 100 | text = "Send" 101 | 102 | [connection signal="text_changed" from="MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer/VBoxContainer1/LineEdit" to="_NativeNotification" method="set_title"] 103 | [connection signal="item_selected" from="MarginContainer/VBoxContainer/VBoxContainer/HBoxContainer/VBoxContainer2/OptionButton" to="_NativeNotification" method="set_icon"] 104 | [connection signal="text_changed" from="MarginContainer/VBoxContainer/VBoxContainer/VBoxContainer/_Text" to="." method="text_changed"] 105 | [connection signal="pressed" from="MarginContainer/VBoxContainer/MarginContainer/Button" to="_NativeNotification" method="send"] 106 | -------------------------------------------------------------------------------- /doc/images/code_usage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/98teg/NativeDialogs/e829043a4d13214ec83b7b9912b4c62a3c0e21b6/doc/images/code_usage.png -------------------------------------------------------------------------------- /doc/images/editor_usage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/98teg/NativeDialogs/e829043a4d13214ec83b7b9912b4c62a3c0e21b6/doc/images/editor_usage.png -------------------------------------------------------------------------------- /doc/images/native_accept_dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/98teg/NativeDialogs/e829043a4d13214ec83b7b9912b4c62a3c0e21b6/doc/images/native_accept_dialog.png -------------------------------------------------------------------------------- /doc/images/native_confirmation_dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/98teg/NativeDialogs/e829043a4d13214ec83b7b9912b4c62a3c0e21b6/doc/images/native_confirmation_dialog.png -------------------------------------------------------------------------------- /doc/images/native_dialogs_banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/98teg/NativeDialogs/e829043a4d13214ec83b7b9912b4c62a3c0e21b6/doc/images/native_dialogs_banner.png -------------------------------------------------------------------------------- /doc/images/native_dialogs_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/98teg/NativeDialogs/e829043a4d13214ec83b7b9912b4c62a3c0e21b6/doc/images/native_dialogs_icon.png -------------------------------------------------------------------------------- /doc/images/native_file_dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/98teg/NativeDialogs/e829043a4d13214ec83b7b9912b4c62a3c0e21b6/doc/images/native_file_dialog.png -------------------------------------------------------------------------------- /doc/images/native_notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/98teg/NativeDialogs/e829043a4d13214ec83b7b9912b4c62a3c0e21b6/doc/images/native_notification.png -------------------------------------------------------------------------------- /doc/images/windows_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/98teg/NativeDialogs/e829043a4d13214ec83b7b9912b4c62a3c0e21b6/doc/images/windows_example.png -------------------------------------------------------------------------------- /doc/native_accept_dialog.md: -------------------------------------------------------------------------------- 1 | # `NativeAcceptDialog` 2 | 3 | ## Description 4 | 5 | This dialog is useful for small notifications to the user about an event. It can only be accepted or closed. The functionality of this node is already implemented in Godot via the `OS.alert()` method, althought it doesn't let you customize the icon. 6 | 7 | ![Native Accept Dialog](./images/native_accept_dialog.png) 8 | 9 | ## Properties 10 | 11 | |Type|Name|Default value| 12 | |-|-|-| 13 | |[`Icon`](#enum-icon)|[`dialog_icon`](#icon-dialog_icon--1)|`1`| 14 | |`String`|[`dialog_text`](#string-dialog_text--)|`""`| 15 | |`String`|[`title`](#string-title--alert)|`"Alert!"`| 16 | 17 | ## Methods 18 | 19 | |Returned type|Declaration| 20 | |-|-| 21 | |`void`|[`hide()`](#void-hide)| 22 | |`void`|[`show()`](#void-show)| 23 | 24 | ## Signals 25 | 26 | ### `canceled()` 27 | 28 | Emitted when the dialog is closed. 29 | 30 | ### `confirmed()` 31 | 32 | Emitted when the dialog is accepted, i.e. the OK button is pressed. 33 | 34 | ## Enumerations 35 | 36 | ### `enum Icon` 37 | 38 | ```gdscript 39 | Icon ICON_INFO = 0 40 | ``` 41 | 42 | Usually an icon with the letter i in a blue circle*. 43 | 44 | ```gdscript 45 | Icon ICON_WARNING = 1 46 | ``` 47 | 48 | Usually an icon with an exclamation mark in a yellow triangle*. 49 | 50 | ```gdscript 51 | Icon ICON_ERROR = 2 52 | ``` 53 | 54 | Usually an icon with a cross in a red circle*. 55 | 56 | ```gdscript 57 | Icon ICON_QUESTION = 3 58 | ``` 59 | 60 | Usually an icon with a question mark*. 61 | 62 | **(*)** Depends on your OS theme. 63 | 64 | ## Property Descriptions 65 | 66 | ### `Icon dialog_icon = 1` 67 | 68 | The icon displayed by the dialog. 69 | 70 | ```gdscript 71 | void set_icon(Icon value) 72 | 73 | Icon get_icon() 74 | ``` 75 | 76 | ### `String dialog_text = ""` 77 | 78 | The text displayed by the dialog. 79 | 80 | ```gdscript 81 | void set_text(String value) 82 | 83 | String get_text() 84 | ``` 85 | 86 | ### `String title = "Alert!"` 87 | 88 | The dialog's title. 89 | 90 | ```gdscript 91 | void set_title(String value) 92 | 93 | String get_title() 94 | ``` 95 | 96 | ## Method Descriptions 97 | 98 | ### `void hide()` 99 | 100 | Hides the dialog. 101 | 102 | ### `void show()` 103 | 104 | Makes the dialog appear. If this dialog is already visible, it would call [`hide`](#void-hide) first. 105 | -------------------------------------------------------------------------------- /doc/native_confirmation_dialog.md: -------------------------------------------------------------------------------- 1 | # `NativeConfirmationDialog` 2 | 3 | ## Description 4 | 5 | Dialog for confirmation of actions. This dialog inherits from [`NativeAcceptDialog`](./native_accept_dialog.md), 6 | 7 | ![Native Confirmation Dialog](./images/native_confirmation_dialog.png) 8 | 9 | ## Properties 10 | 11 | |Type|Name|Default value| 12 | |-|-|-| 13 | |[`ButtonsTexts`](#enum-buttonstexts)|[`buttons_texts`](#buttonstexts-buttons_texts--0)|`0`| 14 | |[`Icon`](./native_accept_dialog.md#enum-icon)|[`dialog_icon`](./native_accept_dialog.md#icon-dialog_icon--1)|`3` (overrides [`NativeAcceptDialog`](./native_accept_dialog.md))| 15 | |`String`|[`title`](./native_accept_dialog.md#string-title--alert)|`"Please Confirm..."` (overrides [`NativeAcceptDialog`](./native_accept_dialog.md))| 16 | 17 | ## Enumerations 18 | 19 | ### `enum ButtonsTexts` 20 | 21 | ```gdscript 22 | ButtonsTexts BUTTONS_TEXTS_OK_CANCEL = 0 23 | ``` 24 | 25 | Pressing `OK` would emit the [`confirmed`](./native_accept_dialog.md#confirmed) signal and pressing `Cancel` would emit the [`canceled`](./native_accept_dialog.md#canceled) signal. 26 | 27 | ```gdscript 28 | Icon BUTTONS_TEXTS_YES_NO = 1 29 | ``` 30 | 31 | Pressing `Yes` would emit the [`confirmed`](./native_accept_dialog.md#confirmed) signal and pressing `No` would emit the [`canceled`](./native_accept_dialog.md#canceled) signal. 32 | 33 | ## Property Descriptions 34 | 35 | ### `ButtonsTexts buttons_texts = 0` 36 | 37 | The text that would be displayed inside the dialog's buttons. 38 | 39 | ```gdscript 40 | void set_buttons_texts(ButtonsTexts value) 41 | 42 | ButtonsTexts get_buttons_texts() 43 | ``` 44 | -------------------------------------------------------------------------------- /doc/native_file_dialog.md: -------------------------------------------------------------------------------- 1 | # `NativeFileDialog` 2 | 3 | ## Description 4 | 5 | NativeFileDialog is a dialog used to choose files and directories in the filesystem. It supports filter masks. The NativeFileDialog automatically sets its window title according to the `mode`. If you want to use a custom title, disable this by setting `mode_overrides_title` to `false`. 6 | 7 | ![Native File Dialog](./images/native_file_dialog.png) 8 | 9 | ## Properties 10 | 11 | |Type|Name|Default value| 12 | |-|-|-| 13 | |[`Access`](#enum-access)|[`access`](#access-access--0)|`0`| 14 | |[`FileMode`](#enum-filemode)|[`file_mode`](#filemode-file_mode--3)|`3`| 15 | |`PoolStringArray`|[`filters`](#packedstringarray-filters--packedstringarray)|`PoolStringArray()`| 16 | |`bool`|[`mode_overrides_title`](#bool-mode_overrides_title--true)|`true`| 17 | |`String`|[`root_subfolder`](#string-root_subfolder--)|`""`| 18 | |`String`|[`title`](#string-title--save-a-file)|`"Save a File"`| 19 | 20 | ## Methods 21 | 22 | |Returned type|Declaration| 23 | |-|-| 24 | |`void`|[`add_filter(String filter, String description="")`](#void-add_filterstring-filter-string-description)| 25 | |`void`|[`clear_filters()`](#void-clear_filters)| 26 | |`void`|[`hide()`](#void-hide)| 27 | |`void`|[`show()`](#void-show)| 28 | 29 | ## Signals 30 | 31 | ### `dir_selected(String dir)` 32 | 33 | Emitted when the user selects a directory. 34 | 35 | ### `file_selected(String path)` 36 | 37 | Emitted when the user selects a file by double-clicking it or pressing the **OK** button. 38 | 39 | ### `files_selected(PoolStringArray paths)` 40 | 41 | Emitted when the user selects multiple files. 42 | 43 | ### `canceled()` 44 | 45 | Emitted when the dialog is closed. 46 | 47 | ## Enumerations 48 | 49 | ### `enum FileMode` 50 | 51 | ```gdscript 52 | FileMode MODE_OPEN_FILE = 0 53 | ``` 54 | 55 | The dialog allows selecting one, and only one file. 56 | 57 | ```gdscript 58 | FileMode MODE_OPEN_FILES = 1 59 | ``` 60 | 61 | The dialog allows selecting multiple files. 62 | 63 | ```gdscript 64 | FileMode MODE_OPEN_DIR = 2 65 | ``` 66 | 67 | The dialog only allows selecting a directory, disallowing the selection of any file. 68 | 69 | ```gdscript 70 | FileMode MODE_SAVE_FILE = 3 71 | ``` 72 | 73 | The dialog will warn when a file exists. 74 | 75 | ### `enum Access` 76 | 77 | ```gdscript 78 | Access ACCESS_RESOURCES = 0 79 | ``` 80 | 81 | The dialog is initiated under the Resource path (`res://`). 82 | 83 | ```gdscript 84 | Access ACCESS_USERDATA = 1 85 | ``` 86 | 87 | The dialog is initiated under the user data path (`user://`). 88 | 89 | ```gdscript 90 | Access ACCESS_FILESYSTEM = 2 91 | ``` 92 | 93 | The dialog allows accessing files on the whole file system. 94 | 95 | ## Property Descriptions 96 | 97 | ### `Access access = 0` 98 | 99 | The file system access scope. See enum `Access` constants. Changing this value would reset `root_subfolder`. So when using code, you need to set `access` before `root_subfolder`. 100 | 101 | ```gdscript 102 | void set_access(Access value) 103 | 104 | Access get_access() 105 | ``` 106 | 107 | ### `FileMode file_mode = 3` 108 | 109 | The dialog's open or save mode, which affects the selection behavior. See `FileMode`. 110 | 111 | ```gdscript 112 | void set_file_mode(FileMode value) 113 | 114 | FileMode get_file_mode() 115 | ``` 116 | 117 | ### `PackedStringArray filters = PackedStringArray()` 118 | 119 | The available file type filters. For example, this shows only `.png` and `.gd` files: `set_filters(PackedStringArray(["*.png ; PNG Images","*.gd ; GDScript Files"]))`. Multiple file types can also be specified in a single filter. `"*.png, *.jpg, *.jpeg ; Supported Images"` will show both PNG and JPEG files when selected. 120 | 121 | ```gdscript 122 | void set_filters(PackedStringArray value) 123 | 124 | PackedStringArray get_filters() 125 | ``` 126 | 127 | ### `bool mode_overrides_title = true` 128 | 129 | If `true`, changing the `Mode` property will set the window title accordingly (e.g. setting mode to `FILE_MODE_OPEN_FILE` will change the window title to "Open a File"). 130 | 131 | ```gdscript 132 | void set_mode_overrides_title(bool value) 133 | 134 | bool is_mode_overriding_title() 135 | ``` 136 | 137 | ### `String root_subfolder = ""` 138 | 139 | The subfolder where the native dialog would start. 140 | 141 | ```gdscript 142 | void set_root_subfolder(String value) 143 | 144 | String get_root_subfolder() 145 | ``` 146 | 147 | ### `String title = "Save a File"` 148 | 149 | The dialog's title. 150 | 151 | ```gdscript 152 | void set_title(String value) 153 | 154 | String get_title() 155 | ``` 156 | 157 | ## Method Descriptions 158 | 159 | ### `void add_filter(String filter, String description="")` 160 | 161 | Adds a comma-delimited file name `filter` option to the **FileDialog** with an optional `description`, which restricts what files can be picked. 162 | 163 | A `filter` should be of the form `"filename.extension"`, where filename and extension can be * to match any string. Filters starting with `.` (i.e. empty filenames) are not allowed. 164 | 165 | For example, a `filter` of `"*.png, *.jpg"` and a description of `"Images"` results in filter text `"Images (*.png, *.jpg)"`. 166 | 167 | ### `void clear_filters()` 168 | 169 | Clear all the added filters in the dialog. 170 | 171 | ### `void hide()` 172 | 173 | Hides the dialog. 174 | 175 | ### `void show()` 176 | 177 | Makes the dialog appear. If this dialog is already visible, it would call [`hide`](#void-hide) first. 178 | -------------------------------------------------------------------------------- /doc/native_notification.md: -------------------------------------------------------------------------------- 1 | # `NativeNotification` 2 | 3 | ## Description 4 | 5 | This node would show the user a brief message and then fade away. 6 | 7 | ![Native Notification](./images/native_notification.png) 8 | 9 | ## Properties 10 | 11 | |Type|Name|Default value| 12 | |-|-|-| 13 | |[`Icon`](#enum-icon)|[`notification_icon`](#icon-notification_icon--1)|`1`| 14 | |`String`|[`notification_text`](#string-notification_text--)|`""`| 15 | |`String`|[`title`](#string-title--alert)|`"Alert!"`| 16 | 17 | ## Methods 18 | 19 | |Returned type|Declaration| 20 | |-|-| 21 | |`void`|[`send()`](#void-send)| 22 | 23 | ## Enumerations 24 | 25 | ### `enum Icon` 26 | 27 | ```gdscript 28 | Icon ICON_INFO = 0 29 | ``` 30 | 31 | Usually an icon with the letter i in a blue circle*. 32 | 33 | ```gdscript 34 | Icon ICON_WARNING = 1 35 | ``` 36 | 37 | Usually an icon with an exclamation mark in a yellow triangle*. 38 | 39 | ```gdscript 40 | Icon ICON_ERROR = 2 41 | ``` 42 | 43 | Usually an icon with a cross in a red circle*. 44 | 45 | **(*)** Depends on your OS theme. 46 | 47 | ## Property Descriptions 48 | 49 | ### `Icon notification_icon = 1` 50 | 51 | The icon displayed by the notification. 52 | 53 | ```gdscript 54 | void set_icon(Icon value) 55 | 56 | Icon get_icon() 57 | ``` 58 | 59 | ### `String notification_text = ""` 60 | 61 | The text displayed by the notification. 62 | 63 | ```gdscript 64 | void set_text(String value) 65 | 66 | String get_text() 67 | ``` 68 | 69 | ### `String title = "Alert!"` 70 | 71 | The notification's title. 72 | 73 | ```gdscript 74 | void set_title(String value) 75 | 76 | String get_title() 77 | ``` 78 | 79 | ## Method Descriptions 80 | 81 | ### `void send()` 82 | 83 | Sends the notification to appear to the user and then fade away. 84 | -------------------------------------------------------------------------------- /src/native_accept_dialog.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "native_accept_dialog.h" 4 | 5 | using namespace godot; 6 | 7 | void NativeAcceptDialog::_bind_methods() { 8 | ClassDB::bind_method(D_METHOD("set_title", "title"), &NativeAcceptDialog::set_title); 9 | ClassDB::bind_method(D_METHOD("get_title"), &NativeAcceptDialog::get_title); 10 | ADD_PROPERTY(PropertyInfo(Variant::STRING, "title"), "set_title", "get_title"); 11 | 12 | ADD_GROUP("Dialog", "dialog"); 13 | ClassDB::bind_method(D_METHOD("set_text", "text"), &NativeAcceptDialog::set_text); 14 | ClassDB::bind_method(D_METHOD("get_text"), &NativeAcceptDialog::get_text); 15 | ADD_PROPERTY(PropertyInfo(Variant::STRING, "dialog_text", PROPERTY_HINT_MULTILINE_TEXT, ""), "set_text", "get_text"); 16 | 17 | ClassDB::bind_method(D_METHOD("set_icon", "icon"), &NativeAcceptDialog::set_icon); 18 | ClassDB::bind_method(D_METHOD("get_icon"), &NativeAcceptDialog::get_icon); 19 | ADD_PROPERTY(PropertyInfo(Variant::INT, "dialog_icon", PROPERTY_HINT_ENUM, "Info,Warning,Error,Question"), "set_icon", "get_icon"); 20 | 21 | ClassDB::bind_method(D_METHOD("show"), &NativeAcceptDialog::show); 22 | ClassDB::bind_method(D_METHOD("hide"), &NativeAcceptDialog::hide); 23 | 24 | ADD_SIGNAL(MethodInfo("confirmed")); 25 | ADD_SIGNAL(MethodInfo("canceled")); 26 | 27 | BIND_ENUM_CONSTANT(ICON_INFO); 28 | BIND_ENUM_CONSTANT(ICON_WARNING); 29 | BIND_ENUM_CONSTANT(ICON_ERROR); 30 | BIND_ENUM_CONSTANT(ICON_QUESTION); 31 | } 32 | 33 | NativeAcceptDialog::NativeAcceptDialog() { 34 | title = "Alert!"; 35 | text = ""; 36 | icon = ICON_WARNING; 37 | } 38 | 39 | NativeAcceptDialog::~NativeAcceptDialog() { 40 | hide(); 41 | } 42 | 43 | void NativeAcceptDialog::_process(float delta) { 44 | if (!message || !message->ready(0)) { 45 | return; 46 | } 47 | 48 | pfd::button button = message->result(); 49 | if (button == pfd::button::ok || button == pfd::button::yes) { 50 | emit_signal("confirmed"); 51 | } else { 52 | emit_signal("canceled"); 53 | } 54 | 55 | hide(); 56 | } 57 | 58 | void NativeAcceptDialog::_exit_tree() { 59 | hide(); 60 | } 61 | 62 | void NativeAcceptDialog::show() { 63 | ERR_FAIL_COND(!is_inside_tree()); 64 | ERR_FAIL_COND(!is_processing()); 65 | 66 | hide(); 67 | 68 | pfd::icon pfd_icon; 69 | 70 | switch (icon) { 71 | case ICON_INFO: 72 | pfd_icon = pfd::icon::info; 73 | break; 74 | case ICON_ERROR: 75 | pfd_icon = pfd::icon::error; 76 | break; 77 | case ICON_QUESTION: 78 | pfd_icon = pfd::icon::question; 79 | break; 80 | default: 81 | pfd_icon = pfd::icon::warning; 82 | } 83 | 84 | message = new pfd::message( 85 | String(TranslationServer::get_singleton()->translate(title)).utf8().get_data(), 86 | String(TranslationServer::get_singleton()->translate(text)).utf8().get_data(), 87 | choice, 88 | pfd_icon); 89 | } 90 | 91 | void NativeAcceptDialog::hide() { 92 | if (!message) { 93 | return; 94 | } 95 | 96 | message->kill(); 97 | 98 | delete message; 99 | 100 | message = nullptr; 101 | } 102 | 103 | void NativeAcceptDialog::set_title(const String &p_title) { 104 | title = p_title; 105 | } 106 | 107 | String NativeAcceptDialog::get_title() { 108 | return title; 109 | } 110 | 111 | void NativeAcceptDialog::set_text(const String &p_text) { 112 | text = p_text; 113 | } 114 | 115 | String NativeAcceptDialog::get_text() { 116 | return text; 117 | } 118 | 119 | void NativeAcceptDialog::set_icon(Icon p_icon) { 120 | ERR_FAIL_INDEX((int)p_icon, 4); 121 | 122 | icon = p_icon; 123 | } 124 | 125 | NativeAcceptDialog::Icon NativeAcceptDialog::get_icon() { 126 | return icon; 127 | } 128 | 129 | void NativeAcceptDialog::set_choice(pfd::choice p_choice) { 130 | choice = p_choice; 131 | } 132 | 133 | pfd::choice NativeAcceptDialog::get_choice() { 134 | return choice; 135 | } 136 | -------------------------------------------------------------------------------- /src/native_accept_dialog.h: -------------------------------------------------------------------------------- 1 | #ifndef NATIVEACCEPTDIALOG_H 2 | #define NATIVEACCEPTDIALOG_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | namespace godot { 11 | 12 | class NativeAcceptDialog : public Node { 13 | GDCLASS(NativeAcceptDialog, Node) 14 | 15 | protected: 16 | static void _bind_methods(); 17 | 18 | void set_choice(pfd::choice p_choice); 19 | pfd::choice get_choice(); 20 | 21 | public: 22 | enum Icon { ICON_INFO, 23 | ICON_WARNING, 24 | ICON_ERROR, 25 | ICON_QUESTION }; 26 | 27 | private: 28 | String title; 29 | String text; 30 | Icon icon; 31 | 32 | pfd::choice choice = pfd::choice::ok; 33 | pfd::message *message = nullptr; 34 | 35 | public: 36 | NativeAcceptDialog(); 37 | ~NativeAcceptDialog(); 38 | 39 | void _process(float delta); 40 | void _exit_tree(); 41 | 42 | void show(); 43 | void hide(); 44 | 45 | void set_title(const String &p_title); 46 | String get_title(); 47 | 48 | void set_text(const String &p_text); 49 | String get_text(); 50 | 51 | void set_icon(Icon p_icon); 52 | Icon get_icon(); 53 | }; 54 | 55 | } // namespace godot 56 | 57 | VARIANT_ENUM_CAST(NativeAcceptDialog::Icon); 58 | 59 | #endif // NATIVEACCEPTDIALOG_H 60 | -------------------------------------------------------------------------------- /src/native_confirmation_dialog.cpp: -------------------------------------------------------------------------------- 1 | #include "native_confirmation_dialog.h" 2 | 3 | using namespace godot; 4 | 5 | void NativeConfirmationDialog::_bind_methods() { 6 | ClassDB::bind_method(D_METHOD("set_buttons_texts", "buttons_texts"), &NativeConfirmationDialog::set_buttons_texts); 7 | ClassDB::bind_method(D_METHOD("get_buttons_texts"), &NativeConfirmationDialog::get_buttons_texts); 8 | ADD_PROPERTY(PropertyInfo(Variant::INT, "buttons_texts", PROPERTY_HINT_ENUM, "OK Cancel,Yes No"), "set_buttons_texts", "get_buttons_texts"); 9 | 10 | BIND_ENUM_CONSTANT(BUTTONS_TEXTS_OK_CANCEL); 11 | BIND_ENUM_CONSTANT(BUTTONS_TEXTS_YES_NO); 12 | } 13 | 14 | NativeConfirmationDialog::NativeConfirmationDialog() { 15 | set_title("Please Confirm..."); 16 | set_icon(ICON_QUESTION); 17 | set_buttons_texts(BUTTONS_TEXTS_OK_CANCEL); 18 | } 19 | 20 | void NativeConfirmationDialog::_process(float delta) { 21 | NativeAcceptDialog::_process(delta); 22 | } 23 | 24 | void NativeConfirmationDialog::_exit_tree() { 25 | NativeAcceptDialog::_exit_tree(); 26 | } 27 | 28 | void NativeConfirmationDialog::set_buttons_texts(ButtonsTexts p_buttons_texts) { 29 | buttons_texts = p_buttons_texts; 30 | 31 | switch (buttons_texts) { 32 | case BUTTONS_TEXTS_OK_CANCEL: 33 | set_choice(pfd::choice::ok_cancel); 34 | break; 35 | case BUTTONS_TEXTS_YES_NO: 36 | set_choice(pfd::choice::yes_no); 37 | break; 38 | } 39 | } 40 | 41 | NativeConfirmationDialog::ButtonsTexts NativeConfirmationDialog::get_buttons_texts() { 42 | return buttons_texts; 43 | } 44 | -------------------------------------------------------------------------------- /src/native_confirmation_dialog.h: -------------------------------------------------------------------------------- 1 | #ifndef NATIVECONFIRMATIONDIALOG_H 2 | #define NATIVECONFIRMATIONDIALOG_H 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "native_accept_dialog.h" 10 | 11 | namespace godot { 12 | 13 | class NativeConfirmationDialog : public NativeAcceptDialog { 14 | GDCLASS(NativeConfirmationDialog, NativeAcceptDialog) 15 | 16 | protected: 17 | static void _bind_methods(); 18 | 19 | public: 20 | enum ButtonsTexts { 21 | BUTTONS_TEXTS_OK_CANCEL, 22 | BUTTONS_TEXTS_YES_NO 23 | }; 24 | 25 | private: 26 | ButtonsTexts buttons_texts; 27 | 28 | public: 29 | NativeConfirmationDialog(); 30 | 31 | void _process(float delta); 32 | void _exit_tree(); 33 | 34 | void set_buttons_texts(ButtonsTexts p_texts); 35 | ButtonsTexts get_buttons_texts(); 36 | }; 37 | 38 | } // namespace godot 39 | 40 | VARIANT_ENUM_CAST(NativeConfirmationDialog::ButtonsTexts); 41 | 42 | #endif // NATIVECONFIRMATIONDIALOG_H 43 | -------------------------------------------------------------------------------- /src/native_file_dialog.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "native_file_dialog.h" 7 | 8 | using namespace godot; 9 | 10 | void NativeFileDialog::_bind_methods() { 11 | ClassDB::bind_method(D_METHOD("set_title", "title"), &NativeFileDialog::set_title); 12 | ClassDB::bind_method(D_METHOD("get_title"), &NativeFileDialog::get_title); 13 | ADD_PROPERTY(PropertyInfo(Variant::STRING, "title"), "set_title", "get_title"); 14 | 15 | ClassDB::bind_method(D_METHOD("set_mode_overrides_title", "override"), &NativeFileDialog::set_mode_overrides_title); 16 | ClassDB::bind_method(D_METHOD("is_mode_overriding_title"), &NativeFileDialog::is_mode_overriding_title); 17 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "mode_overrides_title"), "set_mode_overrides_title", "is_mode_overriding_title"); 18 | 19 | ClassDB::bind_method(D_METHOD("set_file_mode", "mode"), &NativeFileDialog::set_file_mode); 20 | ClassDB::bind_method(D_METHOD("get_file_mode"), &NativeFileDialog::get_file_mode); 21 | ADD_PROPERTY(PropertyInfo(Variant::INT, "file_mode", PROPERTY_HINT_ENUM, "Open File,Open Files,Open Folder,Save"), "set_file_mode", "get_file_mode"); 22 | 23 | ClassDB::bind_method(D_METHOD("set_access", "access"), &NativeFileDialog::set_access); 24 | ClassDB::bind_method(D_METHOD("get_access"), &NativeFileDialog::get_access); 25 | ADD_PROPERTY(PropertyInfo(Variant::INT, "access", PROPERTY_HINT_ENUM, "Resources,User Data,File System"), "set_access", "get_access"); 26 | 27 | ClassDB::bind_method(D_METHOD("set_root_subfolder", "dir"), &NativeFileDialog::set_root_subfolder); 28 | ClassDB::bind_method(D_METHOD("get_root_subfolder"), &NativeFileDialog::get_root_subfolder); 29 | ADD_PROPERTY(PropertyInfo(Variant::STRING, "root_subfolder"), "set_root_subfolder", "get_root_subfolder"); 30 | 31 | ClassDB::bind_method(D_METHOD("set_filters", "filter"), &NativeFileDialog::set_filters); 32 | ClassDB::bind_method(D_METHOD("get_filters"), &NativeFileDialog::get_filters); 33 | ClassDB::bind_method(D_METHOD("add_filter", "filter", "description"), &NativeFileDialog::add_filter, DEFVAL("")); 34 | ClassDB::bind_method(D_METHOD("clear_filters"), &NativeFileDialog::clear_filters); 35 | ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "filters"), "set_filters", "get_filters"); 36 | 37 | ClassDB::bind_method(D_METHOD("show"), &NativeFileDialog::show); 38 | ClassDB::bind_method(D_METHOD("hide"), &NativeFileDialog::hide); 39 | 40 | ADD_SIGNAL(MethodInfo("file_selected", PropertyInfo(Variant::STRING, "path"))); 41 | ADD_SIGNAL(MethodInfo("files_selected", PropertyInfo(Variant::PACKED_STRING_ARRAY, "paths"))); 42 | ADD_SIGNAL(MethodInfo("dir_selected", PropertyInfo(Variant::STRING, "dir"))); 43 | 44 | ADD_SIGNAL(MethodInfo("canceled")); 45 | 46 | BIND_ENUM_CONSTANT(FILE_MODE_OPEN_FILE); 47 | BIND_ENUM_CONSTANT(FILE_MODE_OPEN_FILES); 48 | BIND_ENUM_CONSTANT(FILE_MODE_OPEN_DIR); 49 | BIND_ENUM_CONSTANT(FILE_MODE_SAVE_FILE); 50 | 51 | BIND_ENUM_CONSTANT(ACCESS_RESOURCES); 52 | BIND_ENUM_CONSTANT(ACCESS_USERDATA); 53 | BIND_ENUM_CONSTANT(ACCESS_FILESYSTEM); 54 | } 55 | 56 | NativeFileDialog::NativeFileDialog() { 57 | mode_overrides_title = true; 58 | mode = FILE_MODE_SAVE_FILE; 59 | access = ACCESS_RESOURCES; 60 | root_subfolder = ""; 61 | filters = PackedStringArray(); 62 | title = "Save a File"; 63 | } 64 | 65 | NativeFileDialog::~NativeFileDialog() { 66 | hide(); 67 | } 68 | 69 | void NativeFileDialog::_process(float delta) { 70 | if (open_file && open_file->ready(0)) { 71 | PackedStringArray result = PackedStringArray(); 72 | for (int i = 0; i < open_file->result().size(); i++) { 73 | result.append(get_godot_path(open_file->result()[i])); 74 | } 75 | 76 | if (!result.is_empty()) { 77 | if (mode == FILE_MODE_OPEN_FILE && result.size() == 1) { 78 | emit_signal("file_selected", result[0]); 79 | } else { 80 | emit_signal("files_selected", result); 81 | } 82 | } else { 83 | emit_signal("canceled"); 84 | } 85 | 86 | hide(); 87 | } else if (save_file && save_file->ready(0)) { 88 | String result = get_godot_path(save_file->result()); 89 | 90 | if (!result.is_empty()) { 91 | emit_signal("file_selected", result); 92 | } else { 93 | emit_signal("canceled"); 94 | } 95 | 96 | hide(); 97 | } else if (select_folder && select_folder->ready(0)) { 98 | String result = get_godot_path(select_folder->result()); 99 | 100 | if (!result.is_empty()) { 101 | emit_signal("dir_selected", get_godot_path(select_folder->result())); 102 | } else { 103 | emit_signal("canceled"); 104 | } 105 | 106 | hide(); 107 | } 108 | } 109 | 110 | void NativeFileDialog::_exit_tree() { 111 | hide(); 112 | } 113 | 114 | void NativeFileDialog::show() { 115 | ERR_FAIL_COND(!is_inside_tree()); 116 | ERR_FAIL_COND(!is_processing()); 117 | 118 | hide(); 119 | 120 | if (mode == FILE_MODE_OPEN_FILE || mode == FILE_MODE_OPEN_FILES) { 121 | open_file = new pfd::open_file( 122 | String(TranslationServer::get_singleton()->translate(title)).utf8().get_data(), 123 | get_pfd_path(), 124 | get_pfd_filters(), 125 | mode == FILE_MODE_OPEN_FILES ? pfd::opt::multiselect : pfd::opt::none); 126 | } 127 | 128 | if (mode == FILE_MODE_SAVE_FILE) { 129 | save_file = new pfd::save_file( 130 | String(TranslationServer::get_singleton()->translate(title)).utf8().get_data(), 131 | get_pfd_path(), 132 | get_pfd_filters(), 133 | pfd::opt::none); 134 | } 135 | 136 | if (mode == FILE_MODE_OPEN_DIR) { 137 | select_folder = new pfd::select_folder( 138 | String(TranslationServer::get_singleton()->translate(title)).utf8().get_data(), 139 | get_pfd_path(), 140 | pfd::opt::none); 141 | } 142 | } 143 | 144 | void NativeFileDialog::hide() { 145 | if (open_file) { 146 | open_file->kill(); 147 | 148 | delete open_file; 149 | 150 | open_file = nullptr; 151 | } 152 | 153 | if (save_file) { 154 | save_file->kill(); 155 | 156 | delete save_file; 157 | 158 | save_file = nullptr; 159 | } 160 | 161 | if (select_folder) { 162 | select_folder->kill(); 163 | 164 | delete select_folder; 165 | 166 | select_folder = nullptr; 167 | } 168 | } 169 | 170 | void NativeFileDialog::set_title(const String &p_title) { 171 | title = p_title; 172 | } 173 | 174 | String NativeFileDialog::get_title() const { 175 | return title; 176 | } 177 | 178 | void NativeFileDialog::set_mode_overrides_title(bool p_override) { 179 | if (mode_overrides_title == p_override) { 180 | return; 181 | } 182 | 183 | mode_overrides_title = p_override; 184 | 185 | override_title(); 186 | } 187 | 188 | bool NativeFileDialog::is_mode_overriding_title() { 189 | return mode_overrides_title; 190 | } 191 | 192 | void NativeFileDialog::set_file_mode(FileMode p_mode) { 193 | ERR_FAIL_INDEX((int)p_mode, 4); 194 | 195 | if (mode == p_mode) { 196 | return; 197 | } 198 | 199 | mode = p_mode; 200 | 201 | override_title(); 202 | } 203 | 204 | NativeFileDialog::FileMode NativeFileDialog::get_file_mode() const { 205 | return mode; 206 | } 207 | 208 | void NativeFileDialog::set_access(Access p_access) { 209 | ERR_FAIL_INDEX(p_access, 3); 210 | 211 | if (access == p_access) { 212 | return; 213 | } 214 | 215 | access = p_access; 216 | 217 | set_root_subfolder(""); 218 | } 219 | 220 | NativeFileDialog::Access NativeFileDialog::get_access() const { 221 | return access; 222 | } 223 | 224 | void NativeFileDialog::set_root_subfolder(const String &p_root) { 225 | root_subfolder = p_root; 226 | ERR_FAIL_COND_MSG(!DirAccess::dir_exists_absolute(get_root_string() + p_root), "root_subfolder must be an existing sub-directory."); 227 | } 228 | 229 | String NativeFileDialog::get_root_subfolder() const { 230 | return root_subfolder; 231 | } 232 | 233 | void NativeFileDialog::set_filters(const PackedStringArray &p_filters) { 234 | filters = p_filters; 235 | } 236 | 237 | PackedStringArray NativeFileDialog::get_filters() const { 238 | return filters; 239 | } 240 | 241 | void NativeFileDialog::add_filter(const String &p_filter, const String &p_description = "") { 242 | ERR_FAIL_COND_MSG(p_filter.begins_with("."), "Filter must be \"filename.extension\", can't start with dot."); 243 | 244 | if (p_description.is_empty()) { 245 | filters.push_back(p_filter); 246 | } else { 247 | filters.push_back(p_filter + String(" ; ") + p_description); 248 | } 249 | } 250 | 251 | void NativeFileDialog::clear_filters() { 252 | filters.clear(); 253 | } 254 | 255 | std::vector NativeFileDialog::get_pfd_filters() const { 256 | std::vector pfd_filters = std::vector(); 257 | 258 | for (int i = 0; i < filters.size(); i++) { 259 | PackedStringArray filter = filters[i].split(" ; "); 260 | 261 | String name = TranslationServer::get_singleton()->translate(filter.size() == 2 ? filter[1] : ""); 262 | String extensions = filter[0].replace(", ", " "); 263 | 264 | pfd_filters.push_back(name.utf8().get_data()); 265 | pfd_filters.push_back(extensions.utf8().get_data()); 266 | } 267 | 268 | return pfd_filters; 269 | } 270 | 271 | std::string NativeFileDialog::get_pfd_path() const { 272 | String path = get_root_string() + get_root_subfolder(); 273 | String global_path = ProjectSettings::get_singleton()->globalize_path(path); 274 | 275 | // Fixes issue with Open Folder mode, as it requires the string to end with slash 276 | if (!global_path.ends_with("/")) { 277 | global_path = global_path + "/"; 278 | } 279 | 280 | #if _WIN32 281 | global_path = global_path.replace("/", "\\"); 282 | #endif 283 | 284 | return global_path.utf8().get_data(); 285 | } 286 | 287 | String NativeFileDialog::get_godot_path(const std::string &pfd_path) const { 288 | String godot_path = String::utf8(pfd_path.c_str()); 289 | 290 | #if _WIN32 291 | godot_path = godot_path.replace("\\", "/"); 292 | #endif 293 | 294 | return godot_path; 295 | } 296 | 297 | void NativeFileDialog::override_title() { 298 | if (!mode_overrides_title) { 299 | return; 300 | } 301 | 302 | switch (mode) { 303 | case FILE_MODE_OPEN_FILE: 304 | set_title("Open a File"); 305 | break; 306 | case FILE_MODE_OPEN_FILES: 307 | set_title("Open File(s)"); 308 | break; 309 | case FILE_MODE_OPEN_DIR: 310 | set_title("Open a Directory"); 311 | break; 312 | case FILE_MODE_SAVE_FILE: 313 | set_title("Save a File"); 314 | } 315 | } 316 | 317 | String NativeFileDialog::get_root_string() const { 318 | switch (access) { 319 | case ACCESS_RESOURCES: 320 | return "res://"; 321 | case ACCESS_USERDATA: 322 | return "user://"; 323 | default: 324 | return ""; 325 | } 326 | } 327 | -------------------------------------------------------------------------------- /src/native_file_dialog.h: -------------------------------------------------------------------------------- 1 | #ifndef NATIVEFILEDIALOG_H 2 | #define NATIVEFILEDIALOG_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | namespace godot { 11 | 12 | class NativeFileDialog : public Node { 13 | GDCLASS(NativeFileDialog, Node) 14 | 15 | protected: 16 | static void _bind_methods(); 17 | 18 | public: 19 | enum FileMode { 20 | FILE_MODE_OPEN_FILE, 21 | FILE_MODE_OPEN_FILES, 22 | FILE_MODE_OPEN_DIR, 23 | FILE_MODE_SAVE_FILE 24 | }; 25 | 26 | enum Access { 27 | ACCESS_RESOURCES, 28 | ACCESS_USERDATA, 29 | ACCESS_FILESYSTEM 30 | }; 31 | 32 | private: 33 | String title; 34 | bool mode_overrides_title; 35 | FileMode mode; 36 | Access access; 37 | String root_subfolder; 38 | PackedStringArray filters; 39 | 40 | pfd::open_file *open_file = nullptr; 41 | pfd::save_file *save_file = nullptr; 42 | pfd::select_folder *select_folder = nullptr; 43 | 44 | std::vector get_pfd_filters() const; 45 | 46 | std::string get_pfd_path() const; 47 | String get_godot_path(const std::string &pfd_path) const; 48 | 49 | void override_title(); 50 | 51 | String get_root_string() const; 52 | 53 | public: 54 | NativeFileDialog(); 55 | ~NativeFileDialog(); 56 | 57 | void _process(float delta); 58 | void _exit_tree(); 59 | 60 | void show(); 61 | void hide(); 62 | 63 | void set_title(const String &p_title); 64 | String get_title() const; 65 | 66 | void set_mode_overrides_title(bool p_override); 67 | bool is_mode_overriding_title(); 68 | 69 | void set_file_mode(FileMode p_mode); 70 | FileMode get_file_mode() const; 71 | 72 | void set_access(Access p_access); 73 | Access get_access() const; 74 | 75 | void set_root_subfolder(const String &p_root); 76 | String get_root_subfolder() const; 77 | 78 | void set_filters(const PackedStringArray &p_filters); 79 | PackedStringArray get_filters() const; 80 | void add_filter(const String &p_filter, const String &p_description); 81 | void clear_filters(); 82 | }; 83 | 84 | } // namespace godot 85 | 86 | VARIANT_ENUM_CAST(NativeFileDialog::FileMode); 87 | VARIANT_ENUM_CAST(NativeFileDialog::Access); 88 | 89 | #endif // NATIVEFILEDIALOG_H 90 | -------------------------------------------------------------------------------- /src/native_notification.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "native_notification.h" 4 | 5 | using namespace godot; 6 | 7 | void NativeNotification::_bind_methods() { 8 | ClassDB::bind_method(D_METHOD("set_title", "title"), &NativeNotification::set_title); 9 | ClassDB::bind_method(D_METHOD("get_title"), &NativeNotification::get_title); 10 | ADD_PROPERTY(PropertyInfo(Variant::STRING, "title"), "set_title", "get_title"); 11 | 12 | ADD_GROUP("Notification", "notification"); 13 | ClassDB::bind_method(D_METHOD("set_text", "text"), &NativeNotification::set_text); 14 | ClassDB::bind_method(D_METHOD("get_text"), &NativeNotification::get_text); 15 | ADD_PROPERTY(PropertyInfo(Variant::STRING, "notification_text", PROPERTY_HINT_MULTILINE_TEXT, ""), "set_text", "get_text"); 16 | 17 | ClassDB::bind_method(D_METHOD("set_icon", "icon"), &NativeNotification::set_icon); 18 | ClassDB::bind_method(D_METHOD("get_icon"), &NativeNotification::get_icon); 19 | ADD_PROPERTY(PropertyInfo(Variant::INT, "notification_icon", PROPERTY_HINT_ENUM, "INFO,WARNING,ERROR"), "set_icon", "get_icon"); 20 | 21 | ClassDB::bind_method(D_METHOD("send"), &NativeNotification::send); 22 | 23 | BIND_ENUM_CONSTANT(ICON_INFO); 24 | BIND_ENUM_CONSTANT(ICON_WARNING); 25 | BIND_ENUM_CONSTANT(ICON_ERROR); 26 | } 27 | 28 | NativeNotification::NativeNotification() { 29 | title = "Alert!"; 30 | text = ""; 31 | icon = ICON_WARNING; 32 | } 33 | 34 | void NativeNotification::send() { 35 | pfd::icon pfd_icon; 36 | 37 | switch (icon) { 38 | case ICON_INFO: 39 | pfd_icon = pfd::icon::info; 40 | break; 41 | case ICON_WARNING: 42 | pfd_icon = pfd::icon::warning; 43 | break; 44 | case ICON_ERROR: 45 | pfd_icon = pfd::icon::error; 46 | break; 47 | } 48 | 49 | pfd::notify( 50 | String(TranslationServer::get_singleton()->translate(title)).utf8().get_data(), 51 | String(TranslationServer::get_singleton()->translate(text)).utf8().get_data(), 52 | pfd_icon); 53 | } 54 | 55 | void NativeNotification::set_title(const String &p_title) { 56 | title = p_title; 57 | } 58 | 59 | String NativeNotification::get_title() { 60 | return title; 61 | } 62 | 63 | void NativeNotification::set_text(const String &p_text) { 64 | text = p_text; 65 | } 66 | 67 | String NativeNotification::get_text() { 68 | return text; 69 | } 70 | 71 | void NativeNotification::set_icon(Icon p_icon) { 72 | ERR_FAIL_INDEX(p_icon, 3); 73 | 74 | icon = p_icon; 75 | } 76 | 77 | NativeNotification::Icon NativeNotification::get_icon() { 78 | return icon; 79 | } 80 | -------------------------------------------------------------------------------- /src/native_notification.h: -------------------------------------------------------------------------------- 1 | #ifndef NATIVENOTIFICATION_H 2 | #define NATIVENOTIFICATION_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | namespace godot { 11 | 12 | class NativeNotification : public Node { 13 | GDCLASS(NativeNotification, Node); 14 | 15 | protected: 16 | static void _bind_methods(); 17 | 18 | public: 19 | enum Icon { 20 | ICON_INFO, 21 | ICON_WARNING, 22 | ICON_ERROR 23 | }; 24 | 25 | private: 26 | String title; 27 | String text; 28 | Icon icon; 29 | 30 | public: 31 | NativeNotification(); 32 | 33 | void send(); 34 | 35 | void set_title(const String &p_title); 36 | String get_title(); 37 | 38 | void set_text(const String &p_text); 39 | String get_text(); 40 | 41 | void set_icon(Icon p_icon); 42 | Icon get_icon(); 43 | }; 44 | 45 | } // namespace godot 46 | 47 | VARIANT_ENUM_CAST(NativeNotification::Icon); 48 | 49 | #endif // NATIVENOTIFICATION_H 50 | -------------------------------------------------------------------------------- /src/register_types.cpp: -------------------------------------------------------------------------------- 1 | #include "register_types.h" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "native_accept_dialog.h" 10 | #include "native_confirmation_dialog.h" 11 | #include "native_file_dialog.h" 12 | #include "native_notification.h" 13 | 14 | using namespace godot; 15 | 16 | void register_types(ModuleInitializationLevel p_level) { 17 | if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { 18 | return; 19 | } 20 | 21 | ClassDB::register_class(); 22 | ClassDB::register_class(); 23 | ClassDB::register_class(); 24 | ClassDB::register_class(); 25 | } 26 | 27 | void unregister_types(ModuleInitializationLevel p_level) { 28 | if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { 29 | return; 30 | } 31 | } 32 | 33 | extern "C" { 34 | 35 | // Initialization. 36 | 37 | GDExtensionBool GDE_EXPORT native_dialogs_init(GDExtensionInterfaceGetProcAddress p_get_proc_address, const GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) { 38 | GDExtensionBinding::InitObject init_obj(p_get_proc_address, p_library, r_initialization); 39 | 40 | init_obj.register_initializer(register_types); 41 | init_obj.register_terminator(unregister_types); 42 | 43 | init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SCENE); 44 | 45 | return init_obj.init(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/register_types.h: -------------------------------------------------------------------------------- 1 | #ifndef NATIVE_DIALOGS_REGISTER_TYPES_H 2 | #define NATIVE_DIALOGS_REGISTER_TYPES_H 3 | 4 | #include 5 | 6 | using namespace godot; 7 | 8 | void register_types(ModuleInitializationLevel p_level); 9 | void unregister_types(ModuleInitializationLevel p_level); 10 | 11 | #endif // NATIVE_DIALOGS_REGISTER_TYPES_H 12 | --------------------------------------------------------------------------------