[
  {
    "path": ".github/workflows/CI.yml",
    "content": "name: CI\non:\n  push:\n  pull_request:  \n  \ndefaults:\n  run:\n    shell: bash\n    \njobs:\n    test:\n      name: Julia ${{ matrix.julia-version }} ${{ matrix.os }}\n      runs-on: ${{ matrix.os }}\n      strategy:\n        fail-fast: false\n        matrix:\n          julia-version:\n            - \"1.7\"\n            - \"1.9\"\n          os:\n            - ubuntu-latest\n            - macos-latest\n            - windows-latest\n          julia-arch:\n            - x64\n          include:\n            - os: ubuntu-latest\n              prefix: xvfb-run\n      steps: \n        - uses: actions/checkout@v4\n        - uses: julia-actions/setup-julia@latest\n          with:\n            version: ${{ matrix.julia-version }}\n            arch: ${{ matrix.julia-arch }}\n            show-versioninfo: true\n        - uses: julia-actions/julia-buildpkg@v1\n        - uses: julia-actions/julia-runtest@v1\n  \n    docs:\n      name: Documentation\n      runs-on: windows-latest\n      steps:\n        - uses: actions/checkout@v4\n        - uses: julia-actions/setup-julia@latest\n          with:\n            version: '1.7'\n        - run: julia --project=docs -e '\n            using Pkg;\n            Pkg.develop(PackageSpec(; path=pwd()));\n            Pkg.instantiate();\n            Pkg.add(url=\\\"https://github.com/JuliaDocs/Documenter.jl\\\");'\n        - run: julia --project=docs docs/make.jl\n          env:\n            GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n            DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }}\n"
  },
  {
    "path": ".gitignore",
    "content": "\ndocs/build/\ndocs/mousetrap\ndocs/src/02_library/classes.md\ndocs/src/02_library/enums.md\ndocs/src/02_library/functions.md\njll/build\njll/products\n.idea/\n"
  },
  {
    "path": "Artifacts.toml",
    "content": ""
  },
  {
    "path": "LICENSE",
    "content": "                   GNU LESSER GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n\n  This version of the GNU Lesser General Public License incorporates\nthe terms and conditions of version 3 of the GNU General Public\nLicense, supplemented by the additional permissions listed below.\n\n  0. Additional Definitions.\n\n  As used herein, \"this License\" refers to version 3 of the GNU Lesser\nGeneral Public License, and the \"GNU GPL\" refers to version 3 of the GNU\nGeneral Public License.\n\n  \"The Library\" refers to a covered work governed by this License,\nother than an Application or a Combined Work as defined below.\n\n  An \"Application\" is any work that makes use of an interface provided\nby the Library, but which is not otherwise based on the Library.\nDefining a subclass of a class defined by the Library is deemed a mode\nof using an interface provided by the Library.\n\n  A \"Combined Work\" is a work produced by combining or linking an\nApplication with the Library.  The particular version of the Library\nwith which the Combined Work was made is also called the \"Linked\nVersion\".\n\n  The \"Minimal Corresponding Source\" for a Combined Work means the\nCorresponding Source for the Combined Work, excluding any source code\nfor portions of the Combined Work that, considered in isolation, are\nbased on the Application, and not on the Linked Version.\n\n  The \"Corresponding Application Code\" for a Combined Work means the\nobject code and/or source code for the Application, including any data\nand utility programs needed for reproducing the Combined Work from the\nApplication, but excluding the System Libraries of the Combined Work.\n\n  1. Exception to Section 3 of the GNU GPL.\n\n  You may convey a covered work under sections 3 and 4 of this License\nwithout being bound by section 3 of the GNU GPL.\n\n  2. Conveying Modified Versions.\n\n  If you modify a copy of the Library, and, in your modifications, a\nfacility refers to a function or data to be supplied by an Application\nthat uses the facility (other than as an argument passed when the\nfacility is invoked), then you may convey a copy of the modified\nversion:\n\n   a) under this License, provided that you make a good faith effort to\n   ensure that, in the event an Application does not supply the\n   function or data, the facility still operates, and performs\n   whatever part of its purpose remains meaningful, or\n\n   b) under the GNU GPL, with none of the additional permissions of\n   this License applicable to that copy.\n\n  3. Object Code Incorporating Material from Library Header Files.\n\n  The object code form of an Application may incorporate material from\na header file that is part of the Library.  You may convey such object\ncode under terms of your choice, provided that, if the incorporated\nmaterial is not limited to numerical parameters, data structure\nlayouts and accessors, or small macros, inline functions and templates\n(ten or fewer lines in length), you do both of the following:\n\n   a) Give prominent notice with each copy of the object code that the\n   Library is used in it and that the Library and its use are\n   covered by this License.\n\n   b) Accompany the object code with a copy of the GNU GPL and this license\n   document.\n\n  4. Combined Works.\n\n  You may convey a Combined Work under terms of your choice that,\ntaken together, effectively do not restrict modification of the\nportions of the Library contained in the Combined Work and reverse\nengineering for debugging such modifications, if you also do each of\nthe following:\n\n   a) Give prominent notice with each copy of the Combined Work that\n   the Library is used in it and that the Library and its use are\n   covered by this License.\n\n   b) Accompany the Combined Work with a copy of the GNU GPL and this license\n   document.\n\n   c) For a Combined Work that displays copyright notices during\n   execution, include the copyright notice for the Library among\n   these notices, as well as a reference directing the user to the\n   copies of the GNU GPL and this license document.\n\n   d) Do one of the following:\n\n       0) Convey the Minimal Corresponding Source under the terms of this\n       License, and the Corresponding Application Code in a form\n       suitable for, and under terms that permit, the user to\n       recombine or relink the Application with a modified version of\n       the Linked Version to produce a modified Combined Work, in the\n       manner specified by section 6 of the GNU GPL for conveying\n       Corresponding Source.\n\n       1) Use a suitable shared library mechanism for linking with the\n       Library.  A suitable mechanism is one that (a) uses at run time\n       a copy of the Library already present on the user's computer\n       system, and (b) will operate properly with a modified version\n       of the Library that is interface-compatible with the Linked\n       Version.\n\n   e) Provide Installation Information, but only if you would otherwise\n   be required to provide such information under section 6 of the\n   GNU GPL, and only to the extent that such information is\n   necessary to install and execute a modified version of the\n   Combined Work produced by recombining or relinking the\n   Application with a modified version of the Linked Version. (If\n   you use option 4d0, the Installation Information must accompany\n   the Minimal Corresponding Source and Corresponding Application\n   Code. If you use option 4d1, you must provide the Installation\n   Information in the manner specified by section 6 of the GNU GPL\n   for conveying Corresponding Source.)\n\n  5. Combined Libraries.\n\n  You may place library facilities that are a work based on the\nLibrary side by side in a single library together with other library\nfacilities that are not Applications and are not covered by this\nLicense, and convey such a combined library under terms of your\nchoice, if you do both of the following:\n\n   a) Accompany the combined library with a copy of the same work based\n   on the Library, uncombined with any other library facilities,\n   conveyed under the terms of this License.\n\n   b) Give prominent notice with the combined library that part of it\n   is a work based on the Library, and explaining where to find the\n   accompanying uncombined form of the same work.\n\n  6. Revised Versions of the GNU Lesser General Public License.\n\n  The Free Software Foundation may publish revised and/or new versions\nof the GNU Lesser General Public License from time to time. Such new\nversions will be similar in spirit to the present version, but may\ndiffer in detail to address new problems or concerns.\n\n  Each version is given a distinguishing version number. If the\nLibrary as you received it specifies that a certain numbered version\nof the GNU Lesser General Public License \"or any later version\"\napplies to it, you have the option of following the terms and\nconditions either of that published version or of any later version\npublished by the Free Software Foundation. If the Library as you\nreceived it does not specify a version number of the GNU Lesser\nGeneral Public License, you may choose any version of the GNU Lesser\nGeneral Public License ever published by the Free Software Foundation.\n\n  If the Library as you received it specifies that a proxy can decide\nwhether future versions of the GNU Lesser General Public License shall\napply, that proxy's public statement of acceptance of any version is\npermanent authorization for you to choose that version for the\nLibrary.\n"
  },
  {
    "path": "Manifest.toml",
    "content": "# This file is machine-generated - editing it directly is not advised\n\njulia_version = \"1.9.3\"\nmanifest_format = \"2.0\"\nproject_hash = \"35df27af9c014d69e11708d80e596801030231d1\"\n\n[[deps.ANSIColoredPrinters]]\ngit-tree-sha1 = \"574baf8110975760d391c710b6341da1afa48d8c\"\nuuid = \"a4c015fc-c6ff-483c-b24f-f7ea428134e9\"\nversion = \"0.0.1\"\n\n[[deps.AbstractTrees]]\ngit-tree-sha1 = \"faa260e4cb5aba097a73fab382dd4b5819d8ec8c\"\nuuid = \"1520ce14-60c1-5f80-bbc7-55ef81b5835c\"\nversion = \"0.4.4\"\n\n[[deps.ArgParse]]\ndeps = [\"Logging\", \"TextWrap\"]\ngit-tree-sha1 = \"3102bce13da501c9104df33549f511cd25264d7d\"\nuuid = \"c7e460c6-2fb9-53a9-8c5b-16f535851c63\"\nversion = \"1.1.4\"\n\n[[deps.ArgTools]]\nuuid = \"0dad84c5-d112-42e6-8d28-ef12dabb789f\"\nversion = \"1.1.1\"\n\n[[deps.Artifacts]]\nuuid = \"56f22d72-fd6d-98f1-02f0-08ddc0907c33\"\n\n[[deps.AssetRegistry]]\ndeps = [\"Distributed\", \"JSON\", \"Pidfile\", \"SHA\", \"Test\"]\ngit-tree-sha1 = \"b25e88db7944f98789130d7b503276bc34bc098e\"\nuuid = \"bf4720bc-e11a-5d0c-854e-bdca1663c893\"\nversion = \"0.1.0\"\n\n[[deps.Attr_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\", \"Pkg\"]\ngit-tree-sha1 = \"b132f9aeb209b8790dcc286c857f300369219d8d\"\nuuid = \"1fd713ca-387f-5abc-8002-d8b8b1623b73\"\nversion = \"2.5.1+0\"\n\n[[deps.AutoHashEquals]]\ngit-tree-sha1 = \"45bb6705d93be619b81451bb2006b7ee5d4e4453\"\nuuid = \"15f4f7f2-30c1-5605-9d31-71845cf9641f\"\nversion = \"0.2.0\"\n\n[[deps.Base64]]\nuuid = \"2a0f44e3-6c83-55bd-87e4-b1978d98bd5f\"\n\n[[deps.BinaryBuilder]]\ndeps = [\"ArgParse\", \"BinaryBuilderBase\", \"Dates\", \"Downloads\", \"GitHub\", \"HTTP\", \"JLD2\", \"JSON\", \"LibGit2\", \"Libdl\", \"Logging\", \"LoggingExtras\", \"ObjectFile\", \"OutputCollectors\", \"Pkg\", \"PkgLicenses\", \"REPL\", \"Random\", \"Registrator\", \"RegistryTools\", \"SHA\", \"Scratch\", \"Sockets\", \"TOML\", \"UUIDs\", \"ghr_jll\"]\ngit-tree-sha1 = \"d3e0f34be7db381be6a4b201f7d1b2f4212a4379\"\nuuid = \"12aac903-9f7c-5d81-afc2-d9565ea332ae\"\nversion = \"0.5.6\"\n\n[[deps.BinaryBuilderBase]]\ndeps = [\"Bzip2_jll\", \"CodecZlib\", \"Downloads\", \"Gzip_jll\", \"HistoricalStdlibVersions\", \"InteractiveUtils\", \"JLLWrappers\", \"JSON\", \"LibGit2\", \"LibGit2_jll\", \"Libdl\", \"Logging\", \"OrderedCollections\", \"OutputCollectors\", \"Pkg\", \"Printf\", \"ProgressMeter\", \"REPL\", \"Random\", \"SHA\", \"Scratch\", \"SimpleBufferStream\", \"TOML\", \"Tar\", \"Tar_jll\", \"UUIDs\", \"XZ_jll\", \"Zstd_jll\", \"p7zip_jll\", \"pigz_jll\"]\ngit-tree-sha1 = \"5d3967982f7f293703404d3adc3014bd54d2c580\"\nuuid = \"7f725544-6523-48cd-82d1-3fa08ff4056e\"\nversion = \"1.25.0\"\n\n[[deps.BitFlags]]\ngit-tree-sha1 = \"43b1a4a8f797c1cddadf60499a8a077d4af2cd2d\"\nuuid = \"d1d4a3ce-64b1-5f1a-9ba4-7e7e69966f35\"\nversion = \"0.1.7\"\n\n[[deps.Bzip2_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\", \"Pkg\"]\ngit-tree-sha1 = \"19a35467a82e236ff51bc17a3a44b69ef35185a2\"\nuuid = \"6e34b625-4abd-537c-b88f-471c36dfa7a0\"\nversion = \"1.0.8+0\"\n\n[[deps.Cairo_jll]]\ndeps = [\"Artifacts\", \"Bzip2_jll\", \"CompilerSupportLibraries_jll\", \"Fontconfig_jll\", \"FreeType2_jll\", \"Glib_jll\", \"JLLWrappers\", \"LZO_jll\", \"Libdl\", \"Pixman_jll\", \"Pkg\", \"Xorg_libXext_jll\", \"Xorg_libXrender_jll\", \"Zlib_jll\", \"libpng_jll\"]\ngit-tree-sha1 = \"4b859a208b2397a7a623a03449e4636bdb17bcf2\"\nuuid = \"83423d85-b0ee-5818-9007-b63ccbeb887a\"\nversion = \"1.16.1+1\"\n\n[[deps.CodecZlib]]\ndeps = [\"TranscodingStreams\", \"Zlib_jll\"]\ngit-tree-sha1 = \"02aa26a4cf76381be7f66e020a3eddeb27b0a092\"\nuuid = \"944b1d66-785c-5afd-91f1-9de20f533193\"\nversion = \"0.7.2\"\n\n[[deps.Compat]]\ndeps = [\"UUIDs\"]\ngit-tree-sha1 = \"8a62af3e248a8c4bad6b32cbbe663ae02275e32c\"\nuuid = \"34da2185-b29b-5c13-b0c7-acf172513d20\"\nversion = \"4.10.0\"\nweakdeps = [\"Dates\", \"LinearAlgebra\"]\n\n    [deps.Compat.extensions]\n    CompatLinearAlgebraExt = \"LinearAlgebra\"\n\n[[deps.CompilerSupportLibraries_jll]]\ndeps = [\"Artifacts\", \"Libdl\"]\nuuid = \"e66e0078-7015-5450-92f7-15fbd957f2ae\"\nversion = \"1.0.5+0\"\n\n[[deps.ConcurrentUtilities]]\ndeps = [\"Serialization\", \"Sockets\"]\ngit-tree-sha1 = \"5372dbbf8f0bdb8c700db5367132925c0771ef7e\"\nuuid = \"f0e56b4a-5159-44fe-b623-3e5288b988bb\"\nversion = \"2.2.1\"\n\n[[deps.CxxWrap]]\ndeps = [\"Libdl\", \"MacroTools\", \"libcxxwrap_julia_jll\"]\ngit-tree-sha1 = \"67ebbc028b385658142d3c0644e07992616653d9\"\nuuid = \"1f15a43c-97ca-5a2a-ae31-89f07a497df4\"\nversion = \"0.14.0\"\n\n[[deps.DataAPI]]\ngit-tree-sha1 = \"8da84edb865b0b5b0100c0666a9bc9a0b71c553c\"\nuuid = \"9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a\"\nversion = \"1.15.0\"\n\n[[deps.DataValueInterfaces]]\ngit-tree-sha1 = \"bfc1187b79289637fa0ef6d4436ebdfe6905cbd6\"\nuuid = \"e2d170a0-9d28-54be-80f0-106bbe20a464\"\nversion = \"1.0.0\"\n\n[[deps.Dates]]\ndeps = [\"Printf\"]\nuuid = \"ade2ca70-3891-5945-98fb-dc099432e06a\"\n\n[[deps.Distributed]]\ndeps = [\"Random\", \"Serialization\", \"Sockets\"]\nuuid = \"8ba89e20-285c-5b6f-9357-94700520ee1b\"\n\n[[deps.DocStringExtensions]]\ndeps = [\"LibGit2\"]\ngit-tree-sha1 = \"2fb1e02f2b635d0845df5d7c167fec4dd739b00d\"\nuuid = \"ffbed154-4ef7-542d-bbb7-c09d3a79fcae\"\nversion = \"0.9.3\"\n\n[[deps.Documenter]]\ndeps = [\"ANSIColoredPrinters\", \"AbstractTrees\", \"Base64\", \"Dates\", \"DocStringExtensions\", \"Downloads\", \"IOCapture\", \"InteractiveUtils\", \"JSON\", \"LibGit2\", \"Logging\", \"Markdown\", \"MarkdownAST\", \"Pkg\", \"PrecompileTools\", \"REPL\", \"RegistryInstances\", \"SHA\", \"Test\", \"Unicode\"]\ngit-tree-sha1 = \"f667b805e90d643aeb1ca70189827f991a7cc115\"\nuuid = \"e30172f5-a6a5-5a46-863b-614d45cd2de4\"\nversion = \"1.1.0\"\n\n[[deps.Downloads]]\ndeps = [\"ArgTools\", \"FileWatching\", \"LibCURL\", \"NetworkOptions\"]\nuuid = \"f43a241f-c20a-4ad4-852c-f6b1247861c6\"\nversion = \"1.6.0\"\n\n[[deps.EpollShim_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\"]\ngit-tree-sha1 = \"8e9441ee83492030ace98f9789a654a6d0b1f643\"\nuuid = \"2702e6a9-849d-5ed8-8c21-79e8b8f9ee43\"\nversion = \"0.0.20230411+0\"\n\n[[deps.ExceptionUnwrapping]]\ndeps = [\"Test\"]\ngit-tree-sha1 = \"e90caa41f5a86296e014e148ee061bd6c3edec96\"\nuuid = \"460bff9d-24e4-43bc-9d9f-a8973cb893f4\"\nversion = \"0.1.9\"\n\n[[deps.Expat_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\"]\ngit-tree-sha1 = \"4558ab818dcceaab612d1bb8c19cee87eda2b83c\"\nuuid = \"2e619515-83b5-522b-bb60-26c02a35a201\"\nversion = \"2.5.0+0\"\n\n[[deps.ExprTools]]\ngit-tree-sha1 = \"27415f162e6028e81c72b82ef756bf321213b6ec\"\nuuid = \"e2ba6199-217a-4e67-a87a-7c52f15ade04\"\nversion = \"0.1.10\"\n\n[[deps.FileIO]]\ndeps = [\"Pkg\", \"Requires\", \"UUIDs\"]\ngit-tree-sha1 = \"299dc33549f68299137e51e6d49a13b5b1da9673\"\nuuid = \"5789e2e9-d7fb-5bc7-8068-2c6fae9b9549\"\nversion = \"1.16.1\"\n\n[[deps.FileWatching]]\nuuid = \"7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee\"\n\n[[deps.Fontconfig_jll]]\ndeps = [\"Artifacts\", \"Bzip2_jll\", \"Expat_jll\", \"FreeType2_jll\", \"JLLWrappers\", \"Libdl\", \"Libuuid_jll\", \"Pkg\", \"Zlib_jll\"]\ngit-tree-sha1 = \"21efd19106a55620a188615da6d3d06cd7f6ee03\"\nuuid = \"a3f928ae-7b40-5064-980b-68af3947d34b\"\nversion = \"2.13.93+0\"\n\n[[deps.Formatting]]\ndeps = [\"Printf\"]\ngit-tree-sha1 = \"8339d61043228fdd3eb658d86c926cb282ae72a8\"\nuuid = \"59287772-0a20-5a39-b81b-1366585eb4c0\"\nversion = \"0.4.2\"\n\n[[deps.FreeType2_jll]]\ndeps = [\"Artifacts\", \"Bzip2_jll\", \"JLLWrappers\", \"Libdl\", \"Zlib_jll\"]\ngit-tree-sha1 = \"d8db6a5a2fe1381c1ea4ef2cab7c69c2de7f9ea0\"\nuuid = \"d7e528f0-a631-5988-bf34-fe36492bcfd7\"\nversion = \"2.13.1+0\"\n\n[[deps.FriBidi_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\", \"Pkg\"]\ngit-tree-sha1 = \"aa31987c2ba8704e23c6c8ba8a4f769d5d7e4f91\"\nuuid = \"559328eb-81f9-559d-9380-de523a88c83c\"\nversion = \"1.0.10+0\"\n\n[[deps.GLEW_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\", \"Libglvnd_jll\", \"Xorg_libXi_jll\"]\ngit-tree-sha1 = \"0333b4790daf4e20cdd61b79cae9b86e2b98f359\"\nuuid = \"bde7f898-03f7-559e-8810-194d950ce600\"\nversion = \"2.2.0+0\"\n\n[[deps.GLU_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\", \"Libglvnd_jll\", \"Pkg\"]\ngit-tree-sha1 = \"65af046f4221e27fb79b28b6ca89dd1d12bc5ec7\"\nuuid = \"bd17208b-e95e-5925-bf81-e2f59b3e5c61\"\nversion = \"9.0.1+0\"\n\n[[deps.GTK4_jll]]\ndeps = [\"Artifacts\", \"Cairo_jll\", \"Fontconfig_jll\", \"FreeType2_jll\", \"FriBidi_jll\", \"Glib_jll\", \"Graphene_jll\", \"HarfBuzz_jll\", \"JLLWrappers\", \"Libdl\", \"Libepoxy_jll\", \"Pango_jll\", \"Wayland_jll\", \"Wayland_protocols_jll\", \"Xorg_libX11_jll\", \"Xorg_libXcursor_jll\", \"Xorg_libXdamage_jll\", \"Xorg_libXext_jll\", \"Xorg_libXfixes_jll\", \"Xorg_libXi_jll\", \"Xorg_libXinerama_jll\", \"Xorg_libXrandr_jll\", \"Xorg_libXrender_jll\", \"gdk_pixbuf_jll\", \"iso_codes_jll\", \"xkbcommon_jll\"]\ngit-tree-sha1 = \"e4f9ed37b104967acb37989984d34ad07f3a2d26\"\nuuid = \"6ebb71f1-8434-552f-b6b1-dc18babcca63\"\nversion = \"4.10.5+0\"\n\n[[deps.Gettext_jll]]\ndeps = [\"Artifacts\", \"CompilerSupportLibraries_jll\", \"JLLWrappers\", \"Libdl\", \"Libiconv_jll\", \"Pkg\", \"XML2_jll\"]\ngit-tree-sha1 = \"9b02998aba7bf074d14de89f9d37ca24a1a0b046\"\nuuid = \"78b55507-aeef-58d4-861c-77aaff3498b1\"\nversion = \"0.21.0+0\"\n\n[[deps.GitForge]]\ndeps = [\"Dates\", \"HTTP\", \"JSON3\", \"StructTypes\", \"TimeZones\", \"UUIDs\"]\ngit-tree-sha1 = \"79f1366c7130a92c3719b296f04e96fe90c26626\"\nuuid = \"8f6bce27-0656-5410-875b-07a5572985df\"\nversion = \"0.4.2\"\n\n[[deps.GitHub]]\ndeps = [\"Base64\", \"Dates\", \"HTTP\", \"JSON\", \"MbedTLS\", \"Sockets\", \"SodiumSeal\", \"URIs\"]\ngit-tree-sha1 = \"5688002de970b9eee14b7af7bbbd1fdac10c9bbe\"\nuuid = \"bc5e4493-9b4d-5f90-b8aa-2b2bcaad7a26\"\nversion = \"5.8.2\"\n\n[[deps.Glib_jll]]\ndeps = [\"Artifacts\", \"Gettext_jll\", \"JLLWrappers\", \"Libdl\", \"Libffi_jll\", \"Libiconv_jll\", \"Libmount_jll\", \"PCRE2_jll\", \"Zlib_jll\"]\ngit-tree-sha1 = \"e94c92c7bf4819685eb80186d51c43e71d4afa17\"\nuuid = \"7746bdde-850d-59dc-9ae8-88ece973131d\"\nversion = \"2.76.5+0\"\n\n[[deps.Graphene_jll]]\ndeps = [\"Artifacts\", \"Glib_jll\", \"JLLWrappers\", \"Libdl\", \"Pkg\"]\ngit-tree-sha1 = \"61850a17f562453e3485a489c9c8cccb3abcab93\"\nuuid = \"75302f13-0b7e-5bab-a6d1-23fa92e4c2ea\"\nversion = \"1.10.6+0\"\n\n[[deps.Graphite2_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\", \"Pkg\"]\ngit-tree-sha1 = \"344bf40dcab1073aca04aa0df4fb092f920e4011\"\nuuid = \"3b182d85-2403-5c21-9c21-1e1f0cc25472\"\nversion = \"1.3.14+0\"\n\n[[deps.Gzip_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\", \"Pkg\"]\ngit-tree-sha1 = \"e7a48abe6d5ba74904df632160aa4486b0e80bf0\"\nuuid = \"be1be57a-8558-53c3-a7e5-50095f79957e\"\nversion = \"1.12.0+0\"\n\n[[deps.HTTP]]\ndeps = [\"Base64\", \"CodecZlib\", \"ConcurrentUtilities\", \"Dates\", \"ExceptionUnwrapping\", \"Logging\", \"LoggingExtras\", \"MbedTLS\", \"NetworkOptions\", \"OpenSSL\", \"Random\", \"SimpleBufferStream\", \"Sockets\", \"URIs\", \"UUIDs\"]\ngit-tree-sha1 = \"5eab648309e2e060198b45820af1a37182de3cce\"\nuuid = \"cd3eb016-35fb-5094-929b-558a96fad6f3\"\nversion = \"1.10.0\"\n\n[[deps.HarfBuzz_jll]]\ndeps = [\"Artifacts\", \"Cairo_jll\", \"Fontconfig_jll\", \"FreeType2_jll\", \"Glib_jll\", \"Graphite2_jll\", \"JLLWrappers\", \"Libdl\", \"Libffi_jll\", \"Pkg\"]\ngit-tree-sha1 = \"129acf094d168394e80ee1dc4bc06ec835e510a3\"\nuuid = \"2e76f6c2-a576-52d4-95c1-20adfe4de566\"\nversion = \"2.8.1+1\"\n\n[[deps.Hiccup]]\ndeps = [\"MacroTools\", \"Test\"]\ngit-tree-sha1 = \"6187bb2d5fcbb2007c39e7ac53308b0d371124bd\"\nuuid = \"9fb69e20-1954-56bb-a84f-559cc56a8ff7\"\nversion = \"0.2.2\"\n\n[[deps.HistoricalStdlibVersions]]\ngit-tree-sha1 = \"56ce882a06a846583e82c7c2c2d5194029eec232\"\nuuid = \"6df8b67a-e8a0-4029-b4b7-ac196fe72102\"\nversion = \"1.2.1\"\n\n[[deps.IOCapture]]\ndeps = [\"Logging\", \"Random\"]\ngit-tree-sha1 = \"d75853a0bdbfb1ac815478bacd89cd27b550ace6\"\nuuid = \"b5f81e59-6552-4d32-b1f0-c071b021bf89\"\nversion = \"0.2.3\"\n\n[[deps.InlineStrings]]\ndeps = [\"Parsers\"]\ngit-tree-sha1 = \"9cc2baf75c6d09f9da536ddf58eb2f29dedaf461\"\nuuid = \"842dd82b-1e85-43dc-bf29-5d0ee9dffc48\"\nversion = \"1.4.0\"\n\n[[deps.InteractiveUtils]]\ndeps = [\"Markdown\"]\nuuid = \"b77e0a4c-d291-57a0-90e8-8db25a27a240\"\n\n[[deps.IteratorInterfaceExtensions]]\ngit-tree-sha1 = \"a3f24677c21f5bbe9d2a714f95dcd58337fb2856\"\nuuid = \"82899510-4779-5014-852e-03e436cf321d\"\nversion = \"1.0.0\"\n\n[[deps.JLD2]]\ndeps = [\"FileIO\", \"MacroTools\", \"Mmap\", \"OrderedCollections\", \"Pkg\", \"Printf\", \"Reexport\", \"Requires\", \"TranscodingStreams\", \"UUIDs\"]\ngit-tree-sha1 = \"c11d691a0dc8e90acfa4740d293ade57f68bfdbb\"\nuuid = \"033835bb-8acc-5ee8-8aae-3f567f8a3819\"\nversion = \"0.4.35\"\n\n[[deps.JLLWrappers]]\ndeps = [\"Artifacts\", \"Preferences\"]\ngit-tree-sha1 = \"7e5d6779a1e09a36db2a7b6cff50942a0a7d0fca\"\nuuid = \"692b3bcd-3c85-4b1f-b108-f13ce0eb3210\"\nversion = \"1.5.0\"\n\n[[deps.JSON]]\ndeps = [\"Dates\", \"Mmap\", \"Parsers\", \"Unicode\"]\ngit-tree-sha1 = \"31e996f0a15c7b280ba9f76636b3ff9e2ae58c9a\"\nuuid = \"682c06a0-de6a-54ab-a142-c8b1cf79cde6\"\nversion = \"0.21.4\"\n\n[[deps.JSON3]]\ndeps = [\"Dates\", \"Mmap\", \"Parsers\", \"PrecompileTools\", \"StructTypes\", \"UUIDs\"]\ngit-tree-sha1 = \"95220473901735a0f4df9d1ca5b171b568b2daa3\"\nuuid = \"0f8b85d8-7281-11e9-16c2-39a750bddbf1\"\nversion = \"1.13.2\"\n\n[[deps.JpegTurbo_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\"]\ngit-tree-sha1 = \"6f2675ef130a300a112286de91973805fcc5ffbc\"\nuuid = \"aacddb02-875f-59d6-b918-886e6ef4fbf8\"\nversion = \"2.1.91+0\"\n\n[[deps.LERC_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\", \"Pkg\"]\ngit-tree-sha1 = \"bf36f528eec6634efc60d7ec062008f171071434\"\nuuid = \"88015f11-f218-50d7-93a8-a6af411a945d\"\nversion = \"3.0.0+1\"\n\n[[deps.LLVMOpenMP_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\", \"Pkg\"]\ngit-tree-sha1 = \"f689897ccbe049adb19a065c495e75f372ecd42b\"\nuuid = \"1d63c593-3942-5779-bab2-d838dc0a180e\"\nversion = \"15.0.4+0\"\n\n[[deps.LZO_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\", \"Pkg\"]\ngit-tree-sha1 = \"e5b909bcf985c5e2605737d2ce278ed791b89be6\"\nuuid = \"dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac\"\nversion = \"2.10.1+0\"\n\n[[deps.LaTeXStrings]]\ngit-tree-sha1 = \"f2355693d6778a178ade15952b7ac47a4ff97996\"\nuuid = \"b964fa9f-0449-5b57-a5c2-d3ea65f4040f\"\nversion = \"1.3.0\"\n\n[[deps.Latexify]]\ndeps = [\"Formatting\", \"InteractiveUtils\", \"LaTeXStrings\", \"MacroTools\", \"Markdown\", \"OrderedCollections\", \"Printf\", \"Requires\"]\ngit-tree-sha1 = \"f428ae552340899a935973270b8d98e5a31c49fe\"\nuuid = \"23fbe1c1-3f47-55db-b15f-69d7ec21a316\"\nversion = \"0.16.1\"\n\n    [deps.Latexify.extensions]\n    DataFramesExt = \"DataFrames\"\n    SymEngineExt = \"SymEngine\"\n\n    [deps.Latexify.weakdeps]\n    DataFrames = \"a93c6f00-e57d-5684-b7b6-d8193f3e46c0\"\n    SymEngine = \"123dc426-2d89-5057-bbad-38513e3affd8\"\n\n[[deps.LazilyInitializedFields]]\ngit-tree-sha1 = \"410fe4739a4b092f2ffe36fcb0dcc3ab12648ce1\"\nuuid = \"0e77f7df-68c5-4e49-93ce-4cd80f5598bf\"\nversion = \"1.2.1\"\n\n[[deps.LazyArtifacts]]\ndeps = [\"Artifacts\", \"Pkg\"]\nuuid = \"4af54fe1-eca0-43a8-85a7-787d91b784e3\"\n\n[[deps.LibCURL]]\ndeps = [\"LibCURL_jll\", \"MozillaCACerts_jll\"]\nuuid = \"b27032c2-a3e7-50c8-80cd-2d36dbcbfd21\"\nversion = \"0.6.3\"\n\n[[deps.LibCURL_jll]]\ndeps = [\"Artifacts\", \"LibSSH2_jll\", \"Libdl\", \"MbedTLS_jll\", \"Zlib_jll\", \"nghttp2_jll\"]\nuuid = \"deac9b47-8bc7-5906-a0fe-35ac56dc84c0\"\nversion = \"7.84.0+0\"\n\n[[deps.LibGit2]]\ndeps = [\"Base64\", \"NetworkOptions\", \"Printf\", \"SHA\"]\nuuid = \"76f85450-5226-5b5a-8eaa-529ad045b433\"\n\n[[deps.LibGit2_jll]]\ndeps = [\"Artifacts\", \"LibSSH2_jll\", \"Libdl\", \"MbedTLS_jll\"]\nuuid = \"e37daf67-58a4-590a-8e99-b0245dd2ffc5\"\nversion = \"1.5.0+1\"\n\n[[deps.LibSSH2_jll]]\ndeps = [\"Artifacts\", \"Libdl\", \"MbedTLS_jll\"]\nuuid = \"29816b5a-b9ab-546f-933c-edad1886dfa8\"\nversion = \"1.10.2+0\"\n\n[[deps.Libdl]]\nuuid = \"8f399da3-3557-5675-b5ff-fb832c97cbdb\"\n\n[[deps.Libepoxy_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\", \"Libglvnd_jll\", \"Pkg\", \"Xorg_libX11_jll\"]\ngit-tree-sha1 = \"7a0158b71f8be5c771e7a273183b2d0ac35278c5\"\nuuid = \"42c93a91-0102-5b3f-8f9d-e41de60ac950\"\nversion = \"1.5.10+0\"\n\n[[deps.Libffi_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\", \"Pkg\"]\ngit-tree-sha1 = \"0b4a5d71f3e5200a7dff793393e09dfc2d874290\"\nuuid = \"e9f186c6-92d2-5b65-8a66-fee21dc1b490\"\nversion = \"3.2.2+1\"\n\n[[deps.Libgcrypt_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\", \"Libgpg_error_jll\", \"Pkg\"]\ngit-tree-sha1 = \"64613c82a59c120435c067c2b809fc61cf5166ae\"\nuuid = \"d4300ac3-e22c-5743-9152-c294e39db1e4\"\nversion = \"1.8.7+0\"\n\n[[deps.Libglvnd_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\", \"Pkg\", \"Xorg_libX11_jll\", \"Xorg_libXext_jll\"]\ngit-tree-sha1 = \"6f73d1dd803986947b2c750138528a999a6c7733\"\nuuid = \"7e76a0d4-f3c7-5321-8279-8d96eeed0f29\"\nversion = \"1.6.0+0\"\n\n[[deps.Libgpg_error_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\", \"Pkg\"]\ngit-tree-sha1 = \"c333716e46366857753e273ce6a69ee0945a6db9\"\nuuid = \"7add5ba3-2f88-524e-9cd5-f83b8a55f7b8\"\nversion = \"1.42.0+0\"\n\n[[deps.Libiconv_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\"]\ngit-tree-sha1 = \"f9557a255370125b405568f9767d6d195822a175\"\nuuid = \"94ce4f54-9a6c-5748-9c1c-f9c7231a4531\"\nversion = \"1.17.0+0\"\n\n[[deps.Libmount_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\", \"Pkg\"]\ngit-tree-sha1 = \"9c30530bf0effd46e15e0fdcf2b8636e78cbbd73\"\nuuid = \"4b2f31a3-9ecc-558c-b454-b3730dcb73e9\"\nversion = \"2.35.0+0\"\n\n[[deps.Libtiff_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"JpegTurbo_jll\", \"LERC_jll\", \"Libdl\", \"Pkg\", \"Zlib_jll\", \"Zstd_jll\"]\ngit-tree-sha1 = \"3eb79b0ca5764d4799c06699573fd8f533259713\"\nuuid = \"89763e89-9b03-5906-acba-b20f662cd828\"\nversion = \"4.4.0+0\"\n\n[[deps.Libuuid_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\", \"Pkg\"]\ngit-tree-sha1 = \"7f3efec06033682db852f8b3bc3c1d2b0a0ab066\"\nuuid = \"38a345b3-de98-5d2b-a5d3-14cd9215e700\"\nversion = \"2.36.0+0\"\n\n[[deps.LinearAlgebra]]\ndeps = [\"Libdl\", \"OpenBLAS_jll\", \"libblastrampoline_jll\"]\nuuid = \"37e2e46d-f89d-539d-b4ee-838fcccc9c8e\"\n\n[[deps.Logging]]\nuuid = \"56ddb016-857b-54e1-b83d-db4d58db5568\"\n\n[[deps.LoggingExtras]]\ndeps = [\"Dates\", \"Logging\"]\ngit-tree-sha1 = \"5d4d2d9904227b8bd66386c1138cf4d5ffa826bf\"\nuuid = \"e6f89c97-d47a-5376-807f-9c37f3926c36\"\nversion = \"0.4.9\"\n\n[[deps.MacroTools]]\ndeps = [\"Markdown\", \"Random\"]\ngit-tree-sha1 = \"9ee1618cbf5240e6d4e0371d6f24065083f60c48\"\nuuid = \"1914dd2f-81c6-5fcd-8719-6d5c9610ff09\"\nversion = \"0.5.11\"\n\n[[deps.Markdown]]\ndeps = [\"Base64\"]\nuuid = \"d6f4376e-aef5-505a-96c1-9c027394607a\"\n\n[[deps.MarkdownAST]]\ndeps = [\"AbstractTrees\", \"Markdown\"]\ngit-tree-sha1 = \"e8513266815200c0c8f522d6d44ffb5e9b366ae4\"\nuuid = \"d0879d2d-cac2-40c8-9cee-1863dc0c7391\"\nversion = \"0.1.1\"\n\n[[deps.MbedTLS]]\ndeps = [\"Dates\", \"MbedTLS_jll\", \"MozillaCACerts_jll\", \"Random\", \"Sockets\"]\ngit-tree-sha1 = \"03a9b9718f5682ecb107ac9f7308991db4ce395b\"\nuuid = \"739be429-bea8-5141-9913-cc70e7f3736d\"\nversion = \"1.1.7\"\n\n[[deps.MbedTLS_jll]]\ndeps = [\"Artifacts\", \"Libdl\"]\nuuid = \"c8ffd9c3-330d-5841-b78e-0817d7145fa1\"\nversion = \"2.28.2+0\"\n\n[[deps.Mmap]]\nuuid = \"a63ad114-7e13-5084-954f-fe012c677804\"\n\n[[deps.Mocking]]\ndeps = [\"Compat\", \"ExprTools\"]\ngit-tree-sha1 = \"4cc0c5a83933648b615c36c2b956d94fda70641e\"\nuuid = \"78c3b35d-d492-501b-9361-3d52fe80e533\"\nversion = \"0.7.7\"\n\n[[deps.MozillaCACerts_jll]]\nuuid = \"14a3606d-f60d-562e-9121-12d972cd8159\"\nversion = \"2022.10.11\"\n\n[[deps.Mustache]]\ndeps = [\"Printf\", \"Tables\"]\ngit-tree-sha1 = \"821e918c170ead5298ff84bffee41dd28929a681\"\nuuid = \"ffc61752-8dc7-55ee-8c37-f3e9cdd09e70\"\nversion = \"1.0.17\"\n\n[[deps.Mux]]\ndeps = [\"AssetRegistry\", \"Base64\", \"HTTP\", \"Hiccup\", \"MbedTLS\", \"Pkg\", \"Sockets\"]\ngit-tree-sha1 = \"0bdaa479939d2a1f85e2f93e38fbccfcb73175a5\"\nuuid = \"a975b10e-0019-58db-a62f-e48ff68538c9\"\nversion = \"1.0.1\"\n\n[[deps.NetworkOptions]]\nuuid = \"ca575930-c2e3-43a9-ace4-1e988b2c1908\"\nversion = \"1.2.0\"\n\n[[deps.ObjectFile]]\ndeps = [\"Reexport\", \"StructIO\"]\ngit-tree-sha1 = \"55ce61d43409b1fb0279d1781bf3b0f22c83ab3b\"\nuuid = \"d8793406-e978-5875-9003-1fc021f44a92\"\nversion = \"0.3.7\"\n\n[[deps.OpenBLAS_jll]]\ndeps = [\"Artifacts\", \"CompilerSupportLibraries_jll\", \"Libdl\"]\nuuid = \"4536629a-c528-5b80-bd46-f80d51c5b363\"\nversion = \"0.3.21+4\"\n\n[[deps.OpenGLMathematics_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\", \"Pkg\"]\ngit-tree-sha1 = \"909a33f40ec4bc2ecc9fbc9379c1314bd480a48f\"\nuuid = \"cc7be9be-d298-5888-8f50-b85d5f9d6d73\"\nversion = \"0.9.9+0\"\n\n[[deps.OpenSSL]]\ndeps = [\"BitFlags\", \"Dates\", \"MozillaCACerts_jll\", \"OpenSSL_jll\", \"Sockets\"]\ngit-tree-sha1 = \"51901a49222b09e3743c65b8847687ae5fc78eb2\"\nuuid = \"4d8831e6-92b7-49fb-bdf8-b643e874388c\"\nversion = \"1.4.1\"\n\n[[deps.OpenSSL_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\"]\ngit-tree-sha1 = \"ceeda72c9fd6bbebc4f4f598560789145a8b6c4c\"\nuuid = \"458c3c95-2e84-50aa-8efc-19380b2a3a95\"\nversion = \"3.0.11+0\"\n\n[[deps.OrderedCollections]]\ngit-tree-sha1 = \"2e73fe17cac3c62ad1aebe70d44c963c3cfdc3e3\"\nuuid = \"bac558e1-5e72-5ebc-8fee-abe8a469f55d\"\nversion = \"1.6.2\"\n\n[[deps.OutputCollectors]]\ngit-tree-sha1 = \"5d3f2b3b2e2a9d7d6f1774c78e94530ac7f360cc\"\nuuid = \"6c11c7d4-943b-4e2b-80de-f2cfc2930a8c\"\nversion = \"0.1.1\"\n\n[[deps.PCRE2_jll]]\ndeps = [\"Artifacts\", \"Libdl\"]\nuuid = \"efcefdf7-47ab-520b-bdef-62a2eaa19f15\"\nversion = \"10.42.0+0\"\n\n[[deps.Pango_jll]]\ndeps = [\"Artifacts\", \"Cairo_jll\", \"Fontconfig_jll\", \"FreeType2_jll\", \"FriBidi_jll\", \"Glib_jll\", \"HarfBuzz_jll\", \"JLLWrappers\", \"Libdl\"]\ngit-tree-sha1 = \"4745216e94f71cb768d58330b059c9b76f32cb66\"\nuuid = \"36c8627f-9965-5494-a995-c6b170f724f3\"\nversion = \"1.50.14+0\"\n\n[[deps.Parsers]]\ndeps = [\"Dates\", \"PrecompileTools\", \"UUIDs\"]\ngit-tree-sha1 = \"716e24b21538abc91f6205fd1d8363f39b442851\"\nuuid = \"69de0a69-1ddd-5017-9359-2bf0b02dc9f0\"\nversion = \"2.7.2\"\n\n[[deps.Pidfile]]\ndeps = [\"FileWatching\", \"Test\"]\ngit-tree-sha1 = \"2d8aaf8ee10df53d0dfb9b8ee44ae7c04ced2b03\"\nuuid = \"fa939f87-e72e-5be4-a000-7fc836dbe307\"\nversion = \"1.3.0\"\n\n[[deps.Pixman_jll]]\ndeps = [\"Artifacts\", \"CompilerSupportLibraries_jll\", \"JLLWrappers\", \"LLVMOpenMP_jll\", \"Libdl\"]\ngit-tree-sha1 = \"64779bc4c9784fee475689a1752ef4d5747c5e87\"\nuuid = \"30392449-352a-5448-841d-b1acce4e97dc\"\nversion = \"0.42.2+0\"\n\n[[deps.Pkg]]\ndeps = [\"Artifacts\", \"Dates\", \"Downloads\", \"FileWatching\", \"LibGit2\", \"Libdl\", \"Logging\", \"Markdown\", \"Printf\", \"REPL\", \"Random\", \"SHA\", \"Serialization\", \"TOML\", \"Tar\", \"UUIDs\", \"p7zip_jll\"]\nuuid = \"44cfe95a-1eb2-52ea-b672-e2afdf69b78f\"\nversion = \"1.9.2\"\n\n[[deps.PkgLicenses]]\ndeps = [\"Test\"]\ngit-tree-sha1 = \"0af826be249c6751a3e783c07b8cd3034f508943\"\nuuid = \"fc669557-7ec9-5e45-bca9-462afbc28879\"\nversion = \"0.2.0\"\n\n[[deps.PrecompileTools]]\ndeps = [\"Preferences\"]\ngit-tree-sha1 = \"03b4c25b43cb84cee5c90aa9b5ea0a78fd848d2f\"\nuuid = \"aea7be01-6a6a-4083-8856-8a6e6704d82a\"\nversion = \"1.2.0\"\n\n[[deps.Preferences]]\ndeps = [\"TOML\"]\ngit-tree-sha1 = \"00805cd429dcb4870060ff49ef443486c262e38e\"\nuuid = \"21216c6a-2e73-6563-6e65-726566657250\"\nversion = \"1.4.1\"\n\n[[deps.Printf]]\ndeps = [\"Unicode\"]\nuuid = \"de0858da-6303-5e67-8744-51eddeeeb8d7\"\n\n[[deps.ProgressMeter]]\ndeps = [\"Distributed\", \"Printf\"]\ngit-tree-sha1 = \"00099623ffee15972c16111bcf84c58a0051257c\"\nuuid = \"92933f4c-e287-5a05-a399-4b506db050ca\"\nversion = \"1.9.0\"\n\n[[deps.REPL]]\ndeps = [\"InteractiveUtils\", \"Markdown\", \"Sockets\", \"Unicode\"]\nuuid = \"3fa0cd96-eef1-5676-8a61-b3b8758bbffb\"\n\n[[deps.Random]]\ndeps = [\"SHA\", \"Serialization\"]\nuuid = \"9a3f8284-a2c9-5f02-9a11-845980a1fd5c\"\n\n[[deps.Reexport]]\ngit-tree-sha1 = \"45e428421666073eab6f2da5c9d310d99bb12f9b\"\nuuid = \"189a3867-3050-52da-a836-e630ba90ab69\"\nversion = \"1.2.2\"\n\n[[deps.Registrator]]\ndeps = [\"AutoHashEquals\", \"Base64\", \"Dates\", \"Distributed\", \"FileWatching\", \"GitForge\", \"GitHub\", \"HTTP\", \"JSON\", \"LibGit2\", \"Logging\", \"MbedTLS\", \"Mocking\", \"Mustache\", \"Mux\", \"Pkg\", \"RegistryTools\", \"Serialization\", \"Sockets\", \"TimeToLive\", \"URIs\", \"UUIDs\", \"ZMQ\"]\ngit-tree-sha1 = \"64a7d49e56cf609973854cdd48eb265ac418f6ee\"\nuuid = \"4418983a-e44d-11e8-3aec-9789530b3b3e\"\nversion = \"1.6.0\"\n\n[[deps.RegistryInstances]]\ndeps = [\"LazilyInitializedFields\", \"Pkg\", \"TOML\", \"Tar\"]\ngit-tree-sha1 = \"ffd19052caf598b8653b99404058fce14828be51\"\nuuid = \"2792f1a3-b283-48e8-9a74-f99dce5104f3\"\nversion = \"0.1.0\"\n\n[[deps.RegistryTools]]\ndeps = [\"AutoHashEquals\", \"LibGit2\", \"Pkg\", \"SHA\", \"UUIDs\"]\ngit-tree-sha1 = \"47ab54eff26db6be2496e6300d959e16d8203723\"\nuuid = \"d1eb7eb1-105f-429d-abf5-b0f65cb9e2c4\"\nversion = \"1.9.1\"\n\n[[deps.Requires]]\ndeps = [\"UUIDs\"]\ngit-tree-sha1 = \"838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7\"\nuuid = \"ae029012-a4dd-5104-9daa-d747884805df\"\nversion = \"1.3.0\"\n\n[[deps.SHA]]\nuuid = \"ea8e919c-243c-51af-8825-aaa63cd721ce\"\nversion = \"0.7.0\"\n\n[[deps.Scratch]]\ndeps = [\"Dates\"]\ngit-tree-sha1 = \"30449ee12237627992a99d5e30ae63e4d78cd24a\"\nuuid = \"6c6a2e73-6563-6170-7368-637461726353\"\nversion = \"1.2.0\"\n\n[[deps.Serialization]]\nuuid = \"9e88b42a-f829-5b0c-bbe9-9e923198166b\"\n\n[[deps.SimpleBufferStream]]\ngit-tree-sha1 = \"874e8867b33a00e784c8a7e4b60afe9e037b74e1\"\nuuid = \"777ac1f9-54b0-4bf8-805c-2214025038e7\"\nversion = \"1.1.0\"\n\n[[deps.Sockets]]\nuuid = \"6462fe0b-24de-5631-8697-dd941f90decc\"\n\n[[deps.SodiumSeal]]\ndeps = [\"Base64\", \"Libdl\", \"libsodium_jll\"]\ngit-tree-sha1 = \"80cef67d2953e33935b41c6ab0a178b9987b1c99\"\nuuid = \"2133526b-2bfb-4018-ac12-889fb3908a75\"\nversion = \"0.1.1\"\n\n[[deps.StructIO]]\ndeps = [\"Test\"]\ngit-tree-sha1 = \"010dc73c7146869c042b49adcdb6bf528c12e859\"\nuuid = \"53d494c1-5632-5724-8f4c-31dff12d585f\"\nversion = \"0.3.0\"\n\n[[deps.StructTypes]]\ndeps = [\"Dates\", \"UUIDs\"]\ngit-tree-sha1 = \"ca4bccb03acf9faaf4137a9abc1881ed1841aa70\"\nuuid = \"856f2bd8-1eba-4b0a-8007-ebc267875bd4\"\nversion = \"1.10.0\"\n\n[[deps.TOML]]\ndeps = [\"Dates\"]\nuuid = \"fa267f1f-6049-4f14-aa54-33bafae1ed76\"\nversion = \"1.0.3\"\n\n[[deps.TZJData]]\ndeps = [\"Artifacts\"]\ngit-tree-sha1 = \"d39314cdbaf5b90a047db33858626f8d1cc973e1\"\nuuid = \"dc5dba14-91b3-4cab-a142-028a31da12f7\"\nversion = \"1.0.0+2023c\"\n\n[[deps.TableTraits]]\ndeps = [\"IteratorInterfaceExtensions\"]\ngit-tree-sha1 = \"c06b2f539df1c6efa794486abfb6ed2022561a39\"\nuuid = \"3783bdb8-4a98-5b6b-af9a-565f29a5fe9c\"\nversion = \"1.0.1\"\n\n[[deps.Tables]]\ndeps = [\"DataAPI\", \"DataValueInterfaces\", \"IteratorInterfaceExtensions\", \"LinearAlgebra\", \"OrderedCollections\", \"TableTraits\"]\ngit-tree-sha1 = \"a1f34829d5ac0ef499f6d84428bd6b4c71f02ead\"\nuuid = \"bd369af6-aec1-5ad0-b16a-f7cc5008161c\"\nversion = \"1.11.0\"\n\n[[deps.Tar]]\ndeps = [\"ArgTools\", \"SHA\"]\nuuid = \"a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e\"\nversion = \"1.10.0\"\n\n[[deps.Tar_jll]]\ndeps = [\"Artifacts\", \"Attr_jll\", \"JLLWrappers\", \"Libdl\", \"Libiconv_jll\"]\ngit-tree-sha1 = \"85e7d0ef5248971fbd824f29c52ab6168b895dfd\"\nuuid = \"9b64493d-8859-5bf3-93d7-7c32dd38186f\"\nversion = \"1.35.0+0\"\n\n[[deps.Test]]\ndeps = [\"InteractiveUtils\", \"Logging\", \"Random\", \"Serialization\"]\nuuid = \"8dfed614-e22c-5e08-85e1-65c5234f0b40\"\n\n[[deps.TextWrap]]\ngit-tree-sha1 = \"9250ef9b01b66667380cf3275b3f7488d0e25faf\"\nuuid = \"b718987f-49a8-5099-9789-dcd902bef87d\"\nversion = \"1.0.1\"\n\n[[deps.TimeToLive]]\ndeps = [\"Dates\"]\ngit-tree-sha1 = \"1f1389007d16385ec02e497bef6c2caffba99b65\"\nuuid = \"37f0c46e-897f-50ef-b453-b26c3eed3d6c\"\nversion = \"0.3.0\"\n\n[[deps.TimeZones]]\ndeps = [\"Artifacts\", \"Dates\", \"Downloads\", \"InlineStrings\", \"LazyArtifacts\", \"Mocking\", \"Printf\", \"Scratch\", \"TZJData\", \"Unicode\", \"p7zip_jll\"]\ngit-tree-sha1 = \"89e64d61ef3cd9e80f7fc12b7d13db2d75a23c03\"\nuuid = \"f269a46b-ccf7-5d73-abea-4c690281aa53\"\nversion = \"1.13.0\"\n\n    [deps.TimeZones.extensions]\n    TimeZonesRecipesBaseExt = \"RecipesBase\"\n\n    [deps.TimeZones.weakdeps]\n    RecipesBase = \"3cdcf5f2-1ef4-517c-9805-6587b60abb01\"\n\n[[deps.TranscodingStreams]]\ndeps = [\"Random\", \"Test\"]\ngit-tree-sha1 = \"9a6ae7ed916312b41236fcef7e0af564ef934769\"\nuuid = \"3bb67fe8-82b1-5028-8e26-92a6c54297fa\"\nversion = \"0.9.13\"\n\n[[deps.URIs]]\ngit-tree-sha1 = \"b7a5e99f24892b6824a954199a45e9ffcc1c70f0\"\nuuid = \"5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4\"\nversion = \"1.5.0\"\n\n[[deps.UUIDs]]\ndeps = [\"Random\", \"SHA\"]\nuuid = \"cf7118a7-6976-5b1a-9a39-7adc72f591a4\"\n\n[[deps.Unicode]]\nuuid = \"4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5\"\n\n[[deps.Wayland_jll]]\ndeps = [\"Artifacts\", \"EpollShim_jll\", \"Expat_jll\", \"JLLWrappers\", \"Libdl\", \"Libffi_jll\", \"Pkg\", \"XML2_jll\"]\ngit-tree-sha1 = \"7558e29847e99bc3f04d6569e82d0f5c54460703\"\nuuid = \"a2964d1f-97da-50d4-b82a-358c7fce9d89\"\nversion = \"1.21.0+1\"\n\n[[deps.Wayland_protocols_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\", \"Pkg\"]\ngit-tree-sha1 = \"4528479aa01ee1b3b4cd0e6faef0e04cf16466da\"\nuuid = \"2381bf8a-dfd0-557d-9999-79630e7b1b91\"\nversion = \"1.25.0+0\"\n\n[[deps.XML2_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\", \"Libiconv_jll\", \"Zlib_jll\"]\ngit-tree-sha1 = \"24b81b59bd35b3c42ab84fa589086e19be919916\"\nuuid = \"02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a\"\nversion = \"2.11.5+0\"\n\n[[deps.XSLT_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\", \"Libgcrypt_jll\", \"Libgpg_error_jll\", \"Libiconv_jll\", \"Pkg\", \"XML2_jll\", \"Zlib_jll\"]\ngit-tree-sha1 = \"91844873c4085240b95e795f692c4cec4d805f8a\"\nuuid = \"aed1982a-8fda-507f-9586-7b0439959a61\"\nversion = \"1.1.34+0\"\n\n[[deps.XZ_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\"]\ngit-tree-sha1 = \"cf2c7de82431ca6f39250d2fc4aacd0daa1675c0\"\nuuid = \"ffd25f8a-64ca-5728-b0f7-c24cf3aae800\"\nversion = \"5.4.4+0\"\n\n[[deps.Xorg_libX11_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\", \"Xorg_libxcb_jll\", \"Xorg_xtrans_jll\"]\ngit-tree-sha1 = \"afead5aba5aa507ad5a3bf01f58f82c8d1403495\"\nuuid = \"4f6342f7-b3d2-589e-9d20-edeb45f2b2bc\"\nversion = \"1.8.6+0\"\n\n[[deps.Xorg_libXau_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\"]\ngit-tree-sha1 = \"6035850dcc70518ca32f012e46015b9beeda49d8\"\nuuid = \"0c0b7dd1-d40b-584c-a123-a41640f87eec\"\nversion = \"1.0.11+0\"\n\n[[deps.Xorg_libXcursor_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\", \"Pkg\", \"Xorg_libXfixes_jll\", \"Xorg_libXrender_jll\"]\ngit-tree-sha1 = \"12e0eb3bc634fa2080c1c37fccf56f7c22989afd\"\nuuid = \"935fb764-8cf2-53bf-bb30-45bb1f8bf724\"\nversion = \"1.2.0+4\"\n\n[[deps.Xorg_libXdamage_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\", \"Pkg\", \"Xorg_libXfixes_jll\"]\ngit-tree-sha1 = \"fe4ffb2024ba3eddc862c6e1d70e2b070cd1c2bf\"\nuuid = \"0aeada51-83db-5f97-b67e-184615cfc6f6\"\nversion = \"1.1.5+4\"\n\n[[deps.Xorg_libXdmcp_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\"]\ngit-tree-sha1 = \"34d526d318358a859d7de23da945578e8e8727b7\"\nuuid = \"a3789734-cfe1-5b06-b2d0-1dd0d9d62d05\"\nversion = \"1.1.4+0\"\n\n[[deps.Xorg_libXext_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\", \"Pkg\", \"Xorg_libX11_jll\"]\ngit-tree-sha1 = \"b7c0aa8c376b31e4852b360222848637f481f8c3\"\nuuid = \"1082639a-0dae-5f34-9b06-72781eeb8cb3\"\nversion = \"1.3.4+4\"\n\n[[deps.Xorg_libXfixes_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\", \"Pkg\", \"Xorg_libX11_jll\"]\ngit-tree-sha1 = \"0e0dc7431e7a0587559f9294aeec269471c991a4\"\nuuid = \"d091e8ba-531a-589c-9de9-94069b037ed8\"\nversion = \"5.0.3+4\"\n\n[[deps.Xorg_libXi_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\", \"Pkg\", \"Xorg_libXext_jll\", \"Xorg_libXfixes_jll\"]\ngit-tree-sha1 = \"89b52bc2160aadc84d707093930ef0bffa641246\"\nuuid = \"a51aa0fd-4e3c-5386-b890-e753decda492\"\nversion = \"1.7.10+4\"\n\n[[deps.Xorg_libXinerama_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\", \"Pkg\", \"Xorg_libXext_jll\"]\ngit-tree-sha1 = \"26be8b1c342929259317d8b9f7b53bf2bb73b123\"\nuuid = \"d1454406-59df-5ea1-beac-c340f2130bc3\"\nversion = \"1.1.4+4\"\n\n[[deps.Xorg_libXrandr_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\", \"Pkg\", \"Xorg_libXext_jll\", \"Xorg_libXrender_jll\"]\ngit-tree-sha1 = \"34cea83cb726fb58f325887bf0612c6b3fb17631\"\nuuid = \"ec84b674-ba8e-5d96-8ba1-2a689ba10484\"\nversion = \"1.5.2+4\"\n\n[[deps.Xorg_libXrender_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\", \"Pkg\", \"Xorg_libX11_jll\"]\ngit-tree-sha1 = \"19560f30fd49f4d4efbe7002a1037f8c43d43b96\"\nuuid = \"ea2f1a96-1ddc-540d-b46f-429655e07cfa\"\nversion = \"0.9.10+4\"\n\n[[deps.Xorg_libpthread_stubs_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\"]\ngit-tree-sha1 = \"8fdda4c692503d44d04a0603d9ac0982054635f9\"\nuuid = \"14d82f49-176c-5ed1-bb49-ad3f5cbd8c74\"\nversion = \"0.1.1+0\"\n\n[[deps.Xorg_libxcb_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\", \"XSLT_jll\", \"Xorg_libXau_jll\", \"Xorg_libXdmcp_jll\", \"Xorg_libpthread_stubs_jll\"]\ngit-tree-sha1 = \"b4bfde5d5b652e22b9c790ad00af08b6d042b97d\"\nuuid = \"c7cfdc94-dc32-55de-ac96-5a1b8d977c5b\"\nversion = \"1.15.0+0\"\n\n[[deps.Xorg_libxkbfile_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\", \"Xorg_libX11_jll\"]\ngit-tree-sha1 = \"730eeca102434283c50ccf7d1ecdadf521a765a4\"\nuuid = \"cc61e674-0454-545c-8b26-ed2c68acab7a\"\nversion = \"1.1.2+0\"\n\n[[deps.Xorg_xkbcomp_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\", \"Xorg_libxkbfile_jll\"]\ngit-tree-sha1 = \"330f955bc41bb8f5270a369c473fc4a5a4e4d3cb\"\nuuid = \"35661453-b289-5fab-8a00-3d9160c6a3a4\"\nversion = \"1.4.6+0\"\n\n[[deps.Xorg_xkeyboard_config_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\", \"Xorg_xkbcomp_jll\"]\ngit-tree-sha1 = \"691634e5453ad362044e2ad653e79f3ee3bb98c3\"\nuuid = \"33bec58e-1273-512f-9401-5d533626f822\"\nversion = \"2.39.0+0\"\n\n[[deps.Xorg_xtrans_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\"]\ngit-tree-sha1 = \"e92a1a012a10506618f10b7047e478403a046c77\"\nuuid = \"c5fb5394-a638-5e4d-96e5-b29de1b5cf10\"\nversion = \"1.5.0+0\"\n\n[[deps.ZMQ]]\ndeps = [\"FileWatching\", \"Sockets\", \"ZeroMQ_jll\"]\ngit-tree-sha1 = \"356d2bdcc0bce90aabee1d1c0f6d6f301eda8f77\"\nuuid = \"c2297ded-f4af-51ae-bb23-16f91089e4e1\"\nversion = \"1.2.2\"\n\n[[deps.ZeroMQ_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\", \"Pkg\", \"libsodium_jll\"]\ngit-tree-sha1 = \"fe5c65a526f066fb3000da137d5785d9649a8a47\"\nuuid = \"8f1865be-045e-5c20-9c9f-bfbfb0764568\"\nversion = \"4.3.4+0\"\n\n[[deps.Zlib_jll]]\ndeps = [\"Libdl\"]\nuuid = \"83775a58-1f1d-513f-b197-d71354ab007a\"\nversion = \"1.2.13+0\"\n\n[[deps.Zstd_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\"]\ngit-tree-sha1 = \"49ce682769cd5de6c72dcf1b94ed7790cd08974c\"\nuuid = \"3161d3a3-bdf6-5164-811a-617609db77b4\"\nversion = \"1.5.5+0\"\n\n[[deps.gdk_pixbuf_jll]]\ndeps = [\"Artifacts\", \"Glib_jll\", \"JLLWrappers\", \"JpegTurbo_jll\", \"Libdl\", \"Libtiff_jll\", \"Pkg\", \"Xorg_libX11_jll\", \"libpng_jll\"]\ngit-tree-sha1 = \"e9190f9fb03f9c3b15b9fb0c380b0d57a3c8ea39\"\nuuid = \"da03df04-f53b-5353-a52f-6a8b0620ced0\"\nversion = \"2.42.8+0\"\n\n[[deps.ghr_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\", \"Pkg\"]\ngit-tree-sha1 = \"a83b3feeda837dd3f3cad19076bda0f0a524d687\"\nuuid = \"07c12ed4-43bc-5495-8a2a-d5838ef8d533\"\nversion = \"0.14.0+0\"\n\n[[deps.iso_codes_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\", \"Pkg\"]\ngit-tree-sha1 = \"51559b9071db7e363047a34f658d495843ccd35c\"\nuuid = \"bf975903-5238-5d20-8243-bc370bc1e7e5\"\nversion = \"4.11.0+0\"\n\n[[deps.libadwaita_jll]]\ndeps = [\"Artifacts\", \"GTK4_jll\", \"JLLWrappers\", \"Libdl\", \"Pkg\"]\ngit-tree-sha1 = \"51352620ef59bc200289a398fbc65c587f0034d7\"\nuuid = \"583852a3-1c13-5035-b52b-3b742a7b3316\"\nversion = \"1.2.0+0\"\n\n[[deps.libblastrampoline_jll]]\ndeps = [\"Artifacts\", \"Libdl\"]\nuuid = \"8e850b90-86db-534c-a0d3-1478176c7d93\"\nversion = \"5.8.0+0\"\n\n[[deps.libcxxwrap_julia_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\"]\ngit-tree-sha1 = \"d2058c04963d424ae69f95460fc34fdfce4dc0cf\"\nuuid = \"3eaa8342-bff7-56a5-9981-c04077f7cee7\"\nversion = \"0.11.0+1\"\n\n[[deps.libpng_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\", \"Pkg\", \"Zlib_jll\"]\ngit-tree-sha1 = \"94d180a6d2b5e55e447e2d27a29ed04fe79eb30c\"\nuuid = \"b53b4c65-9356-5827-b1ea-8c7a1a84506f\"\nversion = \"1.6.38+0\"\n\n[[deps.libsodium_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\", \"Pkg\"]\ngit-tree-sha1 = \"848ab3d00fe39d6fbc2a8641048f8f272af1c51e\"\nuuid = \"a9144af2-ca23-56d9-984f-0d03f7b5ccf8\"\nversion = \"1.0.20+0\"\n\n[[deps.mousetrap_jll]]\ndeps = [\"Artifacts\", \"GLEW_jll\", \"GLU_jll\", \"GTK4_jll\", \"JLLWrappers\", \"Libdl\", \"OpenGLMathematics_jll\", \"libadwaita_jll\", \"libcxxwrap_julia_jll\"]\ngit-tree-sha1 = \"6e5d87111686530feb339af45210317f56c27a82\"\nuuid = \"0e90efc8-2bbd-550f-bf3c-306a2edaaeef\"\nversion = \"0.3.0+0\"\n\n[[deps.nghttp2_jll]]\ndeps = [\"Artifacts\", \"Libdl\"]\nuuid = \"8e850ede-7688-5339-a07c-302acd2aaf8d\"\nversion = \"1.48.0+0\"\n\n[[deps.p7zip_jll]]\ndeps = [\"Artifacts\", \"Libdl\"]\nuuid = \"3f19e933-33d8-53b3-aaab-bd5110c3b7a0\"\nversion = \"17.4.0+0\"\n\n[[deps.pigz_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\", \"Pkg\", \"Zlib_jll\"]\ngit-tree-sha1 = \"3c0c0b0c133b6ab53e1af05dc526091ce8781f16\"\nuuid = \"1bc43ea1-30af-5bc8-a9d4-c018457e6e3e\"\nversion = \"2.7.0+0\"\n\n[[deps.xkbcommon_jll]]\ndeps = [\"Artifacts\", \"JLLWrappers\", \"Libdl\", \"Pkg\", \"Wayland_jll\", \"Wayland_protocols_jll\", \"Xorg_libxcb_jll\", \"Xorg_xkeyboard_config_jll\"]\ngit-tree-sha1 = \"9c304562909ab2bab0262639bd4f444d7bc2be37\"\nuuid = \"d8fb68d0-12a3-5cfd-a85a-d49703b185fd\"\nversion = \"1.4.1+1\"\n"
  },
  {
    "path": "Project.toml",
    "content": "name = \"Mousetrap\"\nuuid = \"5deeb4b9-6e04-4da7-8b7f-c77fb1eae65e\"\nauthors = [\"C.Cords <mail@clemens-cords.com>\"]\nversion = \"0.3.1\"\n\n[deps]\nBinaryBuilder = \"12aac903-9f7c-5d81-afc2-d9565ea332ae\"\nCxxWrap = \"1f15a43c-97ca-5a2a-ae31-89f07a497df4\"\nDocumenter = \"e30172f5-a6a5-5a46-863b-614d45cd2de4\"\nGTK4_jll = \"6ebb71f1-8434-552f-b6b1-dc18babcca63\"\nGlib_jll = \"7746bdde-850d-59dc-9ae8-88ece973131d\"\nInteractiveUtils = \"b77e0a4c-d291-57a0-90e8-8db25a27a240\"\nLatexify = \"23fbe1c1-3f47-55db-b15f-69d7ec21a316\"\nPkg = \"44cfe95a-1eb2-52ea-b672-e2afdf69b78f\"\nTest = \"8dfed614-e22c-5e08-85e1-65c5234f0b40\"\nlibadwaita_jll = \"583852a3-1c13-5035-b52b-3b742a7b3316\"\nlibcxxwrap_julia_jll = \"3eaa8342-bff7-56a5-9981-c04077f7cee7\"\nmousetrap_jll = \"0e90efc8-2bbd-550f-bf3c-306a2edaaeef\"\n\n[compat]\nCxxWrap = \"0.14.0\"\nmousetrap_jll = \"0.3.0\"\nGTK4_jll = \"4.10\"\nGlib_jll = \"2.76\""
  },
  {
    "path": "README.md",
    "content": "# Mousetrap\n\n![](docs/src/assets/banner.png)\n\nMousetrap is a GUI library designed for Julia. It fully wraps [GTK4](https://docs.gtk.org/gtk4/) (which is written in C), *vastly* simplifying its interface to improve ease-of-use without sacrificing flexibility.\n\nIt aims to give developers of all skill levels the tools to start creating complex GUI applications with little time and effort, while taking full advantage of Julias idiosyncrasies.\n\n> **Note**: Mousetrap is under active development. Consider participating in the development by [opening an issue](https://github.com/clemapfel/mousetrap.jl) when you encounter an error, bug, question, or missing feature.\n\n> **Note**: February 8th, 2024: I've been having some health issues at the moment, it may take me some time to get to open issues. Mousetrap is still usable and (mostly) stable, the repo will be maintained in the immediate future to the best of my abilities. Thank you for your understanding, C.\n---\n\n## Table of Contents\n0. [Introduction](https://github.com/Clemapfel/mousetrap.jl)<br>\n1. [Features](#features)<br>\n2. [Planned Features](#planned-features)<br>\n3. [Showcase](#showcase)<br>\n3.1 [Hello World](#hello-world)<br>\n3.2 [Swapping between Light- and Dark Themes](#swapping-between-light--and-dark-themes)<br>\n3.3 [Opening a File Explorer Dialog](#opening-a-file-explorer-dialog)<br>\n3.4 [Rendering a Rectangle using OpenGL](#rendering-a-rectangle-with-opengl)<br>\n3.5 [Displaying a GLMakie Plot in a Mousetrap Window](#displaying-a-glmakie-plot-in-a-mousetrap-window)<br>\n4. [Supported Platforms](#supported-platforms)<br>\n6. [Documentation](#documentation)<br>\n5. [Installation](#installation)<br>\n7. [Credits & Donations](#credits--donations)<br>\n8. [License](#license)<br>\n\n---\n\n## Features\n+ Create complex GUI application for Linux, Windows, and macOS\n+ Choose from over 40 different kinds of pre-made widgets, or create your own\n+ Supports mice, keyboards, touchscreens, touchpads, and stylus devices\n+ Image processing facilities, well-suited for image manipulation programs\n+ Built using OpenGL, allowing for high-performance, hardware-accelerated rendering, and integration of other OpenGL-based libraries such as [GLMakie](https://github.com/MakieOrg/Makie.jl)\n+ [Hand-written manual and extensive documentation](https://clemens-cords.com/mousetrap): every exported symbol is documented\n\n---\n\n## Planned Features\n\nIn order of priority, highest first:\n\n+ Allow bundling of Mousetrap apps using [`PackageCompiler.jl`](https://github.com/JuliaLang/PackageCompiler.jl)\n+ Implement installation of .desktop files on end-user computers\n+ Implement drag-and-drop for files, images, and widgets\n+ Allow filtering and searching of selectable widget containers such as `ListView` and `ColumnView`\n+ Allow adding custom signals that use the GLib marshalling system\n+ Make all functions that modify the global state thread-safe\n\n---\n\n## Showcase\n\n### Hello World\n\n```julia\nusing Mousetrap\nmain() do app::Application\n    window = Window(app)\n    set_child!(window, Label(\"Hello World!\"))\n    present!(window)\nend\n```\n![](docs/src/assets/readme_hello_world.png)\n\n---\n\n### Swapping between Light- and Dark Themes\n\n```julia\nset_current_theme!(app, THEME_DEFAULT_LIGHT) \n```\n![](docs/src/assets/light_dark_theme.png)\n\n---\n\n### Opening a File Explorer Dialog\n\n```julia\nfile_chooser = FileChooser()\non_accept!(file_chooser) do self::FileChooser, files\n    println(\"selected files: $files\")\nend\npresent!(file_chooser)\n```\n![](docs/src/assets/readme_file_chooser.png)\n\n---\n\n### Rendering a Rectangle with OpenGL\n\n```julia\nrender_area = RenderArea()\nrectangle = Rectangle(Vector2f(-0.5, 0.5), Vector2f(1, 1))\nadd_render_task!(render_area, RenderTask(rectangle))\n```\n![](docs/src/assets/readme_opengl_rectangle.png)\n\n---\n\n### Displaying a GLMakie Plot in a Mousetrap Window\n\n```julia\nusing GLMakie, MousetrapMakie\ncanvas = GLMakieArea()\nwindow = Mousetrap.Window()\nset_child!(window, canvas) # can be used like any other widget\n\nscreen = create_glmakie_screen(canvas)\ndisplay(screen, scatter(rand(123)))\n```\n![](docs/src/assets/makie_scatter.png)\n\n(**Note**: This feature is still experimental. See [here](https://github.com/Clemapfel/MousetrapMakie.jl) for more information)\n\n---\n\n## Supported Platforms\n\nSince `v0.3.0`, Mousetrap is fully portable. All features are available for all 64-bit versions of Linux, FreeBSD, macOS, and Windows.\n\n> **Note**: Linux systems running Wayland may require additional configuration before the `RenderArea` widget becomes available. See [here](http://clemens-cords.com/mousetrap/01_manual/09_native_rendering/) for more information.\n\n> **Note**: Ubuntu systems using proprietary NVIDIA drivers may encounter a crash on initialization, a fix is available [here](https://github.com/Clemapfel/Mousetrap.jl/issues/25#issuecomment-1731349366).\n\n---\n\n## Documentation\n\nDocumentation is available [here](https://clemens-cords.com/mousetrap). This includes a tutorial on how to get started using Mousetrap, a manual introducing users to Mousetrap and GUI programming in general, as well as an index of all classes, enums, and functions.\n\n---\n\n## Installation\n\nIn the Julia REPL, execute:\n\n```julia\nimport Pkg;\nbegin\n    Pkg.add(url=\"https://github.com/clemapfel/mousetrap.jl\")\n    Pkg.test(\"Mousetrap\")\nend\n```\n\nAt the end, it should say `Mousetrap tests passed`.\n\n> **Note**: On Windows, some `GLib` log messages regarding dbus connections may appear during testing. These do not indicate a problem.\n\n> **Note**: On Linux Wayland, a warning regarding EGL displays may appear during installation. See the [here](http://clemens-cords.com/mousetrap/01_manual/09_native_rendering/) for how to fix this issue.\n\n---\n\nIf you have had Mousetrap version 0.3.0 or earlier installed on your device before, run the following before installing the current version of Mousetrap:\n\n```julia\nimport Pkg\nbegin\n    try Pkg.rm(\"mousetrap\") catch end\n    try Pkg.rm(\"mousetrap_windows_jll\") catch end\n    try Pkg.rm(\"mousetrap_linux_jll\") catch end\n    try Pkg.rm(\"mousetrap_apple_jll\") catch end\n    try Pkg.rm(\"libmousetrap_jll\") catch end\n    Pkg.gc()\nend\n```\n\nThis will remove any trace of older versions that may cause conflicts.\n\n--- \n\n## Credits & Donations\n\nMousetrap was designed and implemented by [C.Cords](https://clemens-cords.com).\n\nIt was created with no expectation of compensation and made available for free. Consider **donating** to reward past work and support the continued development of this library:\n+ [GitHub Sponsors](https://github.com/sponsors/Clemapfel)\n+ [PayPal](https://www.paypal.com/donate/?hosted_button_id=8KWF3JTDF8XL2)\n\nThe goal is for Mousetrap to be 100% stable and flawless when Julia [static compilation](https://github.com/JuliaLang/PackageCompiler.jl) finishes development. Static compilation and the lack of fully featured, easy-to-use, GUI libraries are currently the largest factors as to why Julia is ill-suited for front-end development. Mousetrap aims to address this.\n\n---\n\n## License\n\nThe current and all past version of Mousetrap, including any text or assets used in Mousetraps documentation, are licensed under [GNU Lesser General Public License (Version 3.0)](https://www.gnu.org/licenses/lgpl-3.0.en.html). This means it can be used in both free, open-source, as well as commercial, closed-source software.\n\n"
  },
  {
    "path": "docs/make.jl",
    "content": "#\n# Author: C. Cords (mail@clemens-cords.com)\n# https://github.com/clemapfel/mousetrap.jl\n#\n# Copyright © 2023, Licensed under lGPL3-0\n#\n\nusing Documenter, Pkg, InteractiveUtils\nusing Mousetrap\n\nlet file = open(\"docs/src/02_library/classes.md\", \"w+\")\n    @info \"Mousetrap: Exporting Index...\"\n    write(file, \"# Index: Classes\\n\")\n\n    for name in sort(union(\n        Mousetrap.types, \n        Mousetrap.signal_emitters, \n        Mousetrap.widgets, \n        Mousetrap.event_controllers, \n        Mousetrap.abstract_types,\n    ))\n        if name in [\n            :Vector2f, :Vector3f, :Vector4f,\n            :Vector2i, :Vector3i, :Vector4i,\n            :Vector2ui, :Vector3ui, :Vector4ui\n        ]\n            continue\n        end\n\n        if occursin(\"STYLE_CLASS\", string(name))\n            continue\n        end\n        \n        out = \"## $name\\n\"\n        out *= \"```@docs\\n\"\n        out *= \"$name\\n\"\n        out *= \"```\\n\"\n        \n        binding = getproperty(Mousetrap, name)\n        already_seen = Set{Symbol}()\n\n        once = true\n\n        signal_methods = []\n        non_signal_methods = []\n\n        for method in methodswith(binding, Mousetrap) \n            if isnothing(match(r\".*_signal_.*\", string(method.name)))\n                # first or second argument is type, this is the equivalent of a member function in Julia\n                try\n                    if hasproperty(method.sig, :parameters) && (method.sig.parameters[2] == binding || method.sig.parameters[3] == binding)\n                        if method.name in [:copy!, :flush, :bind, :download]\n                            push!(non_signal_methods, Symbol(\"Mousetrap.\" * string(method.name)))\n                        else\n                            push!(non_signal_methods, method.name)\n                        end\n                    end\n                catch e\n                end\n            else\n                push!(signal_methods, method.name)\n            end\n        end\n\n        # sort by signal, as opposed to alphabetically\n\n        signals = Set{String}()\n        for method in signal_methods\n            m = match(r\".*_signal_(.*)_\", string(method))\n            if !isnothing(m)\n                push!(signals, m.captures[1])\n            end\n        end\n\n        signal_methods_sorted = []\n        for signal in sort([signals...])\n            for method in signal_methods\n                if occursin(\"_\" * signal, string(method))\n                    push!(signal_methods_sorted, method)\n                end\n            end\n            push!(signal_methods_sorted, Symbol(\"\"))\n        end\n\n        if length(non_signal_methods) + length(signal_methods) > 0\n            for m in [non_signal_methods..., Symbol(\"\"), signal_methods_sorted...]\n\n                if once\n                    out *= \"#### Functions that operate on this type:\\n\"\n                    once = false\n                end\n\n                as_string = string(m)\n                if isempty(as_string)\n                    out *= \"\\n\\n\"\n                    continue\n                end\n\n                seen = m in already_seen\n                push!(already_seen, m)\n\n                try \n                if seen\n                    continue\n                elseif binding === String # skip ID typedefs\n                    continue\n                elseif getproperty(Mousetrap, m) isa Type # omit ctors\n                    continue\n                end\n                catch end # to deal with copy!, flush, etc.\n\n                out *= \"+ [`$m`](@ref)\\n\"\n            end\n        end\n\n        out *= \"---\\n\"\n        write(file, out)\n    end\n\n    close(file)\n\n    file = open(\"docs/src/02_library/enums.md\", \"w+\")\n    write(file, \"# Index: Enums\\n\")\n    for enum_name in Mousetrap.enums\n        write(file, \"## $enum_name\\n\")\n        write(file, \"```@docs\\n\")\n        write(file, \"$enum_name\\n\")\n        enum = getproperty(Mousetrap, enum_name)\n        values = []\n        for value_name in Mousetrap.enum_values\n            if typeof(getproperty(Mousetrap, value_name)) <: enum\n                write(file, \"$value_name\\n\")\n            end\n        end\n\n        write(file, \"```\\n\")\n        write(file, \"---\\n\")\n    end\n    close(file)\n\n    file = open(\"docs/src/02_library/functions.md\", \"w+\")\n\n    write(file, \"# Index: Functions\\n\")\n    \n    for f in Mousetrap.functions\n        write(file, \"## `$f`\\n\")\n        write(file, \"```@docs\\n\")\n        write(file, \"Mousetrap.$f\\n\")\n        write(file, \"```\\n\")\n    end\n    close(file)\nend \n\nmakedocs(\n    sitename=\"Mousetrap\", \n    format = Documenter.HTML(\n        size_threshold_warn = nothing,\n        size_threshold = Integer(2e+6)\n    )\n)\n"
  },
  {
    "path": "docs/src/01_manual/01_installation.md",
    "content": "# Chapter 1: Installation & Workflow\n\nIn this chapter, we will learn:\n+ How to install Mousetrap.jl\n+ How to create our first Mousetrap application\n+ Basic Julia skills that are needed to understand the rest of this manual\n\n---\n\n## Installation\n\n\nTo install Mousetrap, in the REPL, press the `]` key, then\n\n\n```julia\nimport Pkg;\nbegin\n    Pkg.add(url=\"https://github.com/clemapfel/mousetrap.jl\")\n    Pkg.test(\"Mousetrap\")\nend\n```\n\nInstallation may take a long time. Once installation is succesfull, `Mousetrap tests passed` will be printed.\n\n!!! compat \"Removing older versions\"\n    Mousetraps installation procedure has changed starting with `v0.3.0`. If older versions of Mousetrap are installed on our computer, we should make sure to delete any trace of the older versions by executing the following, before running `add Mousetrap`:\n    ```julia\n    import Pkg\n    begin\n        try Pkg.rm(\"mousetrap\") catch end\n        try Pkg.rm(\"mousetrap_windows_jll\") catch end\n        try Pkg.rm(\"mousetrap_linux_jll\") catch end\n        try Pkg.rm(\"mousetrap_apple_jll\") catch end\n        try Pkg.rm(\"libmousetrap_jll\") catch end\n    end\n    ```\n  \n\n## Hello World\n\nTo create our first Mousetrap app, we create a Julia file `main.jl`, with the following contents:\n\n```julia\nusing Mousetrap\nmain() do app::Application\n    window = Window(app)\n    set_child!(window, Label(\"Hello World!\"))\n    present!(window)\nend\n```\n\nTo start our app, we navigate to the location of `main.jl` in our console, then execute:\n\n```shell\n# in same directory as `main.jl`\njulia main.jl\n```\n![](../assets/readme_hello_world.png)\n\n!!! compat \"GIO Warnings on non-Linux\"\n    On Windows and macOS, running `main` may  produce a warning of the type:\n\n    ```\n    (julia:10512): GLib-GIO-WARNING **: 15:29:40.047: dbus binary failed to launch bus, maybe incompatible version\n    ```\n\n    This is due to a non-critical bug in one of Mousetraps dependencies, and does not indicate a problem.\n\n!!! compat \"Interactive Use\"\n    Interactive use inside the Julia REPL is only available for Mousetrap `v0.2.1` or newer.\n\n---\n\n## Julia Crash Course\n\nThe rest of this manual will assume that readers are familiar with the basics of Julia and some fundamentals of graphics programming. To bring anyone who considers themselves not in this group up to speed, this section contains a crash course on programming basics needed to understand the rest of this manual.\n\n### Glossary\n\nThe following terms may be unfamiliar to some.\n\n#### Invocation\n\nTo \"invoke\" a function means to execute it using a command, possibly providing arguments. For example, the second line in the following snippet *invokes function `foo`*:\n\n```julia\nfoo(x) = println(x) # definition\nfoo(1234) # invocation\n```\n\n#### Instantiation, Construction\n\nIn Julia, for a type `T`, to create an actual object of this type, we need to call its *constructor*. This is a function that returns an object of that type:\n\n```julia\nstruct T \n  function T() # constructor\n    return new() \n  end\nend\n```\n\nWe call an object returned by a constructor an **instance** of `T`. The act of creating an instance is called **instantiation** of `T`. \n\nIn the above, `T()` (the constructor, which is a function), *instantiates* an object of type `T`, then returns that *instance*.\n\n#### Scope\n\n\"Scope\" refers to where a variable is available after it is defined. For example, the following function introduces what is called a \"hard scope\", meaning we do not have access to any variable defined inside the *function's scope*, which is the block of code between `function` and `end`\n\n```julia\nfunction f(x) # hard scope begin\n    y = x + 1\n    return y\nend # hard scope end\n\nprintln(y) # errors because y is not available, it was defined in hard scope\n```\n\n`begin`-`end` blocks are a \"soft scope\", meaning we can access definitions from within this soft scope from the outer scope:\n\n```julia\nbegin # soft scope begin\n    z = 1234\nend # soft scope end\n\nprintln(z) # works\n```\n```\n1234\n```\n\nA \"global\" variable is a variable that is defined in *module scope*. For example, in the following snippet, **both** `a` and `b` are defined in module scope:\n\n```julia\na = 1234\nmodule M\n    b = \"abcd\"\nend\n```\n\nThis is because all Julia code is scoped in module `Main`. In the above, `a`s scope is `Main`, while `b`s scope is `Main.M`. Both are global in respect to their module.\n\n#### Front-End, Back-End, Engine\n\nRegarding GUI apps, developers will often refer to \"front-end\" vs. \"back-end\" code. The exact meaning of these can vary depending on the field; in this manual, *front-end*  refers to any code that produces an object the user can see on screen, meaning the actual GUI. *back-end*, then, is anything that is not *front-end*. \n\nAn *engine* is a programming library that allows developers to create the *front-end*. For this package, Mousetrap is an *engine* for your (the readers) app.\n\n#### Rendering, Frames\n\nIn our `main.jl` above, Mousetrap created a window and presented it on the physical screen. This process of drawing graphics to the screen is also called *rendering*.\n\nEach screen updates at a set frequency, for example 60hz, which means a new image is drawn to the screen every 1/60th of a second. Each of these drawing steps is called a *frame*. This is why we often refer to the speed at which a graphical apps updates as *frames-per-second* (fps), the number of times a new frame is drawn to the screen - per second.\n\nIn Mousetrap, fps is tied to the monitor's refresh rate. If the user's monitor updates at 120Hz, Mousetrap will attempt to draw a new image 120 times per second. Depending on the user's machine, this could be too costly performance-wise, which is why Mousetrap features a \"lazy\" rendering process. An area on the screen will only be updated if it needs to be. \nFor example, in the `main.jl` above, the label `\"Hello World!\"` will only be drawn once. Because it is static (it stays the same and does not move) there is no need to redraw it every frame, unless the window is moved or the label is changed.\n\nThis is in opposition to how many video games work. Usually, in video game engines, each frame will make it such that the entire screen is re-drawn every time. This difference is important to realize.\n\n#### Native Rendering\n\nNative rendering, in Mousetrap, is the process of updating the currently displayed frame using the graphics card, making it a hardware-accelerated, GPU-side operation. This is in opposition to CPU-side rendering, which is generally slower. Native rendering in Mousetrap is performed using [OpenGL](https://www.khronos.org/opengl/wiki/), with an entire chapter of this manual dedicated to this technique.\n\n---\n\n## Object-Oriented Design\n\nWhile Julia is technically object-oriented, it lacks many of the features of \"proper\" OOP languages such as C++ or Java. Examples of missing features include [member functions](https://en.cppreference.com/w/cpp/language/member_functions) and [inheritance from concrete types](https://learn.microsoft.com/en-us/cpp/cpp/inheritance-cpp?view=msvc-170). Additionally, in Mousetrap specifically, most objects will have **no public properties**.\n\nTo interact with an object, we use *outer methods*, which are functions defined in global scope that operate on one of their arguments by modifying its hidden properties.\n\nIf our object is of type `T`, an outer method will have the structure\n\n```julia\nfunction get_foo(instance::T) ::Foo\n    # ...\nend\n\nfunction set_foo!(instance::T, new_value::Foo) ::Nothing\n    # ...\nend\n```\n\nWhere `get_foo` accesses a hidden property of our `T` instance, while `set_foo!` modifies that property of the instance. The `!` at the end of the method name signals that it will modify the `T` instance. In Mousetrap, only functions marked with `!` will mutate (modify). This is the equivalent of [non-const methods](https://learn.microsoft.com/en-us/cpp/cpp/const-cpp?view=msvc-170#const-member-functions) in other OOP languages.\n\nBecause we cannot inspect an object's properties to learn about it, we are reliant on the Mousetrap documentation to know which functions are available for which object. Navigating to the [index of classes](../02_library/classes.md), we see that after each class, there is a list of all \"member functions\", that is, all functions that operate on that object.\n\nAnother way to find out which functions are available is to use [`methodswith`](https://docs.julialang.org/en/v1/stdlib/InteractiveUtils/#InteractiveUtils.methodswith) from within the REPL:\n\n```julia\nusing Mousetrap\nmethodswith(Window)\n```\n\nwhich will print a list of all functions that have at least one argument of type `Window`.\n\n---\n\n## C Enums   \n\nMousetraps back-end is written in C++, whose enums differ from Julia enums in several ways. To assure compatibility, Mousetrap uses its own enum definitions, it does not use Julias `@enum`.\n\nEach enum is a proper Mousetrap type, while each enum *value* is a numerical constant which is defined as being of that type. \n\nFor example, the enum `Orientation`, which describes whether an object is vertically or horizontally oriented, is a type called `Mousetrap.Orientation`.\n\nThe **values** of `Orientation` are global constants:\n\n+ `ORIENTATION_HORIZONTAL`\n+ `ORIENTATION_VERTICAL`\n\nIn this example, `Orientation` is the enum, while `ORIENTATION_HORIZONTAL` and `ORIENTATION_VERTICAL` are the enums values.\n\nInspecting the values in the REPL, we see that they are actually just numbers:\n\n```\njulia> ORIENTATION_HORIZONTAL\nOrientation(0)\n\njulia> ORIENTATION_VERTICAL\nOrientation(1)\n```\n\nBut both are of type `Orientation`:\n\n```julia\njulia> ORIENTATION_HORIZONTAL isa Orientation && ORIENTATION_VERTICAL isa Orientation\ntrue\n```\n\nAll enum values are written in `SCREAMING_SNAKE_CASE`, while the enum types name uses `UpperCamelCase`. \n\nTo check which enum has which values, we can again use the [Mousetrap documentation](../02_library/enums.md).\n\n---\n\n## Do Syntax\n\nIn Julia, any function whose **first argument is another function** can use **do-syntax**.\n\nFor example, the following function takes two arguments:\n\n```julia\nfunction example_f(f, arg)\n    f(arg)\nend\n```\nIt applies its first argument, a function, to its second argument.\n\nInvoking `example_f`, we could do:\n\n```julia\nto_invoke(x::Integer) = println(x)\nexample_f(to_invoke, 1234)\n```\n```\n1234\n```\n\nWhere `to_invoke` is used as the **first** argument. Because it is the first, we can also write the above using do-syntax:\n\n```julia\nexample_f(1234) do x::Integer\n    println(x)\nend\n```\n\nHere, the first argument of `example_f` was omitted, while the second argument, `1234` remained. Instead of the first argument, we append the line `do x::Integer`, where `x` is the name of the anonymous function's argument. After this line, we define the function's body, then `end`.\n\n## Anonymous Functions in Stacktraces\n\nIn the REPL, we can print any object's name to inspect it. Creating a new function, which prints its argument's name:\n\n```julia\nprint_f_name(f) = println(f)\n```\n\nWe see that if we invoke this function using regular function syntax, we get the following output:\n\n```julia\nfunction to_invoke()\n    # do nothing\nend\n\nprint_f_name(to_invoke)\n```\n```\nto_invoke\n```\n\nIf we instead call this function using do-syntax:\n\n```julia\nprint_f_name() do \n    # do nothing\nend\n```\n```\n#9\n```\n\nWe get `#9`. This is a **temporary name** used by Julia to keep track of anonymous functions. A stacktrace in Mousetrap will often contain many anonymous function names like this:\n\n```julia\nmain() do app::Application\n    throw(ErrorException(\"error\"))\nend\n```\n```\n[ERROR] In Mousetrap.main: error\nStacktrace:\n [1] (::var\"#11#12\")(app::Application)\n   @ Main ./REPL[15]:2\n [2] (::TypedFunction)(args::Application)\n   @ Mousetrap ~/Workspace/Mousetrap.jl/src/Mousetrap.jl:74\n [3] (::Mousetrap.var\"#15#17\"{TypedFunction})(app::Application)\n   @ Mousetrap ~/Workspace/Mousetrap.jl/src/Mousetrap.jl:1571\n [4] (::TypedFunction)(args::Application)\n   @ Mousetrap ~/Workspace/Mousetrap.jl/src/Mousetrap.jl:74\n [5] (::Mousetrap.var\"#6#8\"{TypedFunction})(x::Tuple{CxxWrap.CxxWrapCore.CxxRef{Mousetrap.detail._Application}})\n   @ Mousetrap ~/Workspace/Mousetrap.jl/src/Mousetrap.jl:620\n [6] safe_call(scope::String, f::Function, args::Tuple{CxxWrap.CxxWrapCore.CxxRef{Mousetrap.detail._Application}})\n   @ Mousetrap ~/Workspace/Mousetrap.jl/src/Mousetrap.jl:144\n [7] run!(arg1::Mousetrap.detail._ApplicationAllocated)\n   @ Mousetrap.detail ~/.julia/packages/CxxWrap/aXNBY/src/CxxWrap.jl:624\n [8] run!(app::Application)\n   @ Mousetrap ~/Workspace/Mousetrap.jl/src/Mousetrap.jl:1538\n [9] (::Mousetrap.var\"#14#16\"{var\"#11#12\", String})()\n   @ Mousetrap ~/Workspace/Mousetrap.jl/src/Mousetrap.jl:1581\n```\n\nWe see that the anonymous function was allocated as `var\"#11#12\"`. This refers to the function defined using the do-block after `main()`.\n\nMousetrap stacktraces can get quite long, so it's best to parse them by reading the original message at the top first:\n\n```\n[ERROR] In Mousetrap.main: error\n```\nWe see that the message mentions that the error occurred during invokation of `Mousetrap.main`. We should therefore look for an error inside the do-block after `main`.\n\nKnowledge about anonymous functions and how to read stacktraces will greatly aid us in debugging Mousetrap applications while learning.\n\n"
  },
  {
    "path": "docs/src/01_manual/02_signals.md",
    "content": "# Chapter 2: Signals\n\nIn this chapter, we will learn:\n+ What signals and signal handlers are\n+ How to connect to a signal\n+ How to check which signature a signal expects\n+ How and why to block signals\n\n---\n\n## Signal Architecture\n\nCentral to Mousetrap, as well as other GUI libraries like [GTK4](https://docs.gtk.org/gtk4/) and Qt, is **signal architecture**, or [**signal programming**](https://en.wikipedia.org/wiki/Signal_programming), which is a type of software architecture that triggers behavior using signals.\n\nA **signal**, in this context, has three components:\n+ an **ID**, which uniquely identifies it. IDs may not be shared between signals\n+ an **emitter**, which is a non-signal object\n+ a **callback** or **signal handler**, which is a function called when an emitter emits a signal\n\nIt may be easiest to consider an example:\n\nOne of the simpler interactions with a GUI is clicking a button. In Mousetrap, the [`Button`](@ref) class is made for this purpose. `Button` has the signal `clicked`, which is emitted when a user presses the left mouse button while the cursor hovers over the graphical element of the button.\n\nIn this case, the signals' **ID** is `clicked`, while the signal **emitter** is an instance of `Button`. When a user clicks the button, the in-memory object emits signal `clicked`. \n\nIf we want to tie program behavior to the user clicking the button, we **connect a callback** (a function) to this signal. Once connected, when the button is clicked, `clicked` is emitted, which in turn will trigger invocation of the connected function:\n\n```julia\n# create `Button` instance\nbutton = Button()\n\n# create a signal handler\non_clicked(self::Button) = println(\"clicked\")\n\n# connect signal handler to the signal\nconnect_signal_clicked!(on_clicked, button)\n```\n\nWhich can also be written more succinctly using [do-syntax](https://docs.julialang.org/en/v1/manual/functions/#Do-Block-Syntax-for-Function-Arguments):\n\n```julia\nbutton = Button()\nconnect_signal_clicked!(button) do self::Button\n    println(\"clicked\")\nend\n```\n\n!!! details \"Running Code Snippets\"\n\n    In this section, code snippets will only show the relevant lines. To compile and run the code stated here, we need to create a Julia script with the following content:\n\n    ```julia\n    using Mousetrap\n    main() do app::Application\n        window = Window(app)\n\n        # code snippet goes here\n\n        set_child!(window, widget) # add whatever widget the code snippet is about here\n        present!(window)\n    end\n    ```\n\n    For example, to execute the example snippet above, we would create the following `main.jl` file:\n\n    ```julia\n    using Mousetrap\n    main() do app::Application\n        window = Window(app)\n\n        # snippet start\n        button = Button()\n        connect_signal_clicked!(button) do self::Button\n            println(\"clicked\")\n        end\n        # snippet end\n\n        set_child!(window, button) # add the button to the window\n        present!(window)\n    end\n    ```\n\n    Then execute it from the console by calling `julia main.jl`\n\nWhen we execute this code, we see that a small window opens that contains our button. By clicking it, we get:\n\n```\nclicked\n```\n\nOnly one callback can be connected to each signal of a signal emitter. If we call `connect_signal_clicked!` again with a new callback, the old callback will be overridden. If we want to trigger two functions, `callback_01` and `callback_02` at the same time, we can simply do the following:\n\n```julia\ncallback_01() = # ...\ncallback_02() = # ...\n\n# call both functions from the signal handler\nconnect_signal_clicked!(button) do self::Button\n    callback_01()\n    callback_02()\nend\n```\n\n---\n\n## SignalEmitters\n\n`Button`, like most classes in Mousetrap, is a subtype of an abstract type called [`SignalEmitter`](@ref). \n\nSubtyping `SignalEmitter` is equivalent to saying \"This object can emit signals\". Not all objects in Mousetrap are signal emitters, but most are. \n\nWhen we say \"an object can emit signal `<id>`\", what that means is that the following functions are defined for that object:\n\n+ `connect_signal_<id>!`\n+ `disconnect_signal_<id>!`\n+ `emit_signal_<id>`\n+ `set_signal_<id>_blocked!`\n+ `get_signal_<id>_blocked`\n\nFor example, `Button` supports the signal with ID `clicked`, so the following functions are defined for it:\n\n+ `connect_signal_clicked!`\n+ `disconnect_signal_clicked!`\n+ `emit_signal_clicked`\n+ `set_signal_clicked_blocked!`\n+ `get_signal_clicked_blocked`\n\n\nWe'll now go through what each of these functions does and how to use them.\n\n---\n\n## Connecting Signal Handlers\n\nAbove, we've already seen an example of how to connect a signal handler to a signal using `connect_signal_clicked!`. \n\nWhat may not have been obvious is that the signal handler, the anonymous function in the above code snippet, is required to **conform to a specific signature**.\n\n!!! note \"Function Signature Syntax\" \n    \n    A function's **signature** describes a function's return- and argument types. For example, the function\n\n    ```julia\n    function foo(i::Int32, s::String) ::String\n        return string(i) * s\n    end\n    ```\n    \n    Has the signature `(::Int32, ::String) -> String`.  It takes a 32-bit integer and a string, and it returns a string.\n\n    The anonymous function from this do-block:\n\n    ```julia\n    connect_signal_clicked!(button) do self::Button\n        println(\"clicked\")\n    end\n    ```\n\n    has the signature `(::Button) -> Nothing`. It takes an instance of type `Button` and returns `nothing`.\n\n    For a function with an optional argument like this:\n\n    ```julia\n    function foo_optional(i::Int32, string::String, optional::Bool = true) ::String\n        return string(i) * string * string(optional)\n    end\n    ```\n\n    We convey that the last argument is optional by enclosing it in `[]`: `(::Int32, ::String, [::Bool]) -> String`\n\n    In general, a function with argument types `Arg1_t, Arg2_t, ...`, return type `Return_t`, and optional arguments `Optional1_t, Optional2_t` has the signature \n    ```\n    (Arg1_t, Arg2_t, ..., [Optional1_t, Optional2_t, ...]) -> Return_t`.\n    ```\n\n    If and only if the `Return_t` of a function is `Nothing`, we can omit the return types along with the trailing `->`.\n\n\nEach signal requires it's a callback to conform to a specific signature. This signature is different for each signal. If we attempt to connect a handler that has the wrong signature, an `AssertionError` will be thrown at compile time. This makes it important to know how to check which signal requires which signature. \n\n## Checking Signal Signature\n\nWorking with our example, signal `clicked` of class `Button`, let's say we do not know what function signature this signal expects.\nTo find out, we check the Mousetrap documentation, either by visiting [`Button`](@ref)s documentation online, or from within the REPL by pressing `?` and entering the name of the class we want to look up:\n\n```\nhelp?> Mousetrap.Button\n```\n```  \n  Button <: Widget\n  ≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡\n\n  Button with a label. Connect to signal clicked or specify an action via set_action! in order to\n  react to the user clicking the button.\n\n  Constructors\n  ==============\n\n  Button()\n  Button(label::Widget)\n  Button(::Icon)\n\n  Signals\n  =========\n\n  │  clicked\n  │  ---------\n  │\n  │  │  (::Button, [::Data_t]) -> Nothing\n  │\n  │  Emitted when the user clicks a widget using a mouse or touchscreen.\n\n  Fields\n  ========\n\n  (no public fields)\n\n  Example\n  =========\n\n  button = Button()\n  set_child!(button, Label(\"Click Me\"))\n  connect_signal_clicked!(button) do x::Button\n      println(\"clicked!\")\n  end\n  set_child!(window, button)\n```\n\nWe see that button has a single signal, `clicked`. Along with this information, a description of when that signal is emitted is given, and that it requires the signature `(::Button, [::Data_t]) -> Nothing`, where `Data_t` is an optional argument of arbitrary type, which we can use to hand data to the signal handler.\n\n## Handing Data to Signal Handlers\n\nWhile we do get passed the signal emitter instance as the first argument to the signal handler, `::Button` in this case, we will often need to reference other objects. This may necessitate accessing global variables, which [is discouraged in Julia](https://docs.julialang.org/en/v1/manual/performance-tips/#Avoid-untyped-global-variables).\n\nInstead, Mousetrap allows adding an optional, arbitrarily typed, *single* argument to the end of any signal handler signature. This object is often referred to as `data`, its type will therefore be called `Data_t`.\n\nExpanding on our previous example, if we want to send a customized message when the user clicks our button, we can change the signal handler as follows:\n\n```julia\nbutton = Button()\n\n# new signal handler that takes two arguments\non_clicked(self::Button, data) = println(data)  \n\n# connect the signal handler, providing a third argument as `data`\nconnect_signal_clicked!(on_clicked, button, \"custom message\")\n```\n\nOr, using do-syntax:\n\n```julia\nbutton = Button()\nconnect_signal_clicked!(button, \"custom message\") do self::Button, data\n    println(data)\nend\n```\n\nBy clicking the button, we now get:\n\n```\ncustom message\n```\n\nAny and all objects can be provided as `data`, but they have to be packaged as exactly **one** argument.\n\n### Grouping Data Arguments\n\nBecause there is only one `data`, it may seem limiting as to what or how much data we can pass to the signal handlers. In practice, this is not true, \nbecause we can use a simple trick to group any number of objects into a single argument.\n\nLet's say we want to forward a string `\"abc\"`, an integer `999`, and a vector of floats `[1.0, 2.0, 3.0]` to the signal handler. To achieve this, we can do the following:\n\n```julia\nbutton = Button()\n\nfunction on_clicked(self::Button, data)\n    println(data.string_value)\n    println(data.integer_value)\n    println(data.vector_value)\nend\n\n# create a named tuple that groups the arguments\nnamed_tuple = (\n    string_value = \"abc\",\n    integer_value = 999,\n    vector_value = [1.0, 2.0, 3.0]\n)\n\n# provide the named tuple as\nconnect_signal_clicked!(on_clicked, button, named_tuple)\n```\n\nHere, we grouped the values in a [named tuple](https://docs.julialang.org/en/v1/manual/types/#Named-Tuple-Types), then accessed each value using an\neasy-to-read name.\n\nAgain, we can write the above more succinctly using do-syntax:\n\n```julia\nbutton = Button()\nconnect_signal_clicked!(button, (\n    string_value = \"abc\", integer_value = 999, vector_value = [1.0, 2.0, 3.0]\n)) do self::Button, data\n    println(data.string_value)\n    println(data.integer_value)\n    println(data.vector_value)\nend\n```\n\nUsing this technique, we can forward any objects to the signal handler via the optional `[::Data_t]` argument. This technique is available for all signals.\n\n## Implicit Return Types\n\nJulia allows functions to return a value without using the `return` keyword. This may cause side effects in applications where functions are required to conform to a specific signature. Consider the following example:\n\n```julia\nbutton = Button()\n\nto_append = []\nfunction on_clicked(self::Button)\n    push!(to_append, 1234)\nend\n\nconnect_signal_clicked!(on_clicked, button)\n```\n\nHere we are appending a value to `to_append`, a vector, from within the signal handler for signal `clicked`, which is required to have the signature:\n\n```julia\n(::Button, [::Data_t]) -> Nothing\n```\n\n`on_clicked`, in this example, does not explicitly return a value, yet running the above code we get:\n\n```\n[ERROR] In Mousetrap.main: AssertionError: Object `on_clicked` is not invokable as function with signature `(Button) -> Nothing`, because its return type is not `Nothing`\n```\n\nThis is because `Base.push!` actually **does** return a value, the vector it is operating on:\n\n```julia\nto_append = []\nout = push!(to_append, 1234)\nout == to_append # true\n```\n\nBecause `push!` returns a value and it is the last line of the `on_clicked` definition, `on_clicked`, in turn, returns a value, meaning its return type is no longer `Nothing`, which triggers the error.\n\nIn Mousetrap, all functions whose documentation does not explicitly mention a return type will return `nothing`. This may not be true for functions in `Base` or foreign libraries, so we should take care to be aware of implicit return types. \n\nTo fix the above error, we should return `nothing` manually:\n\n```julia\nto_append = []\nfunction on_clicked(self::Button)\n    push!(to_append, 1234)\n    return nothing\nend\nconnect_signal_clicked!(on_clicked, button) # works\n```\n\n---\n\n## Blocking Signal Emission\n\nIf we want an object to *not* call the signal handler on signal emission, we have two options:\n\nUsing `disconnect_signal_<id>`, we can **disconnect** the signal, which will permanently remove the registered signal handler, deallocating it and fully dissociating it from the original signal emitter instance. This is a quite costly operation and should only rarely be necessary. \n\nA much more performant and convenient method to **temporarily** prevent signal emission is  **blocking** the signal.\n\nBlocking a signal will prevent the invocation of the signal handler. This means, for our `Button` example, once we call:\n\n```julia\nset_signal_clicked_blocked(button, true)\n```\n\nThe user can still click the button, but the connected handler is not called.\n\nTo block a signal, we use `set_signal_<id>_blocked!`, which takes a boolean as its second argument. We can check whether a signal is currently blocked using `get_signal_<id>_blocked`. If no signal handler is connected, this function will return `true`.\n\n### Signal Blocking: An Example\n\nWhen is blocking necessary? Consider the following use-case:\n\n```julia\n# declare two buttons\nbutton_01 = Button()\nbutton_02 = Button()\n\n# when button 01 is clicked, 02 is triggered programmatically\nconnect_signal_clicked!(button_01, button_02) do button_01::Button, button_02::Button\n    # button_01 is self, button_02 is data\n    println(\"01 clicked\")\n    emit_signal_clicked(button_02)\nend\n\n# when button 02 is clicked, 01 is triggered programmatically\nconnect_signal_clicked!(button_02, button_01) do button_02::Button, button_01::Button\n    # button_02 is self, button_01 is data\n    println(\"02 clicked\")\n    emit_signal_clicked(button_01)\nend\n\n# add both buttons to the window\nset_child!(window, hbox(button_01, button_02))\n```\n\nIn which we use [`emit_signal_clicked`](@ref), which manually triggers emission of signal `clicked`.  \n\n[`hbox`](@ref) in the last line makes it so that we can display both buttons in the window.\n\nThe intended behavior is that if the user clicks either one of the buttons, both buttons emit their signal. Clicking one button should always trigger both, regardless of which button is clicked first.\n\nRunning the above code as-is and clicking `button_01`, we get the following output:\n\n```\n01 clicked\n02 clicked\n01 clicked\n02 clicked\n01 clicked\n02 clicked\n01 clicked\n02 clicked\n...\n```\n\nAnd our application deadlocks. This is, of course, extremely undesirable, so let's talk through why this happens.\n\nWhen `button_01` is clicked, it emits signal `clicked`, which invokes the connected signal handler. Going line-by-line through the handler :\n+ `button_01`s handler prints `\"01 clicked\"`\n+ `button_01`s handler activates `button_02`, triggering emission of signal `clicked` on `button_02`\n+ `button_02`s handler prints `\"02 clicked\"`\n+ `button_02`s handler activates `button_01`, triggering emission of signal `clicked` on `button_01`\n+ `button_01`'s handler prints `\"01 clicked\"`\n+ etc.\n\nWe created an infinite loop.\n\nWe can avoid this behavior by **blocking signals** at strategic times:\n\n```julia\nbutton_01 = Button()\nbutton_02 = Button()\n\nconnect_signal_clicked!(button_01, button_02) do button_01::Button, button_02::Button\n    println(\"01 clicked\")\n    \n    # block self (01)\n    set_signal_clicked_blocked!(button_01, true)\n\n    # activate other (02)\n    emit_signal_clicked(button_02)\n\n    # unblock self (01)\n    set_signal_clicked_blocked!(button_01, false)\nend\n\nconnect_signal_clicked!(button_02, button_01) do button_02::Button, button_01::Button\n    println(\"02 clicked\")\n\n    # block self (02)\n    set_signal_clicked_blocked!(button_02, true)\n\n    # activate other (01)\n    emit_signal_clicked(button_01)\n\n    # unblock self (02)\n    set_signal_clicked_blocked!(button_02, false)\nend\n\nset_child!(window, hbox(button_01, button_02))\n```\n\nLet's talk through what happens when the user clicks one of the two buttons now, again assuming `button_01` is the first to be clicked:\n\n+ `button_01` invokes its signal handler\n+ `button_01`s signal handler prints `01 clicked`\n+ `button_01` blocks invocation of its signal handler\n+ `button_01` activates `button_02`, triggering emission of signal `clicked`\n+ `button_02`s signal handler prints `02 clicked`\n+ `button_02` blocks invocation of its signal handler\n+ `button_02` attempts to activate `button_01`, **but that buttons signal is blocked, so nothing happens**\n+ `button_02` unblocks itself\n+ `button_01` unblocks itself\n+ both signal handlers return normally \n\n```\n01 clicked\n02 clicked\n```\n\nBy blocking signals, we get the correct behavior of both buttons being triggered exactly once. Because they unblock themselves at the end of the signal handler, after the two buttons are done, everything returns to the way it was before, meaning both buttons can be clicked once again.\n\nTo verify this is indeed the resulting behavior, we can use the following `main.jl`:\n\n```julia\nusing Mousetrap\nmain() do app::Application\n    window = Window(app)\n    \n    button_01 = Button()\n    button_02 = Button()\n\n    connect_signal_clicked!(button_01, button_02) do button_01::Button, button_02::Button\n        println(\"01 clicked\")\n        \n        set_signal_clicked_blocked!(button_01, true)\n        emit_signal_clicked(button_02)\n        set_signal_clicked_blocked!(button_01, false)\n    end\n\n    connect_signal_clicked!(button_02, button_01) do button_02::Button, button_01::Button\n        println(\"02 clicked\")\n\n        set_signal_clicked_blocked!(button_02, true)\n        emit_signal_clicked(button_01)\n        set_signal_clicked_blocked!(button_02, false)\n    end\n\n    set_child!(window, hbox(button_01, button_02))\n    present!(window)\nend\n```\n\n![](../assets/double_button_signal_blocking.png)\n\n---\n"
  },
  {
    "path": "docs/src/01_manual/03_actions.md",
    "content": "# Chapter 3: Actions\n\nIn this chapter, we will learn:\n+ How and why to use the command pattern to encapsulate application functionality\n+ How to create and use `Action`\n+ How to trigger actions using `Button`, or by pressing a keyboard shortcut\n\n---\n\n## Introduction: The Command Pattern\n\nAs we create more and more complex applications, keeping track of how / when to trigger which functionality gets harder and harder. An application can have hundreds, if not thousands, of functions, all linked to one or more triggers such as buttons, menus, keyboard shortcuts, etc. \n\nThings will get out of hand very quickly, which is why there's a software design pattern just for this purpose: the [**command pattern**](https://en.wikipedia.org/wiki/Command_pattern).\n\nA **command**, henceforth called **action**, is an object that has the following components:\n+ A **function**, which is the action's behavior\n+ An **ID** that uniquely identifies the action\n+ An optional **shortcut trigger**, also often called a keybinding\n\nIn Mousetrap, a command is represented by the type [`Action`](@ref).\n\n## Action\n\nAs early as possible, we should drop the habit of defining application behavior inside a global function. Unless a function is used exactly once, it should be an action.\n\nFor example, in the previous chapter, we declared a [`Button`](@ref) with the following behavior:\n\n```julia\nbutton = Button()\nconnect_signal_clicked!(button) do self::Button\n    println(\"clicked\")\nend\n```\n\nIn this section, we will learn how to reproduce this behavior using the command pattern and why we should prefer this over connecting a signal handler for signal `clicked`.\n\n### Action IDs\n\nWhen creating an action, we first need to choose the action's **ID**. An ID is an identifier that uniquely identifies the action. The ID can only contain the character `[a-zA-Z0-9_-.]`, that is, all Roman letters, numbers `0` to `9`, `_`, `-` and `.`. The dot is usually reserved to simulate scoping. \n\nFor example, one action could be called `image_file.save`, while another is called `text_file.save`. Both actions say what they do, `save` a file, but the prefix makes it clear which part of the application they act on.\n\nAn appropriate ID for our button behavior would therefore be `example.print_clicked`. \n\n### Action Function\n\nArmed with this ID, we can create an action:\n\n```julia\naction = Action(\"example.print_clicked\", app)\n```\nWhere `app` is the application instance from our `main`.\n\nThe second part of an action is its function, also called its callback. We assign an actions function using [`set_function!`](@ref):\n\n```julia\nfunction on_example_print_clicked(x::Action) ::Nothing\n    println(\"clicked\")\nend\n\naction = Action(\"example.print_clicked\", app)\nset_function!(on_example_print_clicked, action)\n```\n\nor, using do-syntax:\n\n```julia\naction = Action(\"example.print_clicked\", app)\nset_function!(action) do x::Action\n    println(\"clicked\")\nend\n```\n\nThe function registered using `set_function!` is required to have the following signature:\n\n```julia\n(::Action, [::Data_t]) -> Nothing\n```\n\nWe see that, much like with signal handlers, the callback is provided the `Action` instance, along with an optional `data` argument.\n\n`Action` also provides a constructor that directly takes the function as its first argument. Using this, we can write the above even more succinctly:\n\n```julia\naction = Action(\"example.print_clicked\", app) do x::Action\n    println(\"clicked\")\nend\n```\n\n### Triggering Actions\n\nAt any point, we can call [`activate!`](@ref) to trigger the actions' callback. This is not the only way to trigger an action, however. \n\n`Button` provides [`set_action!`](@ref), which makes it such that when the button is clicked, the action is triggered:\n\n```julia\naction = Action(\"example.print_clicked\", app)\nset_function!(action) do x::Action\n    println(\"clicked\")\nend\n\nbutton = Button()\nset_action!(button, action)\n```\n```\nclicked\n```\n\nSo far, this doesn't seem to have any upsides over just connecting to signal `clicked`. This is about to change.\n\n## Disabling Actions\n\nSimilarly to how blocking signals work, we can disable an action using [`set_enabled!`](@ref). If set to `false`, calling `activate!` will trigger no behavior. Furthermore, **all objects the action is connected to are automatically disabled**. This means we do not need to keep track of which button calls which action. To disable all of them, we can simply disable the action. \n\n## Action Maps\n\nWe recall that `Action`s constructor requires an instance of our `Application` as its second argument. This is because the two are linked internally, all actions are registered with the application and are accessible only from within that application. In this way, `Application` itself acts as an **action map**, an index of all actions.\n\nOnce `set_function!` was called, we can, at any point, retrieve the action from the application using `get_action!`:\n\n```julia\nlet action = Action(\"example.print_clicked\", app)\n    set_function!(action) do x::Action\n        println(\"clicked\")\n    end\nend\n\n# `action` is no longer defined here because of `let`\n\nactivate!(get_action(app, \"example.print_clicked\"))\n\n# but we can retrieve it anyway\n```\n```\nclicked\n```\n\nWhere we used a [let-block](https://docs.julialang.org/en/v1/base/base/#let) to create a \"hard\" scope, meaning at the end of the block, `action`, the Julia-side object, is no longer defined. We can nonetheless retrieve it by calling `get_action!` on our `Application` instance.\n\nThis way, we do not have to keep track of actions ourselves; by simply remembering the action's ID, we can, at any point, trigger the action from anywhere in our application.\n\n---\n\n## Shortcuts\n\nAn action can have any number of optional **shortcut triggers**, which are also called **keybindings**. \n\nA keybinding is a combination of keyboard keys that, when pressed, trigger an action exactly once. Common keyboard shortcuts familiar to most users of modern operating systems are `Control + C` to copy, `Control + A` to \"select all\", etc. \n\nMost of the time, we will have to implement behavior like this and associate a shortcut with it manually, using actions.\n\n### Shortcut Trigger Syntax\n\nBefore we can learn about keybindings, we need to talk about keys. In Mousetrap, keyboard keys\nare split into two groups: **modifiers**  and **non-modifiers**.\n\nA modifier is one of the following:\n+ `Shift`\n+ `Control`\n+ `Alt`\n\n!!! note \n    Additional modifiers include `CapsLock`, `AltGr`, `Meta`, `Apple`, and `Win`. These are keyboard-layout and/or OS-specific. See [here](https://docs.gtk.org/gdk4/flags.ModifierType.html) for more information.\n\nA non-modifier, then, is any key that is not a modifier.\n\nA keybinding, or **shortcut trigger**, henceforth also called \"shortcut\", is the combination of **any number of modifiers, along with exactly one non-modifier key**. A few examples:\n\n+ `a` (that is the `A` keyboard key) is a shortcut\n+ `<Control><Shift>plus` (that is the `+` keyboard key, along with the `Control` and `Shift` modifiers) is a shortcut\n+ `<Alt><Control><Shift>` is **not** a shortcut, because it does not contain a non-modifier\n+ `<Control>xy` (that is the `X` key *and* the `Y` key) is **not** a shortcut because it contains more than one non-modifier key\n\nShortcuts are represented as strings, which have a specific syntax. As seen above, each modifier is enclosed in `<``>`, with no spaces in between. After the group of modifiers, the non-modifier key is placed after the last modifier`>`. Some more examples:\n\n+ \"Control + C\" is written `<Control>c`\n+ \"Alt + LeftArrow\" is written as `<Alt>Left` (sic, `L` is capitalized)\n+ \"Shift + 1\" is written as `exclam`\n\nThat last one requires an explanation. On most keyboard layouts, to type `!`, the user has to press the shift modifier key, then press the `1` key. When \"Shift + 1\" is pressed,  Mousetrap does not receive this keyboard key event as-is, instead, it receives a single key event for the `!` key with no modifiers. The identifier of `!` is `exclam`, hence why \"Shift + 1\" is written as `exclam`.\n\n!!! tip \"Looking up Key Identifiers\"\n\n    An example of how to look up the identifier of any key will be performed here.\n\n    Let's say we want to write the shortcut \"Control + Space\". We know that we can write \"Control\" as `<Control>`. Next, we navigate to [https://github.com/Clemapfel/mousetrap.jl/blob/main/src/key_codes.jl](https://github.com/Clemapfel/mousetrap.jl/blob/main/src/key_codes.jl), \n    which has a list of all keys recognized by Mousetrap. In [line 1039](https://github.com/Clemapfel/mousetrap.jl/blob/main/src/key_codes.jl#L1039), we find that the constant for the space key is called `KEY_space`. The identifier of a key used for shortcuts is this name, without the `KEY_` prefix. For the space bar key, the enum value is `KEY_space`, the identifier is therefore `space`.\n\n    One more obscure example: to write \"Alt + Beta\", that is, the `β` key on the somewhat rare Greek keyboard layout, we find the constant named `KEY_Greek_BETA` in [line 3034](https://github.com/Clemapfel/mousetrap.jl/blob/main/src/key_codes.jl#L3034). Erasing `KEY_` again, the key's identifier is `Greek_BETA`. \"Alt + Beta\" is therefore written as `<Alt>Greek_BETA`\n\n    If we make an error and use the wrong identifier, a soft warning will be printed at runtime, informing us of this. \n\n    To access a list of key codes from within the REPL, we can search the vector `Mousetrap.key_codes`, which contains the symbols of all key codes recognized by Mousetrap, with the `KEY_` prefix already removed:\n    ```julia\n    julia> Mousetrap.key_codes\n        2278-element Vector{Symbol}:\n        :0\n        :1\n        :2\n        :3\n        ...\n    ```\n\n!!! warning \"Operating System Priority\"\n    Depending on the operating system, some shortcuts will already be assigned. If this is the case, we should take care not to use them in our application. For example, the abovementioned `<Control>space` shortcut [is reserved for changing input sources on macOS](https://discussions.apple.com/thread/8507324), while on Windows `<Control><Alt>Delete` will always open the task manager.\n\n### Assigning Shortcuts to Actions\n\nNow that we know how to write a shortcut as a shortcut trigger string, we can assign it to our actions. For this, we use [`add_shortcut!`](@ref):\n\n```julia\nshortcut_action = Action(\"example.shortcut_action\", app) do self::Action\n    println(\"shortcut action called\")\nend\nadd_shortcut!(shortcut_action, \"<Control>M\")\n```\n\nAn action can have multiple shortcuts, and one shortcut can be associated with two or more actions, though the latter is usually not recommended.\n\nWe need one more thing before we can trigger our action: an object that can receive keyboard key events. We will learn much more about the event model [in the chapter dedicated to it](./05_event_handling.md). For now, we can use [`set_listens_for_shortcut_action!`](@ref) on our top-level window. This makes the window instance listen for any keyboard presses. If it recognizes that a keybinding associated with an action it is listening for was pressed, it will trigger that action.\n\nA complete `main.jl` file showing how to trigger an action using a shortcut is given here:\n\n```julia\nusing Mousetrap\nmain() do app::Application\n\n    # create a window\n    window = Window(app)\n\n    # create an action that prints `shortcut action called`\n    action = Action(\"example.shortcut_action\", app) do action::Action\n        println(\"shortcut action called\")\n    end\n\n    # add the shortcut `Control + M`\n    add_shortcut!(action, \"<Control>M\")\n    \n    # make `window` listen for all shortcuts of `action`\n    set_listens_for_shortcut_action!(window, action)\n\n    # show the window to the user\n    present!(window)\nend\n```\n\nPressing \"Control + M\", we get:\n\n```\nshortcut action called\n```\n"
  },
  {
    "path": "docs/src/01_manual/04_widgets.md",
    "content": "# Chapter 4: Widgets\n\nIn this chapter, we will learn:\n+ What a widget is\n+ Properties that all widgets share\n+ What widgets are available in Mousetrap and how to use each of them\n+ How to create compound widgets\n\n---\n\n!!! note \"Running snippets from this Chapter\"\n    To run any partial code snippet from this section, we can use the following `main.jl` file:\n    ```julia\n    using Mousetrap\n    main() do app::Application\n        window = Window(app)\n\n        # snippet here, creates widget, and adds it to `window' using `set_child!`\n\n        present!(window)\n    end\n    ```\n\n!!! note \"Images in this Chapter\"\n    Images for this chapter were captured on a Fedora Linux machine running Gnome 44.2. The exact look of each window and widget may be slightly different, depending on the user's operating system and application theme. We will learn how to manually change the look of widgets by creating our own theme in the [section on app customization](./10_theme_customization.md).\n\n---\n\n## What is a widget?\n\nWidgets are the central element of all GUI applications. In general, a widget is anything that can be rendered on screen. Often, widgets are **interactable**, which means that the user can trigger behavior by interacting with the widget using a device such as a mouse, keyboard, or touch screen.\n\nFor example, to interact with the widget [`Button`](@ref) from the previous chapter, the user has to move the cursor over the area of the button on the screen, and then press the left mouse button. This will trigger an animation where the button changes its appearance to look \"pressed in\", emit its signal `clicked` to trigger custom behavior, then return to its previous state. \n\nHaving used computers for many years, most of us never think about how things work in this gradual of a manner. `Button` makes it so we don't have to, all of these steps are already implemented for us. All we have to do is place the button and connect to its signals.\n\n## Widget Signals\n\nIn Mousetrap, [`Widget`](@ref) is an abstract type that all widgets subtype. `Widget` is a subtype of `SignalEmitter`, meaning\nall widgets are signal emitters, but not all signal emitters are widgets. \n\nAll widgets **share a number of signals**. These signals are accessible for every subtype of `Widget`:\n\n| Signal ID  | Signature                       |\n|------------|---------------------------------|\n| `realize`  | `(::T, [::Data_t]) -> Nothing`  |\n| `destroy`  | `(::T, [::Data_t]) -> Nothing`  | \n| `show`     | `(::T, [::Data_t]) -> Nothing`  | \n| `hide`     | `(::T, [::Data_t]) -> Nothing`  |\n| `map`      | `(::T, [::Data_t]) -> Nothing`  | \n| `unmap`    | `(::T, [::Data_t]) -> Nothing`  | \n\nWhere `T` is the subtype. For example, since `Button` is a `Widget`, the signature of `Button`s signal `realize` is `(::Button, [::Data_t]) -> Nothing`.\n\nBy the end of this chapter, we will have learned when all these signals are emitted and what they mean. For now, we will just note that all widgets share these signals. For any class subtyping `Widget`, these signals are available. \n\n---\n\n## Widget Properties\n\nWhen displayed on screen, a widget's size and location will be chosen dynamically. Resizing the window may or may not resize all widgets inside such that they fill the entire window. A somewhat complex heuristic determines the exact position and size of a widget during runtime. We can influence this process using multiple properties all widgets share.\n\nEach widget will choose a position and size on screen. We call this area, an [axis-aligned rectangle](https://en.wikipedia.org/wiki/Minimum_bounding_box#Axis-aligned_minimum_bounding_box), the widget's **allocated area**. The allocated area can change over the course of runtime, most widgets are easily resized either by us, the developers, or the user.\n\n### Parent and Children\n\nWidgets can be inside other widgets. A widget that can contain other widgets is called a **container** widget. Each widget inside this container is called the **child** of the container. `Window`, in our previous `main.jl`, is a container widget, we inserted `Button`, a widget, into it using `set_child!`.\n\nHow many children a widget can contain depends on the type of widget. Some may have no children, exactly one child, exactly two, exactly three, or any number of children. Shared for all widgets, however, is that each widget has exactly one **parent**. This is the widget it is contained within. \n\nBecause a widget can only have exactly one parent, we cannot put the same widget instance into two containers. If we want two identical `Button`s in two different positions on screen, we have to create two button instances.\n\n```julia\nbox_a = Box(ORIENTATION_HORIZONTAL)\nbox_b = Box(ORIENTATION_HORIZONTAL)\nbutton = Button()\n\n# insert `button` into box A\npush_back!(box_a, button)\n\n# insert `button` into box B also\npush_back!(box_b, button)\n```\n\nThis latter call will print a warning\n\n```\n(julia:445703): Mousetrap-CRITICAL **: 18:13:31.132: In Box::push_back: Attempting to insert widget into a container, but that widget already has a parent.\n```\n\nbecause `button`s parent is already `box_a`. Instead, we should create two buttons:\n\n```julia\nbox_a = Box(ORIENTATION_HORIZONTAL)\nbox_b = Box(ORIENTATION_HORIZONTAL)\nbutton_a = Button()\nbutton_b = Button()\n\npush_back!(box_a, button_a)\npush_back!(box_b, button_b)\n```\n\nBy connecting the same handler to both of these buttons' signals, we have two identically behaving objects, that are still separate widget instances.\n\n### Size Request\n \nMoving onto the properties that determine the widget's size, we have its **size request**. This is a [`Vector2f`](@ref Vector2) which governs the minimum width and height of the widget, in pixels. Once set with [`set_size_request!`](@ref), no matter what, that widget will always allocate at least that amount of space. \n\nBy default, all widget's size request is `Vector2f(0, 0)`. Setting the width and/or height of a widget's size request to `0` will tell the size manager, the algorithm determining the widget's final size on screen, that the widget has not requested a minimum size. A size request may not be negative.\n\nManipulating the size request to influence a widget's minimum size is also called **size-hinting**.\n\n### Accessing Widget Size\n\nWe can query information about a widget's current and target size using multiple functions, some of which are only available after a widget is **realized**. Realization means that the widget is initialized, has chosen its final size on screen, and is ready to be displayed. When these conditions are met, any widget will emit its signal `realize`. \n\nOnce realized, [`get_allocated_size`](@ref) and [`get_position`](@ref) return the current size and position of a widget, in pixels.\n\nThis size may or may not be equal to what we size-hinted the widget to, as size-hinting only determines the widget's *minimum size*. The layout manager is free to allocate a size larger than that.\n\nLastly, [`get_natural_size`](@ref) will access the size preferred by the layout manager. This size will always be equal to or larger than the size requested. When trying to predict the size a widget has, `get_natural_size` will give us the best estimate. Once the widget and all its children are realized, `get_allocated_size` and `get_position`  will give us the exact value.\n\nLayout management is complex and the algorithm behind managing the size of the totality of all widgets is sophisticated. Users of Mousetrap are not required to understand this exact mechanism, only how to influence it.\n\nOn top of a widget's size request, a widget's final allocated size depends on a number of other variables.\n\n### Margin\n\nAny widget has four margins: `start`, `end`, `top`, and `bottom`. Usually, these correspond to space left, right, above, and below the widget, respectively. Margins are rendered as empty space added to the corresponding side of the widget. In this way, they work similarly to the [css properties of the same name](https://www.w3schools.com/css/css_margin.asp), though in Mousetrap, margins may not be negative.\n\nWe use [`set_margin_start!`](@ref), [`set_margin_end!`](@ref), [`set_margin_top!`](@ref) and [`set_margin_bottom!`](@ref) to control each individual margin:\n\n```julia\nwidget = # ...\nset_margin_start!(widget, 10)\nset_margin_end!(widget, 10)\nset_margin_top!(widget, 10)\nset_margin_bottom!(widget, 10)\n\n# equivalent to\nset_margin_horizontal(widget, 10)\nset_margin_vertical(widget, 10)\n\n# equivalent to\nset_margin!(widget, 10)\n```\n\nWhere [`set_margin_horizontal!`](@ref), [`set_margin_vertical!`](@ref) set two of the margins at the same time, while [`set_margin!`](@ref) sets all four margins at once.\n\nMargins are used extensively in UI design. They make an application look more professional and aesthetically pleasing. A good rule of thumb is that for a 1920x1080 display, the **margin unit** should be 10 pixels. That is, all margins should be a multiple of 10. If the display has a higher or lower resolution, the margin unit should be adjusted.\n\n### Expansion\n\nIf the size of the parent of a widget changes, for example when resizing the window a `Button` is contained within, the child widget may or may not **expand**. Expansion governs if a widget should fill out the entire space available to it. We set expansion along the x- and y-axis separately using [`set_expand_horizontally!`](@ref) and [`set_expand_vertically!`](@ref). If set to `false`, a widget will usually not grow past its natural size.\n\n[`set_expand!`](@ref) is a convenience function that sets expansion along both axes simultaneously.\n\n```julia\nwidget = # ...\nset_expand_horizontally!(widget, true)\nset_expand_vertically!(widget, true)\n\n# equivalent to\nset_expand!(widget, true)\n```\n\n### Alignment\n\nWidget **alignment** governs where inside its container a widget will attempt to position itself.\n\nAn example: a `Button` size-hinted to 100x100 pixels has expansion disabled (`set_expand!` was set to `false`). It has a margin of 0 and is placed inside a `Window` of 200x200. When we scale the window, the button will not change size, and the button does not fill the entire area of the window. \n\nAlignment, then, governs **where in the window the button is positioned**.\n\nWe set alignment for the horizontal and vertical axis separately using [`set_horizontal_alignment!`](@ref) and [`set_vertical_alignment!`](@ref), which both take values of the enum [`Alignment`](@ref). This enum has three possible values, whose meaning depends on whether we use this value for the horizontal or vertical alignment:\n\n+ `ALIGNMENT_START`: left if horizontal, top if vertical\n+ `ALIGNMENT_END`: right if horizontal, bottom if vertical\n+ `ALIGNMENT_CENTER`: center of axis, regardless of orientation\n\nWe note that the horizontal x-axis is oriented from left to right, while the vertical y-axis is oriented from top to bottom.\n\nFor our example, the button would take on these locations based on which enum value we chose for each alignment axis:\n\n| Vertical Alignment | Horizontal Alignment | Resulting Position  |\n|--------------------|----------------------|---------------------|\n| `ALIGNMENT_START`  | `ALIGNMENT_START`    | top left corner     |\n| `ALIGNMENT_START`  | `ALIGNMENT_CENTER`   | top center          |\n| `ALIGNMENT_START`  | `ALIGNMENT_END`      | top right corner    |\n| `ALIGNMENT_CENTER` | `ALIGNMENT_START`    | center left         |\n| `ALIGNMENT_CENTER` | `ALIGNMENT_CENTER`   | center              |\n| `ALIGNMENT_CENTER` | `ALIGNMENT_END`      | center right        |\n| `ALIGNMENT_END`    | `ALIGNMENT_START`    | bottom left corner  |\n| `ALIGNMENT_END`    | `ALIGNMENT_CENTER`   | bottom center       |\n| `ALIGNMENT_END`    | `ALIGNMENT_END`      | bottom right corner |\n\n```julia\nwidget = # ...\nset_horizontal_alignment!(widget, ALIGNMENT_START)\nset_vertical_alignment!(widget, ALIGNMENT_START)\n\n# equivalent to\nset_alignment!(widget, ALIGNMENT_START)\n```\n\nUsing alignment, size-hinting, and expansion, we can fully control where and at what size a widget will appear on screen, without having to worry about manually placing it by choosing the exact position or size.\n\n---\n\n### Visibility & Opacity\n\nOnce a widget is realized, when we call [`present!`](@ref) on the window it is contained within, it is **shown**, appearing on screen and emitting signal `show`. If the widget leaves the screen, for example, because it is removed from a container or its window is closed, it is **hidden**, emitting signal `hide`.\n\nTo hide a shown widget or show a hidden widget, we use [`set_is_visible!`](@ref):\n\n```julia\nbutton = Button()\nconnect_signal_clicked!(button) do self::Button\n    set_is_visible!(self, false)\nend\nset_child!(window, button)\n```\n\nIn which a button is hidden when it is clicked. This means the button cannot be clicked again, as its interactivity is only available while it is shown. Once the button is hidden, its allocated size becomes `(0, 0)`.\n\nIf we instead just want to make the button invisible but still have it be clickable, we should use [`set_opacity!`](@ref). This function takes a float in `[0, 1]`, where `0` is fully transparent, `1` is fully opaque:\n\n```julia\n# make a button invisible if it is visible, or visible if it is invisible\nbutton = Button()\nconnect_signal_clicked!(button) do self::Button\n    current = get_opacity(self)\n    if current < 1.0\n        set_opacity!(button, 1.0)\n    else\n        set_opacity!(button, 0.0)\n    end\nend\nset_child!(window, button)\n```\n\nSetting opacity does **not** emit the `hide` or `show` signal. While the widget may be fully transparent and thus invisible to us humans, it retains its interactivity and allocated area on screen.\n\n---\n\n### Cursor Type\n\nEach widget has a property governing what the user's cursor will look like while it is inside the widget's allocated area. By default, the cursor is a simple arrow. A widget intended for text entry would want the cursor to be a [caret](https://en.wikipedia.org/wiki/Cursor_(user_interface)), while a clickable widget would likely want a [pointer](https://en.wikipedia.org/wiki/Cursor_(user_interface)#Pointer).\n\nSome widgets already set the cursor to an appropriate shape automatically, but we can control the cursor shape for each widget manually using [`set_cursor!`](@ref), which takes a value of the enum [`CursorType`](@ref):\n\n| `CursorType` value             | Appearance                                                                                          |\n|--------------------------------|-----------------------------------------------------------------------------------------------------|\n| `CURSOR_TYPE_NONE`             | Invisible cursor                                                                                    |\n| `CURSOR_TYPE_DEFAULT`          | Default arrow pointer                                                                               |\n| `CURSOR_TYPE_POINTER`          | Hand pointing                                                                                       |\n| `CURSOR_TYPE_TEXT`             | Caret                                                                                               |\n| `CURSOR_TYPE_GRAB`             | Hand, not yet grabbing                                                                              |\n| `CURSOR_TYPE_GRABBING`         | Hand, currently grabbing                                                                            |\n| `CURSOR_TYPE_CELL`             | Cross, used for selecting cells from a table                                                        |\n| `CURSOR_TYPE_CROSSHAIR`        | Crosshair, used for making pixel-perfect selections                                                 |\n| `CURSOR_TYPE_HELP`             | Questionmark, instructs the user that clicking or hovering above this element will open a help menu |\n| `CURSOR_TYPE_CONTEXT_MENU`     | Questionmark, instructs the user that clicking will open a context menu                             |\n| `CURSOR_TYPE_NOT_ALLOWED`      | Instructs the user that this action is currently disabled                                           |\n| `CURSOR_TYPE_PROGRESS`         | Spinning animation, signifies that the object is currently busy                                     |\n| `CURSOR_TYPE_WAIT`             | Loading animation, Instructs the user that an action will become available soon                     |\n| `CURSOR_TYPE_ZOOM_IN`          | Lens, usually with a plus icon                                                                      |\n| `CURSOR_TYPE_ALL_SCROLL`       | Omni-directional scrolling                                                                          |\n| `CURSOR_TYPE_MOVE`             | 4-directional arrow                                                                                 |\n| `CURSOR_TYPE_NORTH_RESIZE`     | Up-arrow                                                                                            |\n| `CURSOR_TYPE_NORTH_EAST_RESIZE` | Up-left arrow                                                                                       |\n| `CURSOR_TYPE_EAST_RESIZE`      | Left arrow                                                                                          |\n| `CURSOR_TYPE_SOUTH_EAST_RESIZE` | Down-left arrow                                                                                     |\n| `CURSOR_TYPE_SOUTH_RESIZE`     | Down arrow                                                                                          |\n| `CURSOR_TYPE_SOUTH_WEST_RESIZE` | Down-right arrow                                                                                    |\n| `CURSOR_TYPE_WEST_RESIZE`      | Right arrow                                                                                         |\n| `CURSOR_TYPE_NORTH_WEST_RESIZE` | Up-right arrow                                                                                      |\n| `CURSOR_TYPE_ROW_RESIZE`        | Up-down arrow                                                                                       |\n| `CURSOR_TYPE_COLUMN_RESIZE`     | Left-right arrow                                                                                    |\n\n\n`Button`s default cursor is `CURSOR_TYPE_DEFAULT`. If we want to indicate to the user that the button should be clicked, we can instead set it to be a pointer:\n\n```julia\nbutton = Button()\nset_cursor!(button, CURSOR_TYPE_POINTER)\n```\n\nThe exact look of each cursor type depends on the user's operating system and UI configuration. To choose a fully custom cursor, we use [`set_cursor_from_image!`](@ref), which takes an `Image`. We will learn more about `Image` in the [chapter dedicated to it](./06_image.md). Until then, this is how we set a cursor from a `.png` file on disk:\n\n```julia\nwidget = # ...\nset_cursor_from_image!(widget, Image(\"/path/to/image.png\"))\n```\n\n---\n\n### Tooltip\n\nEach widget can have a **tooltip**. This is a little window that opens when the cursor hovers over a widget's allocated area for enough time. The exact duration is decided by the user's OS, we do not have control over it. \n\nTooltips are usually a simple text message. We can set the text directly using [`set_tooltip_text!`](@ref):\n\n```julia\nbutton = Button()\nset_tooltip_text!(button, \"Click to Open\")\n```\n![](../assets/widget_tooltip_text.png)\n\nIf we want to use something more complex than just simple text, we can register an arbitrarily complex widget as a tooltip by calling [`set_tooltip_widget!`](@ref). As a matter of style, this widget should not be interactable, though there is no mechanism in place to enforce this.\n\n---\n\nNow that we know the properties shared by all widgets, we can continue onto learning about all the specific widget types. From this point onwards, we can be sure that **all widgets support all properties and signals discussed so far**.\n\n## Window\n\nFor our first widget, we have [`Window`](@ref). Windows are central to any application, as such, `Window` and `Application` are inherently connected. We cannot create a `Window` without an `Application` instance. If all windows are closed, the underlying application usually exists.\n\nWhile windows are widgets, they occupy a somewhat of a special place. `Window` is the only widget that does not have a parent. This is called being **top-level**, nothing is \"above\" the window in the parent-child hierarchy. \n\n`Window` has exactly one child, which we set with `set_child!`, as we have so far already.\n\n### Opening / Closing a Window\n\nWhen we create a `Window` instance, it will be initially hidden. None of its children will be realized or shown, and the user has no way to know that the window exists. A `Window`s lifetime only begins once we call [`present!`](@ref). This opens the window and shows it to the user, realizing all its children. We've seen this in our `main` functions before:\n\n```julia\nmain() do app::Application\n\n    # create the window\n    window = Window(app)\n    \n    # show the window, this realizes all widgets inside\n    present!(window)\nend\n```\n\nAt any point, we can call `close!`, which hides the window. This does not destroy the window permanently unless [`set_hide_on_close!`](@ref) was set to `false` previously, we can `present!` to show the window again. For an application to exit, all its windows only need to be hidden, not permanently destroyed. Therefore, calling `close!` on all windows may cause the application to attempt to exit.\n\n### Close Request\n\n`Window` has three signals, only the latter of which is relevant to us for now.\n\n```@eval\nusing Mousetrap\nMousetrap.@signal_table(Window,\n    activate_default_widget,\n    activate_focused_widget,\n    close_request\n)\n```\n\nWhen the window handler of the user's OS asks the window to close, for example, because the user pressed the \"x\" button, signal `close_request` will be emitted. Its result, of type [`WindowCloseRequestResult`](@ref), determines whether the window does close.\n\n```julia\n# create a window that cannot be closed\nwindow = Window(app)\nconnect_signal_close_request!(window) do self::Window\n    return WINDOW_CLOSE_REQUEST_RESULT_PREVENT_CLOSE\nend\npresent!(window)\n```\n\nIf the signal handler instead returns `WINDOW_CLOSE_REQUEST_RESULT_ALLOW_CLOSE`, the window will close, which is the default behavior. We should never call `close!` from within the signal handler of `closer_request`. Whether the window is closed should only be controlled by the handler's return value.\n\n### Window Properties\n\nOther than its singular child, `Window` has a number of other properties.\n\n#### Title & Header Bar\n\n[`set_title!`](@ref) sets the name displayed in the window's **header bar**, which is the part on top of the content area. By default, this name will be the name of the application. We can choose to hide the title by simply calling `set_title!(window, \"\")`.\n\nBy default, the header bar will show the window title, a minimize-, maximize-, and close-button. We can completely hide the header bar using `set_is_decorated!(window, false)`, which also means the user has no way to move or close the window.\n\n#### Modality & Transience\n\nWhen dealing with multiple windows, we can influence the way two windows interact with each other. Two of these interactions are determined by whether a window is **modal** and whether it is **transient** for another window.\n\nBy setting [`set_is_modal!`](@ref) to true, if the window is revealed, **all other windows of the application will be deactivated**, preventing user interaction with them. This also freezes animations, it essentially pauses all other windows. The most common use-case for this is for dialogs, for example, if the user requests to close the application, it is common to open a small dialog requesting the user to confirm exiting the application. While this dialog is shown, the main window should be disabled and all other processes should halt until a selection is made. This is possible by making the dialog window *modal*. If two modal windows are active at the same time, the user can choose to swap between active windows by clicking a currently inactive window.\n\nUsing [`set_transient_for!`](@ref), we can make sure a window will always be shown in front of another. `set_transient_for!(A, B)` will make it so, while `A` overlaps `B` on the user's desktop, `A` will be shown in front of `B`. \n\n---\n\n## Label\n\nIn contention for being *the* most used widget, `Label`s are important to understand. A [`Label`](@ref) displays static text, meaning it is not interactable. It is initialized as one would expect:\n\n```julia\nlabel = Label(\"text\")\n```\n\nTo change a `Label`s text after initialization, we use [`set_text!`](@ref). This can be any number of lines, `Label` is not just for single-line text. If our text has more than one line, a number of additional formatting options are available.\n\n### Justify Mode\n\n[Justification](https://en.wikipedia.org/wiki/Typographic_alignment) determines how words are distributed along the horizontal axis. There are 5 modes in total, all of which are values of the enum [`JustifyMode`](@ref), set using [`set_justify_mode!`](@ref):\n\n![](../assets/text_justify_left.png)\n\n`JUSTIFY_MODE_LEFT`\n\n![](../assets/text_justify_center.png)\n\n`JUSTIFY_MODE_CENTER`\n\n![](../assets/text_justify_right.png)\n\n`JUSTIFY_MODE_RIGHT`\n\n![](../assets/text_justify_fill.png)\n\n`JUSTIFY_MODE_FILL`\n\nWhere the fifth mode is `JUSTIFY_MODE_NONE`, which arranges all text in exactly one line.\n\n### Wrapping\n\nWrapping determines where a line break is inserted if the linewidth exceeds that of `Label`s allocated area. For wrapping to happen at all, the `JustifyMode` has to be set to anything other than `LABEL_WRAP_MODE_NONE`.\n\nWrapping modes are values of the enum [`LabelWrapMode`](@ref). We set the wrap mode of a `Label` using [`set_wrap_mode!`](@ref).\n\n| `LabelWrapMode` value  | Meaning                                                 | Example                 |\n|------------------------|---------------------------------------------------------|-------------------------|\n| `NONE`                 | no wrapping                                             | `\"humane mousetrap\"`    |\n| `ONLY_ON_WORD`         | line will only be split between two words               | `\"humane\\nmousetrap\"`   |\n| `ONLY_ON_CHAR`         | line will only be split between syllables, adding a `-` | `\"hu-\\nmane mouse-\\ntrap\"` |\n| `WORD_OR_CHAR`         | line will be split between words and/or syllables       | `\"humane\\nmouse-\\ntrap\"`   |\n\nWhere `\\n` is the newline character.\n\n### Ellipsize Mode\n\nIf a line is too long for the available space and wrapping is disabled, **ellipsizing** will take place. The corresponding enum [`EllipsizeMode`](@ref) has four possible values, which we set using [`set_ellipsize_mode!`](@ref).\n\n| `EllipsizeMode` value | Meaning                                                  | Example                     |\n|-----------------------|----------------------------------------------------------|-----------------------------|\n| `NONE`                | text will not be ellipsized                              | `\"Humane mousetrap engineer\"` |\n| `START`               | starts with `...`, showing only the last few words       | `\"...engineer\"`               |\n| `END`                 | ends with `...`, showing only the first few words        | `\"Humane mousetrap...\"`       |\n| `MIDDLE`              | `...` in the center, shows start and beginning           | `\"Humane...engineer\"`         |\n\n### Markup\n\nLabels support **markup**, which allows users to change properties about individual words or characters in a way similar to text formatting in HTML. Markup in Mousetrap uses [Pango attributes](https://docs.gtk.org/Pango/pango_markup.html), which allows for styles including the following:\n\n| Tag          | Example                  | Result                  |\n|--------------|--------------------------|-------------------------|\n| `b`          | `<b>bold</b>`            | <b>bold</b>             |\n| `i`          | `<i>italic</i>`          | <i>italic</i>           |\n| `u`          | `<u>underline</u>`       | <u>underline</u>        |\n| `s`          | `<s>strikethrough</s>`   | <s>strike-through</s>    |\n| `tt`         | `<tt>inline_code</tt>`   | <tt>inline_code</tt>    |\n| `small`      | `<small>small</small>`   | <small>small</small>    |\n| `big`        | `<big>big</big>`         | <h3>big</h3>            |\n| `sub`        | `x<sub>subscript</sub>`  | x<sub>subscript</sub>   |\n| `sup`        | `x<sup>superscript</sup>` | x<sup>superscript</sup> |\n| `&#` and `;` | `&#129700;`              | 🪤                      | \n\nWhere in the last row, we used the [decimal html code](https://www.compart.com/en/unicode/U+1FAA4) for the Mousetrap emoji provided by unicode.\n\n!!! warning\n    Pango only accepts the **decimal** code, not *hexadecimal*. For example, the Mousetrap emoji has the decimal code `129700`, while its hexadecimal code is `x1FAA4`. \n    To use this emoji in text, we choose `&#129700;`, **not** `&#x1FAA4;`. The latter will not work.\n\n!!! note \n    All `<`, `>` will be parsed as style tags, regardless of whether they are escaped. To display them as characters, we use `&lt;` \n    (less-than) and `&gt;` (greater-than) instead of `<` and `>`. For example, we would write `x < y` as `\"x &lt; y\"`.\n\nPango also supports colors, different fonts, text direction, and more. For these, we can [consult the Pango documentation](https://docs.gtk.org/Pango/pango_markup.html) directly.\n\n```julia\nlabel = Label(\"&lt;tt&gt;01234&lt;/tt&gt; is rendered as <tt>01234</tt>\")\nset_child!(window, label)\n```\n![](../assets/label_example.png)\n\n---\n\n## Box\n\n[`Box`](@ref) is a multi-widget container that aligns its children horizontally or vertically, depending on **orientation**. A number of widgets are orientable like this, which means they support the functions [`set_orientation!`](@ref) and [`get_orientation`](@ref), which take / return an enum value of [`Orientation`](@ref):\n\n| `Orientation` Value       | Meaning                                  |\n|---------------------------|------------------------------------------|\n| `ORIENTATION_HORIZONTAL`  | Oriented left-to-right, along the x-axis |\n| `ORIENTATION_VERTICAL`    | Oriented top-to-bottom, along the y-axis |\n\nTo add widgets to the `Box`, we use `push_front!`, `push_back!` and `insert_after!`:\n\n```julia\nleft = Label(\"LEFT\")\nset_margin_start!(left, 10)\n\ncenter = Label(\"CENTER\")\nset_margin_horizontal!(center, 10)\n\nright = Label(\"RIGHT\")\nset_margin_end!(right, 10)\n\n# create a horizontal box\nbox = Box(ORIENTATION_HORIZONTAL)\n\n# add `left` to the start\npush_front!(box, left)\n\n# add `right to the end\npush_back!(box, right)\n\n# insert `center` after `left`\ninsert_after!(box, center, left)\n\n# add box to window\nset_child!(window, box)\n```\n\n![](../assets/box_example.png)\n\nIn this example, we use margins to add a 10px gap in between each child. This can be done more succinctly using the boxe's own **spacing** property. By setting [`set_spacing!`](@ref) to `10`, it will automatically insert a 10 pixel gap in between any two children, in addition to the children's regular margin.\n\n[`hbox`](@ref) and [`vbox`](@ref) are two convenience functions that take any number of widgets and return a horizontal or vertical box with those widgets already inserted. Using this, and spacing instead of margins, we can write the above as two lines:\n\n```julia\nbox = hbox(Label(\"LEFT\"), Label(\"CENTER\"), Label(\"RIGHT\"))\nset_spacing!(box, 10)\nset_child!(window, box)\n```\n---\n\n## CenterBox\n\n[`CenterBox`](@ref) is an orientable container that has exactly three children. `CenterBox` prioritizes keeping the designated center-child centered at all costs, making it a good choice when symmetry is desired.\n\nWe use [`set_start_child!`](@ref), [`set_center_child!`](@ref), and [`set_end_child!`](@ref) to insert a child widget in the corresponding position:\n\n```julia\ncenter_box = CenterBox(ORIENTATION_HORIZONTAL)\nset_start_child!(center_box, Label(\"start\"))\nset_center_child!(center_box, Button())\nset_end_child!(center_box, Label(\"end\"))\n```\n\n![](../assets/center_box.png)\n\nUsing `CenterBox`s constructor, we can also write the above as a one-liner:\n\n```julia\ncenter_box = CenterBox(ORIENTATION_HORIZONTAL, Label(\"start\"), Button(), Label(\"end\"))\n```\n\n---\n\n## FlowBox\n\nThird of the `Box` relatives, we have [`FlowBox`](@ref). This widget is similar to `Box`, except that it will **redistribute** its children along more than one row (or column, if vertical) depending on the available width (or height) of the `FlowBox`. This is useful for situations where we want to group a number of widgets in a way that does not impact resizability.\n\n```julia\nflow_box = FlowBox(ORIENTATION_VERTICAL)\nfor i in 1:7 \n    push_back!(flow_box, Label(string(i)))\nend\n```\n\n![](../assets/flow_box.png)\n\n---\n\n## HeaderBar\n\nThe visual element on top of a window, which usually contains the window's title along with the title buttons, is its own separate widget called [`HeaderBar`](@ref). All `Window` instances come with their own `HeaderBar`, which we can access using [`get_header_bar`](@ref). It's rarely necessary to create a `HeaderBar` on our own.\n\nEach `HeaderBar` has a title widget, which will usually be a `Label`, along with two areas for widgets to be inserted. To insert widgets left of the title, we use [`push_front!](@ref), while inserting widgets right of the title is done using [`push_back!`](@ref).\n\nEach `HeaderBar` can have a close-, minimize- and maximize- button, all of which are optional. To specify which buttons should appear and in what order, we use [`set_layout!`](@ref). This function takes a string, which has the following components:\n\n+ `close`\n+ `minimize`\n+ `maximize`\n\nEach element is separated using `,`. The string has to furthermore contain a `:`. Each element before the `:` will appear left of the title, while elements after `:` will appear right of the title. Note that this just affects the close-, minimize-, and maximize buttons, any widget inserted using `push_front!` or `push_back!` is unaffected.\n\nA few examples:\n\n| `set_layout!` string | close button | minimize button | maximize button |\n|----------------------|--------------|----------------|-----------------|\n| `:minimize,maximize,close` | right of title | right of title | right of title |\n| `close:`             | left of title | hidden        | hidden |\n| `minimize:maximize`  | hidden | left of title | right of title |\n| `:`                  | hidden | hidden | hidden |\n\nFor example, to create a `HeaderBar` that has no elements, meaning no title and none of the title buttons, we would do the following:\n\n```julia\nwindow = Window(app)\n\n# access windows header bar instance\nheader_bar = get_header_bar(window)\n\n# hide title buttons\nset_layout!(header_bar, \":\")\n\n# hide default title by replacing it with an empty label\nset_title_widget!(header_bar, Label(\"\"))\n```\n![](../assets/header_bar_blank.png)\n\n\n---\n\n## Separator\n\nPerhaps the simplest widget is [`Separator`](@ref). It simply fills its allocated area with a solid color:\n\n```julia\nseparator = Separator()\nset_margin!(separator, 20)\nset_expand!(separator, true)\nset_child!(window, separator)\n```\n\n![](../assets/separator_example.png)\n\nThis widget is used as a background to another widget, to fill empty space, or as an element visually separating two sections. \n\nOften, we want to have the separator be a specific thickness. This can be accomplished using size-hinting. For example, to draw a horizontal line similar to the `<hr>` element in HTML, we would do the following:\n\n```julia\nhr = Separator()\nset_expand_horizontally!(hr, true)\nset_expand_vertically!(hr, false)\nset_size_request!(hr, Vector2f(\n    0,  # width: any \n    3   # height: exactly 3 px\n));\n```\n\nThis will render as a line that has a height of `3` px at all times but will assume the entire width of its parent.\n\n---\n\n## ImageDisplay\n\n[`ImageDisplay`](@ref) is used to display static images.\n\nAssuming we have an image at the absolute path `/assets/image.png`, we can create an `ImageDisplay` like so:\n\n```julia\nimage_display = ImageDisplay()\ncreate_from_file!(image_display, \"/assets/image.png\")\n\n# equivalent to\nimage_display = ImageDisplay(\"/assets/image.png\")\n```\n\nThe following image formats are supported by `ImageDisplay`:\n\n| Format Name             | File Extensions            |\n|-------------------------|----------------------------|\n| PNG                     | `.png`                     |\n| JPEG                    | `.jpeg` `.jpe` `.jpg`      |\n| JPEG XL image           | `.jxl`                     |\n| Windows Metafile        | `.wmf` `.apm`              |\n| Windows animated cursor | `.ani`                     |\n| BMP                     | `.bmp`                     |\n| GIF                     | `.gif`                     |\n| MacOS X icon            | `.icns`                    |\n| Windows icon            | `.ico` `.cur`              |\n| PNM/PBM/PGM/PPM         | `.pnm` `.pbm` `.pgm` `.ppm` |\n| QuickTime               | `.qtif` `.qif`             |\n| Scalable Vector Graphics | `.svg` `.svgz` `.svg.gz`   |\n| Targa                   | `.tga` `.targa`            |\n| TIFF                    | `.tiff` `.tif`             |\n| WebP                    | `.webp`                    |\n| XBM                     | `.xbm`                     |\n| XPM                     | `.xpm`                     |\n\nAfter realization, we cannot change the contents of `ImageDisplay` directly. If the file on disk changes, `ImageDisplay` remains unchanged. If we want to update `ImageDisplay`, we need to call [`create_from_file!`](@ref) manually again.\n\n---\n\n## Button\n\nFamiliar from previous chapters, [`Button`](@ref) is commonly used to trigger behavior.\n\nIt has one signal, which is emitted when the button is activated:\n\n```@eval\nusing Mousetrap\nMousetrap.@signal_table(Button,\n    clicked\n)\n```\n\nWe can manually emit this signal using `emit_signal_clicked`, or by calling `activate!` on the button instance. The latter will also play the animation associated with a user physically clicking the button.\n\n`Button` has a single child that is used as its label. We set it using `set_child!`. Other than this child widget, we can customize the look of a button further. `set_has_frame!` will make all graphical elements of the button other than its label invisible, while `set_is_circular!` changes the button from rectangular to fully rounded:\n\n![](../assets/button_types.png)\n\n!!! details \"How to generate this image\"\n    ```julia\n    using Mousetrap\n    main() do app::Application\n        window = Window(app)\n    \n        normal = Button()\n        set_child!(normal, Label(\"01\"))\n    \n        no_frame = Button()\n        set_has_frame!(no_frame, false)\n        set_child!(no_frame, Label(\"02\"))\n    \n        circular = Button()\n        set_is_circular!(circular, true)\n        set_child!(circular, Label(\"03\"))\n    \n        box = CenterBox(ORIENTATION_HORIZONTAL, normal, no_frame, circular)\n        set_margin!(box, 75)\n    \n        set_child!(window, box)\n        pesent!(window)\n    end\n    ```\n\nWhere the above-shown buttons have the following properties:\n\n| Button | `set_has_frame!` | `set_is_circular!`|\n|--------|------------------|-------------------|\n| 01     | `true`             | `false`             |\n| 02     | `false`            | `false`             |\n| 03     | `true`             | `true`              |\n\n---\n\n## ToggleButton\n\n[`ToggleButton`](@ref) is a specialized form of `Button`. It supports most of `Button`s methods / signals, including `set_child!`, `set_has_frame!`, `set_is_circular!`, and signal `clicked`.\n\nUnique to `ToggleButton` is that, if clicked, the button will **remain pressed**. When clicked again, it returns to being unpressed. Anytime the state of the `ToggleButton` changes, signal `toggled` will be emitted. In this way, `ToggleButton` can be used to track a boolean state.\n\n```@eval\nusing Mousetrap\nMousetrap.@signal_table(ToggleButton,\n    toggled,\n    clicked\n)\n```\n\nTo check whether the button is currently toggled, we use `get_is_active`, which returns `true` if the button is currently depressed, `false` otherwise.\n\n```julia\ntoggle_button = ToggleButton()\nconnect_signal_toggled!(toggle_button) do self::ToggleButton\n  println(\"state is now: $(get_is_active(self))\")\nend\nset_child!(window, toggle_button)\n```\n\n---\n\n## CheckButton\n\n[`CheckButton`](@ref) is very similar to `ToggleButton` in function - but not in appearance. `CheckButton` is an empty box in which a checkmark appears when it is toggled. Just like before, we query whether it is pressed by calling `get_is_active`. \n\n```@eval\nusing Mousetrap\nMousetrap.@signal_table(ToggleButton,\n    toggled\n)\n```\n\n`CheckButton` can be in one of **three** states, which are represented by the enum [`CheckButtonState`](@ref). The button can either be `CHECK_BUTTON_STATE_ACTIVE`, `CHECK_BUTTON_STATE_INACTIVE`, or `CHECK_BUTTON_STATE_INCONSISTENT`. This changes the appearance of the button:\n\n![](../assets/check_button_states.png)\n\n!!! details \"How to generate this image\"\n    ```julia\n    using Mousetrap\n    main() do app::Application\n    \n        window = Window(app)\n    \n        active = CheckButton()\n        set_state!(active, CHECK_BUTTON_STATE_ACTIVE)\n        active_box = vbox(active, Label(\"active\"))\n    \n        inconsistent = CheckButton()\n        set_state!(inconsistent, CHECK_BUTTON_STATE_INCONSISTENT)\n        inconsistent_box = vbox(inconsistent, Label(\"inconsistent\"))\n    \n        inactive = CheckButton()\n        set_state!(inactive, CHECK_BUTTON_STATE_INACTIVE)\n        inactive_box = vbox(inactive, Label(\"inactive\"))\n    \n        for button in [active, inconsistent, inactive]\n            set_horizontal_alignment!(button, ALIGNMENT_CENTER)\n        end\n    \n        box = CenterBox(ORIENTATION_HORIZONTAL, active_box, inconsistent_box, inactive_box)\n        set_margin!(box, 75)\n    \n        set_child!(window, box)\n        present!(window)\n    end\n    ```\n\nNote that `get_is_active` will only return `true` if the current state is specifically `CHECK_BUTTON_STATE_ACTIVE`. `toggled` is emitted whenever the state changes, regardless of which state the `CheckButton` was in.\n\n---\n\n## Switch\n\nAs the last widget intended to convey a boolean state to the user, we have [`Switch`](@ref), which has an appearance similar to a light switch. `Switch` does not emit `toggled`, instead, we connect to the `switched` signal, which is emitted anytime the switch's internal state changes:\n\n```@eval\nusing Mousetrap\nMousetrap.@signal_table(Switch,\n    switched\n)\n```\n\n![](../assets/switch_states.png)\n\n!!! details \"How to generate this image\"\n    ```julia\n    main() do app::Application\n    \n        window = Window(app)\n    \n        active = Switch()\n        set_is_active!(active, true)\n        active_box = vbox(active, Label(\"active\"))\n    \n        inactive = Switch()\n        set_is_active!(inactive, false)\n        inactive_box = vbox(inactive, Label(\"inactive\"))\n    \n        for switch in [active, inactive]\n            set_horizontal_alignment!(switch, ALIGNMENT_CENTER)\n            set_margin!(switch, 10)\n        end\n    \n        box = CenterBox(ORIENTATION_HORIZONTAL)\n        set_start_child!(box, active_box)\n        set_end_child!(box, inactive_box)\n        set_margin!(box, 75)\n    \n        set_child!(window, box)\n        present!(window)\n    end\n    ```\n---\n\n---\n\n## Adjustment\n\nFrom widgets conveying a boolean state, we'll now move on to widgets conveying a discrete number. These let the user choose a value from a **range**, which, in Mousetrap, is represented by a signal emitter called [`Adjustment`](@ref).\n\n`Adjustment` has four properties:\n\n+ `lower`: lower bound of the range\n+ `upper`: upper bound of the range\n+ `increment`: step increment\n+ `value`: current value, in `[lower, upper]`\n\nFor example, the following `Adjustment`:\n\n```julia\nadjustment = Adjustment(\n    1,      # value\n    0,      # lower\n    2,      # upper\n    0.5     # step increment\n)\n```\nExpresses the range `{0, 0.5, 1, 1.5, 2}`, with `1` being the value on initialization.\n\nWe usually do not need to create our own `Adjustment`, rather, it is provided by a number of widgets that use it to select their value. Notably, if the `Adjustment` is modified, that widget's appearance is modified, and if the widget is modified, the adjustment is, too. \n\n`Adjustment` has two signals:\n\n```@eval\nusing Mousetrap\nMousetrap.@signal_table(Adjustment,\n    value_changed,\n    properties_changed\n)\n```\n\nWe can connect to `value_changed` to monitor the `value` property of an `Adjustment` (and thus whatever widget is controlled by it), while `properties_changed` is emitted when one of `upper`, `lower` or `step increment` changes.\n\n---\n\n## SpinButton\n\n`SpinButton` is used to pick an exact value from a range. The user can click the rectangular area and manually enter a value using the keyboard, or they can increase or decrease the current value by the step increment of the widgets `Adjustment` by pressing the plus or minus button.\n\nWe supply the properties of the range underlying the `SpinButton` to its constructor:\n\n```julia\n# create SpinButton with range [0, 2] and increment 0.5\nspin_button = SpinButton(0, 2, 0.5)\n```\n\n![](../assets/spin_button.png)\n\n!!! details \"How to generate this image\"\n    ```julia\n    main() do app::Application\n        window = Window(app)\n    \n        horizontal = SpinButton(0, 2, 0.5)\n        set_value!(horizontal, 1)\n    \n        # Add invisible separator buffers above and below spin button for better symmetry\n        horizontal_buffer = CenterBox(\n            ORIENTATION_VERTICAL, \n            Separator(; opacity = 0.0),\n            horizontal,\n            Separator(; opacity = 0.0)\n        )\n    \n        vertical = SpinButton(0, 2, 0.5)\n        set_value!(vertical, 1)\n        set_orientation!(vertical, ORIENTATION_VERTICAL)\n    \n        box = CenterBox(ORIENTATION_HORIZONTAL)\n        set_start_child!(box, horizontal_buffer)\n        set_end_child!(box, vertical)\n    \n        set_child!(window, box)\n        present!(window)\n    end             \n    ```\n\nWe set and access any property of spin button using `get_value`, `set_value!`, `get_lower`, `set_lower!`, etc. These work exactly as if we were modifying the underlying `Adjustment`, which we can also obtain using `get_adjustment`.\n\nAlong with being *orientable*, `SpinButton` has two signals, one of which, `value_changed`, we recognize from `Adjustment`. To react to the user changing the value of a `SpinButton`, we would do the following:\n\n```julia\nspin_button = SpinButton(0, 2, 0.5)\nconnect_signal_value_changed!(spin_button) do self::SpinButton\n    println(\"Value is now: $(get_value(self))\")\nend\n```\n\nThe other signal is `wrapped`, which is emitted when [`set_should_wrap!`](@ref) is set to `true` and the `SpinButton`'s value under- or overflows.\n\n---\n\n## Scale\n\n![](../assets/scale_no_value.png)\n\n!!! details \"How to generate this image\"\n    ```julia\n    main() do app::Application\n        window = Window(app)\n\n        horizontal = Scale(0, 2, 0.5)\n        set_orientation!(horizontal, ORIENTATION_HORIZONTAL)\n        set_value!(horizontal, 1)\n        set_size_request!(horizontal, Vector2f(200, 0))\n\n        vertical = Scale(0, 2, 0.5)\n        set_orientation!(vertical, ORIENTATION_VERTICAL)\n        set_value!(vertical, 1)\n        set_size_request!(vertical, Vector2f(0, 200))\n\n        box = CenterBox(ORIENTATION_HORIZONTAL)\n        set_start_child!(box, horizontal)\n        set_end_child!(box, vertical)\n\n        set_margin_horizontal!(box, 75)\n        set_margin_vertical!(box, 40)\n\n        set_child!(window, box)\n        present!(window)\n    end\n    ```\n\n[`Scale`](@ref), just like `SpinButton`, is a widget that allows a user to choose a value from the underlying `Adjustment`. This is done by click-dragging the knob of the scale or clicking anywhere on its rail. In this way, it is usually harder to pick an exact decimal value on a `Scale` as opposed to a `SpinButton`. We can aid in this task by displaying the exact value next to the scale, which is enabled with [`set_should_draw_value!`](@ref):\n\n![](../assets/scale_with_value.png)\n\n!!! details \"How to generate this image\"\n    ```julia\n    main() do app::Application\n        window = Window(app)\n\n        window = Window(app)\n\n        horizontal = Scale(0, 2, 0.5)\n        set_orientation!(horizontal, ORIENTATION_HORIZONTAL)\n        set_value!(horizontal, 1)\n        set_size_request!(horizontal, Vector2f(200, 0))\n        set_should_draw_value!(horizontal, true)\n\n        vertical = Scale(0, 2, 0.5)\n        set_orientation!(vertical, ORIENTATION_VERTICAL)\n        set_value!(vertical, 1)\n        set_size_request!(vertical, Vector2f(0, 200))\n        set_should_draw_value!(vertical, true)\n\n        box = CenterBox(ORIENTATION_HORIZONTAL)\n        set_start_child!(box, horizontal)\n        set_end_child!(box, vertical)\n\n        set_margin_horizontal!(box, 75)\n        set_margin_vertical!(box, 40)\n\n        set_child!(window, box)\n        present!(window)\n    end\n    ```\n\n`Scale` supports most of `SpinButton`'s functions, including querying information about its underlying range, setting the orientation, and signal `value_changed`:\n\n```julia\nscale = Scale(0, 2, 0.5)\nconnect_signal_value_changed!(scale) do self::Scale\n    println(\"Value is now: $(get_value(self))\")\nend\n```\n---\n\n## LevelBar\n\n[`LevelBar`](@ref) is used to display a fraction to indicate the level of something, for example, the volume of a playback device. This widget is static, it cannot be interacted with.\n\nTo create a `LevelBar`, we need to specify the minimum and maximum value of the range we wish to display. We can then set the current value using `set_value!`. The resulting fraction is computed automatically, based on the upper and lower limit we supplied to the constructor:\n\n```julia\n# create a LevelBar for range [0, 2]\nlevel_bar = LevelBar(0, 2)\nset_value!(level_bar, 1.0) # set to 50%\n```\n\nUnlike the previous widgets, `LevelBar` does not have a step increment.\n\nOnce the bar reaches 75%, it changes color:\n\n![](../assets/level_bar.png)\n\n!!! details \"How to generate this image\"\n    ```julia\n    main() do app::Application\n        window = Window(app)\n\n        box = Box(ORIENTATION_VERTICAL)\n        set_spacing!(box, 10)\n        set_margin!(box, 10)\n\n        n_bars = 5\n        for i in 1:n_bars\n            fraction = Float32(i) / n_bars\n            label = Label(string(Int64(round(fraction * 100))) * \"%\")\n            set_size_request!(label, Vector2f(50, 0))\n\n            bar = LevelBar(0, 1)\n            set_value!(bar, fraction)\n            set_expand_horizontally!(bar, true)\n\n            row_box = Box(ORIENTATION_HORIZONTAL)\n            set_spacing!(box, 10)\n            push_back!(row_box, label)\n            push_back!(row_box, bar)\n\n            push_back!(box, row_box)\n        end\n\n        set_child!(window, box)\n        present!(window)\n    end\n    ```\n\n`LevelBar` also supports displaying a discrete value, in which case it will be drawn segmented. To enable this, we set [`set_mode!`](@ref) to `LEVEL_BAR_DISPLAY_MODE_DISCRETE`, as opposed to `LEVEL_BAR_MODE_CONTINUOUS`, which is the default.\n\n---\n\n## ProgressBar\n\nSimilarly to `LevelBar`, [`ProgressBar`](@ref) communicates a fraction to the user, which is frequently used to show the user how much of a task is currently completed.\n\n`ProgressBar` only expresses values in `[0, 1]`, and [`set_fraction!`](@ref) will only accept values in this range.\n\nUsing `set_show_text!`, we can make it so the current percentage is drawn along with the progress bar, or we can draw a custom label using `set_text!`\n\n![](../assets/progress_bar.png)\n\n!!! details \"How to generate this image\"\n    ```julia\n    main() do app::Application\n        window = Window(app)\n\n        box = Box(ORIENTATION_VERTICAL)\n    \n        progress_bar = ProgressBar()\n        set_fraction!(progress_bar, 0.47)\n        set_vertical_alignment!(progress_bar, ALIGNMENT_CENTER)\n        set_expand!(progress_bar, true)\n        set_show_text!(progress_bar, true)\n        set_margin!(progress_bar, 10)\n        \n        set_child!(window, progress_bar)\n        present!(window)\n    end\n    ```\n---\n\n## Spinner\n\nTo signal progress when we do not have an exact fraction, we use [`Spinner`](@ref) which is a small spinning icon. Once we set [`set_is_spinning!`](@ref) to `true`, a spinning animation will play, indicating to the user that work is being done.\n\n![](../assets/spinner.png)\n\n!!! details \"How to generate this image\"\n    ```julia\n    main() do app::Application\n\n        window = Window(app)\n\n        spinner = Spinner()\n        set_is_spinning!(spinner, true)\n\n        set_child!(window, spinner)\n        present!(window)\n    end\n     ```\n\n---\n\n## Entry\n\nText entry is central to many applications. Mousetrap offers two widgets that allow the user to type freely. [`Entry`](@ref) is the widget of choice for **single-line** text entry.\n\nThe entries currently displayed text is stored in an internal text buffer. We can freely access or modify the buffer's content with [`get_text`](@ref) and [`set_text!`](@ref).\n\nWhile we could control the size of an `Entry` using size-hinting, a better way is [`set_max_width_chars!`](@ref), which resizes the entry such that its width is enough to fit a certain number of characters into its area. This automatically respects the system font and font size.\n\n`Entry` supports \"password mode\", in which each character typed is replaced with a dot. This is to prevent a third party in the real world looking at a user's screen and seeing what they are typing. \n\nTo enter password mode, we set [`set_text_visible!`](@ref) to `false`. Note that this does not encrypt the text buffer in memory, it is a purely visual change.\n\n![](../assets/entry.png)\n\n!!! details \"How to generate this image\"\n    ```julia\n    main() do app::Application\n\n        window = Window(app)\n\n        clear = Entry()\n        set_text!(clear, \"text\")\n\n        password = Entry()\n        set_text!(password, \"text\")\n        set_text_visible!(password, false)\n\n        box = vbox(clear, password)\n        set_spacing!(box, 10)\n        set_margin_horizontal!(box, 75)\n        set_margin_vertical!(box, 40)\n\n        set_child!(window, box)\n        present!(window)\n    end\n    ```\n\nLastly, `Entry` is **activatable**, when the user presses the enter key while the cursor is inside the entries text area, it will emit signal `activate`. Its other signal, `text_changed`, is emitted whenever the internal text buffer changes: \n\n```@eval\nusing Mousetrap\nMousetrap.@signal_table(Entry,\n    activate,\n    text_changed\n)\n```\n\nWe would therefore connect a handler that reacts to the text of an entry changing like so:\n\n```julia\nentry = Entry()\nset_text!(entry, \"Write here\")\nconnect_signal_text_changed!(entry) do self::Entry\n    println(\"text is now: $(get_text(self))\")\nend\n```\n\nNote that the user cannot insert a newline character using the enter key. `Entry` should exclusively be used for text prompts that have **no line breaks**. For multi-line text entry, we should use the next widget instead.\n\n## TextView\n\n[`TextView`](@ref) is the multi-line equivalent of `Entry`. It supports several basic text-editor features, including **undo / redo**, which are triggered by the user pressing `Control + Z` and `Control + Y` respectively. We as developers can also trigger this behavior manually with [`undo!`](@ref) / [`redo!`](@ref).\n\nMuch like `Label`, we can set how the text aligns horizontally using `set_justify_mode!`. To further customize how text is displayed, we can choose the **internal margin**, which is the distance between the frame of the `TextView` and the text inside of it. `set_left_margin!`, `set_right_margin!`, `set_top_margin!` and `set_bottom_margin!` allow us to choose these values freely.\n\n`TextView` does **not** have the `activate` signal, pressing enter while the cursor is inside the widget will simply create a new line. Instead, it only has signal `text_changed`, which behaves identically to that of `Entry`:\n\n```@eval\nusing Mousetrap\nMousetrap.@signal_table(TextView,\n    text_changed\n)\n```\n\n```julia\ntext_view = TextView()\nset_text!(text_view, \"Write here\")\nconnect_signal_text_changed!(text_view) do self::TextView\n    println(\"text is now: $(get_text(self))\")\nend\n```\n\n---\n\n## Dropdown\n\nWe sometimes want users to be able to pick a value from a **set list of values**, which may or may not be numeric. [`DropDown`](@ref) allows for this. If clicked, a small popup presents the user with a list of items. When clicking one of these items, that item becomes the active item.\n\nWe add an item using `push_back!`, which takes a string that will be used as the item's label:\n\n```julia\ndropdown = DropDown()\nitem_01_id = push_back!(dropdown, \"Item #01\")\nitem_02_id = push_back!(dropdown, \"Item #02\")\nitem_03_id = push_back!(dropdown, \"Item #03\")\n```\n\n![](../assets/dropdown_simple.png)\n\n`push_back!` returns the internal ID of the item. We should keep track of this ID, as it will be used to identify the currently selected item when using [`get_selected`](@ref).\n\nIf we do lose track of the ID, we can always retrieve it using [`get_item_at`](@ref), which returns the ID of the item at a given position.\n\n`push_back!`, and its equivalents `push_front!` and `insert_at!`, provide a method that also takes a *callback*. This callback will be invoked when the item is selected. It is required to have the signature:\n\n```\n(::DropDown, [::Data_t]) -> Nothing\n```\n\n```julia\ndropdown = DropDown()\npush_back!(dropdown, \"Item #01\") do self::DropDown\n    println(\"Item #01 selected\")\nend\npush_back!(dropdown, \"Item #02\") do self::DropDown\n    println(\"Item #03 selected\")\nend\npush_back!(dropdown, \"Item #03\") do self::DropDown\n    println(\"Item #03 selected\")\nend\n```\n\nThis gives us a better mechanism for keeping track of which item is currently selected. Instead of querying the `DropDown` using `get_selected` and reacting to its result, we should instead register a callback using this method, in a similar way to using signals.\n\nLastly, sometimes we want a different label for when an item is selected, and for when the user opens the menu to select an item. For this situation, `push_back!` offers a method that lets us specify the label widgets separately:\n\n```julia\ndropdown = DropDown()\npush_back!(dropdown,\n    Label(\"Item #01\"),  # Widget displayed in dropdown menu\n    Label(\"01\")         # Widget displayed when item is selected\n)\n\npush_back!(dropdown, Label(\"Item #02\"), Label(\"02\"))\npush_back!(dropdown, Label(\"Item #03\"), Label(\"03\"))\n```\n\n![](../assets/dropdown_separate.png)\n\nWhere we had to first create a `Label` instance, then use it as the label widget, as this method of `push_back!` takes any two *widgets*, as opposed to just strings. This gives us full flexibility with how we want the dropdown to be displayed. This method, along with all methods of `push_front!` and `insert_at!`, also supports adding a callback as the first argument, which behaves exactly as before.\n\n---\n\n## Frame\n\n[`Frame`](@ref) is a purely cosmetic widget that displays its singular child in a frame with a small border and rounded corners:\n\n![](../assets/frame.png)\n\n!!! details \"How to generate this image\"\n    ```julia\n    main() do app::Application\n\n        window = Window(app)\n\n        left = Separator()\n        right = Separator()\n\n        for separator in [left, right]\n            set_size_request!(separator, Vector2f(50, 50))\n            set_expand!(separator, false)\n        end\n\n        box = CenterBox(ORIENTATION_HORIZONTAL)\n        set_start_child!(box, left)\n        set_end_child!(box, Frame(right))\n\n        set_margin_horizontal!(box, 75)\n        set_margin_vertical!(box, 40)\n    \n        set_child!(window, box)\n        present!(window)\n    end\n    ```\n\nUsing [`set_label_widget!`](@ref), we can furthermore choose a widget to be displayed above the child widget of the frame. This will usually be a `Label`, though `set_label_widget!` accepts any kind of widget.\n\n`Frame` is rarely necessary, but will make GUIs seem more aesthetically pleasing and polished. \n\n---\n\n## AspectFrame\n\nNot to be confused with `Frame`, [`AspectFrame`](@ref) adds no graphical element to its singular child. Instead, the widget added with `set_child!` will be forced to allocate a size that conforms to a specific **aspect ratio**. That is, its width-to-height ratio will stay constant, regardless of the size of its parent. If expansion is enabled, the `AspectFrame` will still try to expand to the maximum size that still fulfills the aspect ratio requirement.\n\nWe choose the aspect ratio in `AspectFrame`s constructor, though we can later adjust it using [`set_ratio!`](@ref). Both of these functions accept a floating point ratio calculated as `width / height`. For example, if we want to force a widget to keep an aspect ratio of 4:3, we would do:\n\n```julia\nchild_widget = # ...\naspect_frame = AspectFrame(4.0 / 3.0)\nset_child!(aspect_frame, child_widget);\n```\n\n---\n\n## ClampFrame\n\nUsing size-hinting, we can control the *minimum* size of a widget. No widget property lets us control the *maximum size*, however. For this, we need [`ClampFrame`](@ref), a widget that constrains its singular child to never exceed the given maximum width, or height if the frame's orientation is `ORIENTATION_VERTICAL`.\n\nWe choose the maximum size in pixels during construction, or using [`set_maximum_size!`](@ref):\n\n```julia\nchild_widget = # ...\nwidth_clamp_frame = ClampFrame(150, ORIENTATION_HORIZONTAL)\nheight_clamp_frame = ClampFame(150, ORIENTATION_VERTICAL)\n\nset_child!(width_clamp_frame, child_widget)\nset_child!(height_clamp_frame, width_clamp_frame)\n```\n\nIn which we use two nested `ClampFrame`s, such that `child_widget` can never exceed `150px` for both its width and height.\n\n---\n\n\n## Overlay\n\nSo far, all widget containers have aligned their children such that they do not overlap. In cases where we do want this to happen, for example, if we want to render one widget in front of another, we have to use [`Overlay`](@ref).\n\n`Overlay` has one \"base\" widget, which is at the conceptual bottom of the overlay. It is set using `set_child!`. We can then add any number of widgets \"on top\" of the base widget using [`add_overlay!`](@ref):\n\n![](../assets/overlay_buttons.png)\n\n!!! details \"How to generate this image\"\n    ```julia\n    main() do app::Application\n\n        window = Window(app)\n\n        lower = Button()\n        set_horizontal_alignment!(lower, ALIGNMENT_START)\n        set_vertical_alignment!(lower, ALIGNMENT_START)\n\n        upper = Button()\n        set_horizontal_alignment!(upper, ALIGNMENT_END)\n        set_vertical_alignment!(upper, ALIGNMENT_END)\n\n        overlay = Overlay()\n        set_child!(overlay, lower)\n        add_overlay!(overlay, upper)\n\n        set_child!(window, AspectFrame(1, overlay))\n        present!(window)\n    end\n    ```\n\nWhere the position and size of overlayed widgets depend on their expansion and alignment properties.\n\nBy default, `Overlay` will allocate exactly as much space as the base widget (set with `set_child!`) does. If one of the overlaid widgets takes up more space than the base widget, it will be truncated. We can avoid this by supplying a second argument to `add_overlay!`, which is a boolean keyword argument indicating whether the overlay widget should be included in the entire container's size allocation. That is, if the overlaid widget is larger than the base widget, should the `Overlay` resize itself such that the entire overlaid widget is visible:\n\n```julia\nadd_overlay!(overlay, overlaid_widge; include_in_measurement = true)\n```\n\n---\n\n## Paned\n\n[`Paned`](@ref) is a widget that always has exactly two children. Between the two children, a visual barrier is drawn. The user can click on this barrier and drag it horizontally or vertically, depending on the orientation of the `Paned`. This gives the user the option to resize how much of a shared space the two widgets allocate.\n\n`Paned` is orientable. Depending on its orientation, `set_start_child!` and  `set_end_child!` add a widget to the corresponding side.\n\n![](../assets/paned.png)\n\n!!! details \"How to generate this image\"\n    ```julia\n    function generate_child(label::String)\n        out = Frame(Overlay(Separator(), Label(label)))\n        set_margin!(out, 10)\n        return out\n    end\n\n    main() do app::Application\n\n        window = Window(app)\n\n        paned = Paned(ORIENTATION_HORIZONTAL)\n        set_start_child!(paned, generate_child(\"Left\"))\n        set_end_child!(paned, generate_child(\"Right\"))\n\n        set_start_child_shrinkable!(paned, true)\n        set_end_child_shrinkable!(paned, true)\n\n        set_child!(window, paned)\n        present!(window)\n    end\n    ```\n\n`Paned` has two per-child properties: whether a child is **resizable** and whether it is **shrinkable**.\n\nResizable means that if the `Paned` changes size, the allocated area of its child should resize accordingly.\n\nShrinkable sets whether the side of the `Paned` can be made smaller than the allocated size of that side's child widget. If set to `true`, the user can drag the `Paned`s barrier, such that one of the widgets is partially or completely hidden:\n\n```julia\nset_start_child_shrinkable!(paned, true)\nset_end_child_shrinkable!(paned, true)\n```\n\n![](../assets/paned_shrinkable.png)\n\n---\n\n## Revealer\n\nWhile not technically necessary, animations can improve user experience drastically. Not only do they add visual style, but they can also hide abrupt transitions or small loading times. As such, they should be in any advanced GUI designer's repertoire.\n\nOne of the most common applications for animations is the act of hiding or showing a widget. [`Revealer`](@ref) was made for this purpose.\n\nTo trigger the `Revealer`s animation and change whether its singular child widget is currently visible, we call [`set_is_revealed!`](@ref) which takes a boolean as its argument. If the widget goes from hidden to shown or shown to hidden, the animation will play. Once the animation is done, signal `revealed` will be emitted.\n\n### Transition Animation\n\nWe have control over the kind and speed of the transition animation. By calling [`set_transition_duration!`](@ref), we can set the exact amount of time an animation should take. For example, to set the animation duration to 1 second:\n\n```julia\nrevealer = Revealer()\nset_child!(revealer, #= widget =#)\nset_transition_duration!(revealer, seconds(1));\n```\n\nWhere `seconds` returns a [`Mousetrap.Time`](@ref).\n\nApart from the speed, we also have a choice of animation **type**, represented by the enum [`RevealerTransitionType`](@ref). Animations include a simple cross-fade, sliding, swinging, or no animation at all, which instantly shows or hides the widget.\n\n![](../assets/revealer.png)\n\n!!! details \"How to generate this image\"\n    ```julia\n    main() do app::Application\n        window = Window(app)\n\n        # create child\n        child = Frame(Overlay(Separator(), Label(\"<span size='200%'>[Item]</span>\")))\n        set_margin!(child, 10)\n        set_size_request!(child, Vector2f(0, 100))\n\n        # setup revealer\n        revealer = Revealer()\n        set_child!(revealer, child)\n        set_transition_duration!(revealer, seconds(1))\n        set_transition_type!(revealer, REVEALER_TRANSITION_TYPE_SLIDE_DOWN)\n\n        # create a button that, when clicked, triggers the revealer animation\n        button = Button()\n        connect_signal_clicked!(button, revealer) do self::Button, revealer::Revealer\n            set_is_revealed!(revealer, !get_revealed(revealer))\n        end\n    \n        set_child!(window, vbox(button, revealer))\n        present!(window)\n    end\n    ```\n---\n\n## ActionBar\n\nOne common application for using a `Revealer` is to show or hide a *toolbar*, which is a horizontal box with any number of buttons for contextual actions. For this purpose, [`ActionBar`](@ref) is well suited, because it can be shown/hidden using [`set_is_revealed!`](@ref) all by itself, making it so we don't need to use a separate `Revealer` instance.\n\n`ActionBar` is always horizontal, it cannot be oriented. It has space for any number of widgets on either side, along with having a singular centered widget. We can pack widgets to either side using `push_start!` and `push_end!`, while the centered widget is set using `set_center_widget!`. \n\n---\n\n## Expander\n\n[`Expander`](@ref) is similar to `Revealer`, in that it also has exactly one child widget, and it shows / hides the widget. Unlike `Revealer`, there is no animation attached to `Expander`. Instead, it hides the widget behind a collapsible label.\n\nExpander has two children, the label, set with `set_label_widget!`, and its child, set with `set_child!`, which is the widget that will be shown / hidden.\n\n![](../assets/expander.png)\n\n!!! details \"How to generate this image\"\n    ```julia\n    main() do app::Application\n        window = Window(app)\n\n        child = Frame(Overlay(Separator(), Label(\"Child\")))\n        set_margin!(child, 10)\n        set_size_request!(child, Vector2f(0, 100))\n\n        label = Label(\"Label\")\n        set_margin!(label, 10)\n    \n        expander_and_frame = Frame(Expander(child, label))\n        set_margin!(expander_and_frame, 10)\n\n        set_child!(window, expander_and_frame)\n        present!(window)\n    end\n    ```\n\nNote that `Expander` should not be used to create nested lists, as `ListView`, a widget we will learn about later in this chapter, is better suited for this purpose.\n\n---\n\n## Viewport\n\nBy default, most containers will allocate a size equal to or exceeding that of its children. For example, if we create a widget that has a natural size of 5000x1000 px and use it as the child of a `Window`, the `Window` will attempt to allocate 5000x1000 pixels on screen, making the window far larger than most monitors can display. \n\nSometimes, widgets that are this large are unavoidable. In situations like this, we can use [`Viewport`](@ref) to only display part of a widget.\n\nWe set the viewport's singular child using `set_child!`, after which the user can operate the two scrollbars to change which part of the child is currently visible:\n\n![](../assets/viewport.png)\n\n!!! details \"How to generate this image\"\n    ```julia\n    main() do app::Application\n\n        window = Window(app)\n\n        child = Frame(Overlay(Separator(), Label(\"<span size='800%'>CHILD</span>\")))\n        set_margin!(child, 10);\n\n        viewport = Viewport()\n        set_child!(viewport, child)\n\n        set_child!(window, viewport)\n        present!(window)\n    end\n    ```\n\n### Size Propagation\n\nBy default, `Viewport` will disregard the size of its child and simply allocate an area based only on the properties of the `Viewport` itself. This behavior can be overridden by setting the viewport's **size propagation**.\n\nIf [`set_propagate_natural_height!`](@ref) is set to true, the viewport's height will be equal to the height of its child. Conversely, [`set_propagate_natural_width!`](@ref) does the same for the child's width.\n\n```julia\nset_propagate_natural_width!(viewport, true)\nset_propagate_natural_height!(viewport, false)\n```\n\n![](../assets/viewport_propagation.png)\n\nHere, the viewport will be the same width as the child, but the viewport's height is independent of that of its child. \n\n### Scrollbar Policy\n\n`Viewport` has two scrollbars, controlling the horizontal and vertical position. By default, these will automatically reveal themself when the user's cursor enters the viewport, hiding again once the cursor exists.\n\nIf and when to reveal the scrollbars is determined by the viewports **scrollbar policy**, set with [`set_horizontal_scrollbar_policy!`](@ref) and  [`set_vertical_scrollbar_policy!`](@ref), both of which take a value of the enum [`ScrollbarVisibilityPolicy`](@ref), which has the following instances:\n\n\n| `ScrollbarVisibilityPolicy` | Meaning |\n|-----------------------------|-----------|\n| `SCROLLBAR_VISIBILITY_POLICY_NEVER` | scrollbar is always hidden |\n| `SCROLLBAR_VISIBILITY_POLICY_ALWAYS` | scrollbar is always shown |\n| `SCROLLBAR_VISIBILITY_POLICY_AUTOMATIC` | scrollbar hides/shows automatically|\n\nIf `set_propagate_natural_height!` is set to `true`, the vertical scrollbar will always be hidden, regardless of policy. The same is true for `set_propagate_natural_width!` and the horizontal scrollbar.\n\n### Scrollbar Position\n\nLastly, we can customize the location of both scrollbars at the same time using [`set_scrollbar_placement!`](@ref), which takes one of the following values of the enum [`CornerPlacement`](@ref).\n\n| `CornerPlacement` | Meaning |\n|-------------------|---------|\n| `CORNER_PLACEMENT_TOP_LEFT` | horizontal scrollbar at the top, vertical scrollbar on the left |\n| `CORNER_PLACEMENT_TOP_RIGHT` | horizontal at the top, vertical on the right |\n| `CORNER_PLACEMENT_BOTTOM_LEFT` | horizontal at the bottom, vertical on the left |\n| `CORNER_PLACEMENT_BOTTOM_RIGHT` | horizontal at the bottom, vertical on the right |\n\n### Signals\n\nIf we want to react to the user scrolling the `Viewport`'s child, we can either connect to its signal `scroll_child`, or we can access the `Adjustment` controlling each scrollbar using [`get_horizontal_adjustment`](@ref) and [`get_vertical_adjustment`](@ref), then connect to the signals of the obtained `Adjustment`s.\n\nWith this, scrollbar policy, and size propagation we have full control over every aspect of `Viewport`.\n\n--- \n\n## Scrollbar\n\n`Viewport` comes with two scrollbars, but we can also create our own. Using [`Scrollbar`](@ref), which takes an `Orientation` as well as an  `Adjustment` for its constructor, we can create a fully custom scrolling widget.\n\nTo react to the user scrolling, we need to connect to the signals of the `Adjustment`, as `Scrollbar` does not provide any signals itself:\n\n```julia\nadjustment = Adjustment(0.5, 0, 1, 0.01)\nscrollbar = Scrollbar(ORIENTATION_HORIZONTAL, adjustment)\nconnect_signal_value_changed!(adjustment) do self::Adjustment\n    println(\"Value is now $(get_value(self))\")\nend\n\nset_child!(window, scrollbar)\n```\n\nWhere we made it such that the scrollbar expresses the range `[0, 1]`, with a step increment of `0.01` and an initial value of `0.5`.\n\nIf we lose track of the `Adjustment` instance after constructing the `ScrollBar`, we can retrieve it anytime using `get_adjustment`.\n\n---\n\n## Popover\n\nA [`Popover`](@ref) is a special kind of window. It is always [modal](#modality--transience). Rather than having the normal window decoration with a close button and title, `Popover` closes dynamically (or when requested by the application).\n\nShowing the popover is called **popup**, closing the popover is called **popdown**, `Popover` correspondingly has [`popup!`](@ref) and [`popdown!`](@ref) to show or hide itself.\n\n![](../assets/popover.png)\n\n!!! details \"How to generate this image\"\n    ```julia\n    using Mousetrap\n\n    main() do app::Application\n        window = Window(app)\n\n        child = Frame(Overlay(Separator(), Label(\"Child\")))\n        set_size_request!(child, Vector2f(100, 75))\n        set_margin!(child, 10);\n\n        popover = Popover()\n        set_child!(popover, child)\n        button = Button()\n\n        # when the button is clicked, the popover is shown. It hides automatically\n        connect_signal_clicked!(button, popover) do self::Button, popover::Popover\n            popup!(popover)\n        end\n\n        # to show the popover, it needs to be inside the same container as the child it should be attached to\n        set_child!(window, vbox(popover, button))\n        present!(window)\n    end\n    ```\n\nManually calling `popup!` or `popdown!` to show or hide the `Popover` can be bothersome. To address this, Mousetrap offers a widget that automatically manages the popover for us: [`PopoverButton`](@ref)\n\n## PopoverButton\n\n`PopoverButton` has a single child and one signal: `activate`:\n\n```@eval\nusing Mousetrap\nMousetrap.@signal_table(PopoverButton,\n    activate\n)\n```\n\nInstead of triggering behavior, `PopoverButton`'s purpose is to reveal and hide a `Popover`.\n\nWe first create the `Popover`, then supply it as the argument to `PopoverButton`'s constructor:\n\n```julia\npopover = Popover()\npopover_button = PopoverButton(popover)\n```\n\nAdditionally, an arrow is shown next to the label of the `PopoverButton`, indicating to the user that, when it is clicked, a popover will open.\n\n---\n\n## SelectionModel\n\nWe will now move on to **selectable widgets**, which tend to be the most complex and powerful widgets in Mousetrap.\n\nAll selectable widgets have one thing in common: their multiple children are managed by a **selection model**. This model is an ordered list of widgets. For each widget, the model will keep track of whether that widget is currently selected. If it is, a graphical element will be added to the selectable widget that indicates to the user which item(s) are currently selected:\n\n![](../assets/list_view_selected.png)\n\nModifying the model will modify the selectable widget, and modifying the selectable widget will modify the model. In this way, the two are linked, similar to how `Adjustment` works. We use [`select!`](@ref) and [`unselect!`](@ref) to change the selection manually, while [`get_selection`](@ref) returns a vector with one or more of the selected items indices.\n\n`SelectionModel` has signal `selection_changed`, which is emitted anytime an item is selected or unselected in any way. This signal requires the signature\n```\n(::SelectionModel, position::Integer, n_items::Integer, [::Data_t]) -> Nothing\n```\nWhere `position` is the new index of the changed item (1-based), while `n_items` is the number of currently selected items.\n\nEach model has an associated property called the **selection mode**, which is expressed by the enum [`SelectionMode`](@ref). This governs how many items can be selected at one time:\n\n| `SelectionMode`           | Number of Items |\n|---------------------------|-----------------|\n| `SELECTION_MODE_NONE`     | exactly zero    |\n| `SELECTION_MODE_SINGLE`   | exactly one     |\n| `SELECTION_MODE_MULTIPLE` | zero or more    |\n\n\nWe do not create instances of `SelectionModel` ourselves, instead, they are automatically created along with the selectable widget. Because of this, we will need to specify the selection mode in the selectable widget's constructor.\n\n## ListView\n\nFor our first selectable widget, we have [`ListView`](@ref). This is a widget that arranges its children in a row or column, depending on orientation. We add children using `push_back!`, `push_front!` and `insert_at!`:\n\n```julia\nlist_view = ListView(ORIENTATION_VERTICAL, SELECTION_MODE_SINGLE)\npush_back!(list_view, Label(\"Child #01\"))\npush_back!(list_view, Label(\"Child #02\"))\npush_back!(list_view, Label(\"Child #03\"))\n```\n\n![](../assets/list_view_simple.png)\n\nWhere the second child is currently selected.\n\n`ListView` can be requested to automatically show separators in between two items by setting [`set_show_separators!`](@ref) to `true`. To check which item is selected, we query its selection model, which we obtain using [`get_selection_model`](@ref).\n\n### Nested Trees\n\nBy default, `ListView` displays its children in a linear list, either horizontally or vertically. `ListView` also supports **nested lists**, sometimes called a **tree view**:\n\n![](../assets/list_view_nested.png)\n\n!!! details \"How to generate this image\"\n    ```julia\n    main() do app::Application\n\n        window = Window(app)\n\n        list_view = ListView(ORIENTATION_VERTICAL, SELECTION_MODE_SINGLE)\n        push_back!(list_view, Label(\"Child #01\"))\n        child_02_it = push_back!(list_view, Label(\"Child #02\"))\n        push_back!(list_view, Label(\"Child #03\"))\n\n        push_back!(list_view, Label(\"Nested Child #01\"), child_02_it)\n        nested_child_02_it = push_back!(list_view, Label(\"Nested Child #02\"), child_02_it)\n\n        push_back!(list_view, Label(\"Inner Child #01\"), nested_child_02_it)\n\n        frame = Frame(list_view)\n        set_margin!(frame, 10)\n        set_child!(window, frame)\n        present!(window)\n    end\n    ```\n\nThe user can click any item to hide or show its children. Items that do not have any children will appear just as with a non-nested `ListView`.\n\nEach item of a `ListView` can in itself be made a list view. To do this, we use an optional argument of `push_back!` (or `push_front!`, `insert_at!`), which is of type [`ListViewIterator`](@ref). \n\nThis iterator identifies which list view to insert the item in. We obtain an iterator like so:\n\n```julia\nlist_view = ListView()\nchild_01_it = push_back!(list_view, Label(\"Child #01\"))\nchild_02_it = push_back!(list_view, Lable(\"Child #02\"))\nchild_03_it = push_back!(list_view, Lable(\"Child #03\"))\n```\n\nIf we want to convert the item containing the label `Child #02` to a list view and insert an item as its child, we use that item's iterator as the optional argument:\n\n```julia\nnested_child_01_it = push_back!(list_view, Label(\"Nested Child #01\"), child_02_it)\n```\n\nTo insert a new widget as the child of this already nested list, we use its iterator. Through this mechanism, we can create arbitrarily deep nested lists.\n\nIf we do not want a nested list, we can instead completely ignore the iterator. Specifying no iterator when using `push_back!` means we will be inserting items into the outermost list.\n\n---\n\n## GridView\n\n[`GridView`](@ref) supports many of the same functions as `ListView`, including `push_back!`, `push_front!`, and `insert_at!`. Unlike `ListView`, `GridView` cannot be nested, as it instead displays its children in a **uniform grid**.\n\n`GridView`s constructor also takes an orientation as well as the selection mode. The orientation determines in which order elements will be shown. Consider the next two images, the first of which is a `GridView` whose orientation is `ORIENTATION_HORIZONTAL`, while the latter is `ORIENTATION_VERTICAL`:\n\n![](../assets/grid_view_horizontal.png)\n\n*A horizontal `GridView`*\n\n![](../assets/grid_view_vertical.png)\n\n*A vertical `GridView`*\n\n!!! details \"How to generate this image\"\n    ```julia\n    function generate_child(label::String) ::Widget\n\n        child = Frame(Overlay(Separator(), Label(label)))\n        set_size_request!(child, Vector2f(50, 50))\n        set_expand!(child, false)\n        return AspectFrame(1, child)\n    end\n\n    main() do app::Application\n        window = Window(app)\n\n        grid_view = GridView(ORIENTATION_VERTICAL) # or `ORIENTATION_HORIZONTAL`\n        set_expand!(grid_view, true)\n\n        for i in 1:9\n            push_back!(grid_view, generate_child(\"0$i\"))\n        end\n\n        separator = Separator()\n        set_expand!(separator, true)\n        set_expand!(grid_view, false)\n\n        set_child!(window, vbox(separator, grid_view))\n        present!(window)\n    end\n    ```\n\nWe can control the exact distribution of widgets more closely by using [`set_max_n_columns!`](@ref) and [`set_min_n_columns!`](@ref), which make it so the grid view will always have the given number of columns (or rows, for a horizontal `GridView`).\n\n---\n\n## Column View\n\n[`ColumnView`](@ref) is used to display widgets as a table, with rows and columns. Each column has a title, which uniquely identifies it.\n\nTo fill our `ColumnView`, we first instance it, then allocate a non-zero number of columns:\n\n```julia\ncolumn_view = ColumnView()\n\ncolumn_01 = push_back_column!(column_view, \"Column #01\")\ncolumn_02 = push_back_column!(column_view, \"Column #02\")\ncolumn_03 = push_back_column!(column_view, \"Column #03\")\n```\n\nEach column requires a title that should be unique to that column.\n\nWe can also add a column at any point, even after rows have been added. Along with [`push_back_column!`](@ref), [`push_front_column!`](@ref) and [`insert_column_at!`](@ref) are also available. All of these functions return an object of type [`ColumnViewColumn`](@ref).\n\nTo add a widget into the n-th row (1-based) of a `ColumnViewColumn`, we use `set_widget_at!`:\n\n```julia\n# add 3 labels into column 1, rows 1 - 3\nset_widget_at!(column_view, column_01, Label(\"01\"))\nset_widget_at!(column_view, column_01, Label(\"02\"))\nset_widget_at!(column_view, column_01, Label(\"03\"))\n```\n\n![](../assets/column_view.png)\n\n!!! details \"How to generate this image\"\n    ```julia\n    main() do app::Application\n\n        println(\"called\")\n\n        window = Window(app)\n        set_title!(window, \"Mousetrap.jl\")\n\n        column_view = ColumnView()\n\n        column_01 = push_back_column!(column_view, \"Column #01\")\n        column_02 = push_back_column!(column_view, \"Column #02\")\n        column_03 = push_back_column!(column_view, \"Column #03\")\n\n        column_i = 1\n        for column in [column_01, column_02, column_03]\n            for row_i in 1:9\n                set_widget_at!(column_view, column, row_i, Label(\"0$column_i | 0$row_i\"))\n            end\n            column_i = column_i + 1\n        end\n\n        set_expand!(column_view, true)\n        set_child!(window, column_view)\n        present!(window)\n    end\n    ```\n\nAny rows that do not yet have widgets will be backfilled and appear empty. \n\nIf we lose track of the `ColumnViewColumn` instance returned when adding a new column, we can retrieve it using `get_column_at` or `get_column_with_title`, the latter of which takes the unique title we chose when adding the column.\n\nSince most of the time, we will want all cells in a row to contain a widget, we can also use [`push_back_row!`](@ref), [`push_front_row!`](@ref), or [`insert_row_at!`](@ref), which insert n widgets at once, where n is the number of columns:\n\n```julia\n# add 1st widget to 1st column, 2nd widget to 2nd column, etc.\npush_back_row!(column_view, Label(\"Column 01 Child\"), Label(\"Column 02 Child\"), Label(\"Column 03 Child\"))\n```\n\nThis is a more convenient way to fill the column view, though if we later want to edit it, we will have to use `set_widget_at!` to override widgets in any rows.\n\n`ColumnViewColumn` has several other features. We can make it so the user can freely resize each column by setting [`set_is_resizable!`](@ref) to `true`, or we can force each column to have an exact width using [`set_fixed_width!`](@ref), which takes the widget width in pixels.\n\n---\n\n## Stack\n\n[`Stack`](@ref) is a selectable widget that can only ever display exactly one child at a time. Each child of the stack is called a **page**.\n\nWe add a page using `add_child!`, which takes any widget, and the title of the page. This title is mandatory and it has to uniquely identify the page. `add_child!` returns the page's ID, which, similarly to how adding elements to `DropDown` works, we need to keep track of to later refer to pages in a position-independent manner.\n\n```julia\nstack = Stack()\n\nid_01 = add_child!(stack, page_01_widget, \"Page #01\")\nid_02 = add_child!(stack, page_02_widget, \"Page #02\")\nid_03 = add_child!(stack, page_03_widget, \"Page #03\")\n```\n\nTo check which page is currently visible, we use [`get_visible_child`](@ref), which returns that page's ID. If we loose track of it, we can retrieve the ID of a stack page at a given position using [`get_child_at`](@ref).\n\nTo keep track of which page is currently selected, we should connect to the stack's underlying `SelectionModel`, just like we would with `ListView` and `GridView`:\n\n```julia\nfunction on_selection_changed(self::SelectionModel, position::Integer, n_items::Integer, stack::Stack) ::Nothing\n    println(\"Current stack page is now: $(get_child_at(stack, position))\")\nend\n\nstack = Stack()\nstack_model = get_selection_model(stack)\nconnect_signal_selection_changed!(on_selection_changed, stack_model, stack)\n```\n\nWhere we provided the `Stack` instance to the selection model's signal handler as the optional `data` argument, so that we can reference it from within the signal handler.\n\nWhile we can change the currently active page using `set_visible_child!`, our user cannot. To allow them to change the page of a `Stack`, we either need\nto provide another widget that modifies the stack, or we can use one of two pre-made widgets for this purpose: `StackSwitcher` and `StackSidebar`.\n\n### StackSwitcher\n\n[`StackSwitcher`](@ref) presents the user with a row of buttons, each of which uses the corresponding stack page's title:\n\n![](../assets/stack_switcher.png)\n\n!!! details \"How to generate this image\"\n    ```julia\n    function generate_child(label::String) ::Widget\n        child = Frame(Overlay(Separator(), Label(label)))\n        set_size_request!(child, Vector2f(150, 150))\n        set_margin!(child, 10)\n        return child\n    end\n\n    main() do app::Application\n        window = Window(app)\n\n        stack = Stack()\n\n        add_child!(stack, generate_child(\"Child #01\"), \"Page #01\")\n        add_child!(stack, generate_child(\"Child #02\"), \"Page #02\")\n        add_child!(stack, generate_child(\"Child #03\"), \"Page #03\")\n\n        stack_model = get_selection_model(stack)\n        connect_signal_selection_changed!(stack_model, stack) do x::SelectionModel, position::Integer, n_items::Integer, stack::Stack\n            println(\"Current stack page is now: $(get_child_at(stack, position))\")\n        end\n\n        set_child!(window, vbox(stack, StackSwitcher(stack))) # create StackSwitcher from stack\n        present!(window)\n    end\n    ```\n\n`StackSwitcher` has no other methods or properties, though it provides the signals that all widgets share.\n\n### StackSidebar\n\n[`StackSidebar`](@ref) has the same purpose as `StackSwitcher`, except it displays the list of stack pages as a vertical list:\n\n!!! details \"How to generate this image\"\n    ```julia\n    function generate_child(label::String) ::Widget\n        child = Frame(Overlay(Separator(), Label(label)))\n        set_size_request!(child, Vector2f(150, 150))\n        set_margin!(child, 10)\n        return child\n    end\n\n    main() do app::Application\n        window = Window(app)\n\n        stack = Stack()\n\n        add_child!(stack, generate_child(\"Child #01\"), \"Page #01\")\n        add_child!(stack, generate_child(\"Child #02\"), \"Page #02\")\n        add_child!(stack, generate_child(\"Child #03\"), \"Page #03\")\n\n        stack_model = get_selection_model(stack)\n        connect_signal_selection_changed!(stack_model, stack) do x::SelectionModel, position::Integer, n_items::Integer, stack::Stack\n            println(\"Current stack page is now: $(get_child_at(stack, position))\")\n        end\n\n        set_child!(window, vbox(stack, StackSwitcher(stack))) # Create StackSidebar from stack\n        present!(window)\n    end\n    ```\n\nOther than this visual component, its purpose is identical to that of `StackSwitcher`.\n\n### Transition Animation\n\nWhen changing which of the stack pages is currently shown, regardless of how that selection was triggered, an animation transitioning from one page to the other plays. Similar to `Revealer`, we can influence the type and speed of the animation in multiple ways:\n\n+ [`set_transition_duration!`](@ref) chooses how long the animation will take to complete\n+ [`set_should_interpolate_size!`](@ref), if set to `true`, makes it, such that while the transition animation plays, the stack will change from the size of the previous child to the size of the current child gradually. If set to `false`, this size change happens instantly\n+ [`set_transition_type!`](@ref) governs the type of animation, which is one of the enum values of [`StackTransitionType`](@ref).\n\nIf we want all of the stacks children to allocate the same size, we can set [`set_is_vertically_homogeneous!`](@ref) and [`set_is_horizontally_homogeneous!`](@ref) to `true`, in which case the stack will assume the height or widget of its largest page, respectively.\n\n---\n\nMoving on from selectable widgets, we have a few more widget containers to get through. These offer more flexibility in arranging widgets, but do not offer `get_selection_model` and its related features.\n\n## Grid\n\nNot to be confused with `GridView`, [`Grid`](@ref) arranges its children in a **non-uniform** grid:\n\n![](../assets/grid.png)\n\n!!! details \"How to generate this image\"\n    ```julia\n    function generate_child(label::String) ::Widget\n        child = Frame(Overlay(Separator(), Label(label)))\n        set_size_request!(child, Vector2f(50, 50))\n        return child\n    end\n\n    main() do app::Application\n\n        window = Window(app)\n        \n        grid = Grid()\n\n        Mousetrap.insert_at!(grid, generate_child(\"01\"), 1, 1, 2, 1)\n        Mousetrap.insert_at!(grid, generate_child(\"02\"), 3, 1, 1, 2)\n        Mousetrap.insert_at!(grid, generate_child(\"03\"), 4, 1, 1, 1)\n        Mousetrap.insert_at!(grid, generate_child(\"04\"), 1, 2, 1, 2)\n        Mousetrap.insert_at!(grid, generate_child(\"05\"), 2, 2, 1, 1)\n        Mousetrap.insert_at!(grid, generate_child(\"06\"), 4, 2, 1, 1)\n        Mousetrap.insert_at!(grid, generate_child(\"07\"), 2, 3, 3, 1)\n    \n        set_margin!(grid, 10)\n        set_child!(window, grid)\n        present!(window)\n    end\n    ```\n\nEach widget in the grid has an x- and y-position, along with a widget and height, both measured in **number of cells** . \n\nFor example, in the image above, the widget labeled `05` has an x-position of 2, y-position of 2, a width of 1 cell, and a height of 1 cell. \n\n```julia\nMousetrap.insert_at!(\n    grid, \n    widget_05,\n    2,  # x-position\n    2,  # y-position\n    1,  # width\n    1   # height\n)\n```\n\nMeanwhile, the widget labeled `07` has an x-position of 2, y-position of 3, width of 3 cells, and height of 1 cell.\n\n```julia\nMousetrap.insert_at!(\n    grid, \n    widget_07, \n    2,  # x-position\n    3,  # y-position\n    3,  # width\n    1   # height\n)\n```\n\nWhen using `insert_at!`, we have to make sure that no two widgets overlap.\n\nFor a less manual way of arranging widgets, we can insert a widget next to another widget already in the grid using [`insert_next_to!`](@ref), which takes an argument of type [`RelativePosition`](@ref) in order to specify whether we want to insert the new widget above, below, left of, or right of the widget already inside the grid:\n\n```julia\nchild = # ...\n\n# insert `child` at position (1, 1)\ninsert_at!(grid, child, 1, 1)\n\nnew_widget = # ...\n# insert new widget left of child\ninsert_next_to!(grid, new_widget, child, RELATIVE_POSITION_LEFT_OF)\n```\n\nWhere we omitted the width and height in cells, which default to `1` respectively. \n\nNote that in this example, `child` is at position `(1, 1)`, and `new_widget` was inserted left of `child`, meaning it is now at position `(0, 1)`. `Grid` will dynamically insert rows and columns as needed, meaning that the x- and y-position may be `0` or even negative.\n\nLastly, `set_column_spacing!` and `set_row_spacing!` automatically insert margins in between columns and rows, while `set_rows_homogeneous!` and `set_columns_homogeneous!` make it so all rows or columns will allocate the same height and width, respectively.\n\n`Grid` offers a more flexible, but also more manual way of arranging widgets in 2D space.\n\n---\n\n## Notebook\n\n[`Notebook`](@ref) is very similar to `Stack`, it always displays exactly one child, though `Notebook` is not based on a `SelectionModel`. Unlike `Stack`, it comes with a built-in way for users to choose which child to show.\n\nEach notebook page has two widgets, the **child widget**, which is displayed in the content area of each page, and the **label widget**, which is the label used for the tab. This will usually be a `Label`, though any widget can be used.\n\nWe add pages using `push_back!`, `push_front!` or `insert_at!`, which take the child and label widget as their arguments:\n\n```julia\nnotebook = Notebook()\n\npush_back!(notebook, child_01, Label(\"Label #01\"))\npush_back!(notebook, child_02, Label(\"Label #02\"))\npush_back!(notebook, child_03, Label(\"Label #03\"))\n```\n![](../assets/notebook.png)\n\n!!! details \"How to generate this image\"\n    ```julia\n    function generate_child(label::String) ::Widget\n        child = Frame(Overlay(Separator(), Label(label)))\n        set_size_request!(child, Vector2f(150, 150))\n        set_margin!(child, 10)\n        return child\n    end\n\n    main() do app::Application\n        window = Window(app)\n\n        notebook = Notebook()\n\n        push_back!(notebook, generate_child(\"Child #01\"), Label(\"Label #01\"))\n        push_back!(notebook, generate_child(\"Child #02\"), Label(\"Label #02\"))\n        push_back!(notebook, generate_child(\"Child #03\"), Label(\"Label #03\"))\n\n        set_child!(window, notebook)\n        present!(window)\n    end\n    ```\n\nWe can allow the user to reorder the pages by setting [`set_tabs_reorderable!`](@ref) to `true`. This will make it so they can click drag-and-drop the label widget, moving the entire notebook page to that position.\n\nIf we want to change the currently selected page, we use `next_page!`, `previous_page!`, or `goto_page!`, the latter of which takes the page position as an integer.\n\nTo react to the user changing the currently selected page, we have to connect to one of `Notebook`'s unique signals, all of which require a signal handler with the same signature: \n\n```@eval\nusing Mousetrap\nMousetrap.@signal_table(Notebook,\n    page_added,\n    page_removed,\n    page_reordered,\n    page_selection_changed\n)\n```\n\nFor example, if we want to react to the currently selected page changing, we would connect to signal `page_selection_changed` like so:\n\n```julia\nnotebook = Notebook()\nconnect_signal_page_selection_changed!(notebook) do self::Notebook, index::Integer\n    println(\"Page #$index is now selected\")\nend\n```\n\n---\n\n## Compound Widgets\n\nNow that we have many new widgets in our arsenal, a natural question is \"How do we make our own?\". If we want to construct a completely new widget pixel-by-pixel, we will have to wait until the chapter on [native rendering](./09_native_rendering.md). Until then, we are already perfectly capable of creating what we'll call a **compound widget**.\n\nA compound widget is a widget that groups many other, pre-made widgets together. Compound widgets give an easy mechanism to \nlogically group a collection of widgets and treat them as a whole, instead of having to operate on each of its parts individually.\n\nIn order for a Julia object to be considered a `Widget`, that is, all functions that take a `Mousetrap.Widget` also work \non this new object, it has to implement the **widget interface**. An object is considered a `Widget` if...\n\n+ it is a direct or indirect subtype of `Widget`\n+ [`Mousetrap.get_top_level_widget`](@ref), which maps it to its top-level widget, is defined for this type\n\nIn this section we will work through an example, explaining what both of these conditions mean, and how to fulfill them.\n\n### Example: Placeholder\n\nIn the previous section on selectable containers such as `ListView` and `GridView`, we used this \"placeholder\" widget:\n\n![](../assets/compound_widget_list_view.png)\n\nIn this image we have a `ListView` with four elements, each of the elements is an object of type `Placeholder`, which is a newly created compound widget.\n\nLooking closely, we see that each placeholder has a `Label` with the title and a `Separator` behind the label. Because they are rendered on top of each other, an `Overlay` has to be involved. A `Frame` is used to give the element rounded corners, lastly, each element is square at all times, which means its size is managed by an `AspectFrame`.\n\nCreating a new Julia struct with these elements, we do:\n\n```julia\nstruct Placeholder\n    label::Label\n    separator::Separator\n    overlay::Overlay\n    frame::Frame\n    aspect_frame::AspectFrame\nend\n```\n\nWe add a constructor that correctly assembles these widgets like so:\n\n```julia\nfunction Placeholder(text::String)\n    out = Placeholder(\n        Label(\"<tt>\" * text * \"</tt>\")  # label with monospaced text \n        Separator()                     # background\n        Overlay()                       # overlay to draw `label` on top of `separator` background\n        Frame()                         # frame for outline and rounded corners\n        AspectFrame(1.0)                # square aspect frame\n   )     \n   \n   # make the background the lower most layer\n   set_child!(out.overlay, out.separator)\n   \n   # add the label on top\n   add_overlay(out.overlay, out.label; include_in_measurement = true)\n   \n   # add everything into the `Frame`\n   set_child!(frame, overlay)\n   \n   # force frame to be square\n   set_child!(aspect_frame, frame)\n   \n   return out\nend\n```\n\nWith our compound widget assembled, we will want to make it the child of a `Window` or other container:\n\n```julia\nmain() do app::Application\n    window = Window(app)\n    set_child!(window, Placeholder(\"Test\"))\n    present!(window)\nend \n```\n```\n[ERROR] In Mousetrap.main: MethodError: no method matching set_child!(::Window, ::Placeholder)\n\nClosest candidates are:\n  set_child!(::Window, ::Widget)\n```\n\nWe can't, because `Placeholder`, the new Julia struct, is not yet considered a `Mousetrap.Widget`. \n\n### Widget Interface\n\nTo solve this, we come back to our two properties that make something a widget:\n1) it is a direct or indirect subtype of `Widget`\n2) `get_top_level_widget`, which maps it to its top-level widget, is defined for this type\n\nSolving 1), we make `Placeholder` a subtype of `mousetrap.Widget`:\n\n```julia\nstruct Placeholder <: Widget\n    label::Label\n    separator::Separator\n    overlay::Overlay\n    frame::Frame\n    aspect_frame::AspectFrame\nend\n```\n\nTo implement `get_top_level_widget`, we need to look at how we assembled `Placeholder` during its constructor, then figure out which widget is indeed top-level.\n\nWe can write the `Placeholder` architecture out like so:\n\n```cpp\nAspectFrame \\\n  Frame \\\n    Overlay \\\n      Label\n      Separator\n```\n\nWhere a widget with `\\` is a widget container. The only widget without a direct parent is the `AspectFrame`, therefore `aspect_frame` is the top-level widget.\n\nGiven this, we implement `get_top_level_widget` like so:\n\n```julia\nMousetrap.get_top_level_widget(x::Placeholder) = x.aspect_frame\n```\n\nWhere the `Mousetrap.` prefix is necessary to tell the compiler that we are overloading that method, not creating a new method in our current module.\n\nWith `Widget` subtyped and `get_top_level_widget` implemented, our `main.jl` now looks like this:\n\n```julia\nstruct Placeholder <: Widget\n    label::Label\n    separator::Separator\n    overlay::Overlay\n    frame::Frame\n    aspect_frame::AspectFrame\nend\n\nfunction Placeholder(text::String)\n    out = Placeholder(\n        Label(\"<tt>\" * text * \"</tt>\"),\n        Separator(),\n        Overlay(),\n        Frame(),\n        AspectFrame(1.0)\n    )     \n    \n    set_child!(out.overlay, out.separator)\n    add_overlay!(out.overlay, out.label; include_in_measurement = true)\n    set_child!(out.frame, out.overlay)\n    set_child!(out.aspect_frame, out.frame)\n    return out\nend\n\nMousetrap.get_top_level_widget(x::Placeholder) = x.aspect_frame\n\nmain() do app::Application\n    window = Window(app)\n    set_child!(window, Placeholder(\"TEST\"))\n    present!(window)\nend\n```\n\n![](../assets/compound_widget_complete.png)\n\nNow that `Placeholder` is a proper widget, all of Mousetraps functions, including all widget signals, have become available to use. We need to keep in mind that these functions will modify the top-level widget, so only signals of the top-level widget are available to compound widgets.\n\nUsing this technique, we can build an application piece by piece. A compound widget itself can be made up of multiple other compound widgets. In some way, the entire application itself is just one giant compound widget, with a `Window` as the top-level widget.\n\n!!! compat \n    Since Mousetrap `v0.3.0`, compound widgets will assume the signals of their top-level widgets. For example, if the compound widget `EntryWrapper` has its top-level widget be an `Entry`, then `connect_signal_activate!` will work on instances of `EntryWrapper`, since `EntryWarpper` assumed signal `activate`\n\nOur users are only able to interact with the compound widget by interacting with each of its components, which works but is fairly limiting. In the next chapter, this will change. By learning about **event handling**, we will be able to react to any kind of user interaction, giving us the last tool needed to create an application that isn't just a collection of pre-made widgets, but something we have built ourselves.\n\n## Custom Signals\n\n!!! compat\n    This feature is not yet implemented, this section is incomplete. See [here](https://github.com/users/Clemapfel/projects/2?pane=issue&itemId=35716010#) for more information.\n"
  },
  {
    "path": "docs/src/01_manual/05_event_handling.md",
    "content": "# Chapter 5: Event Handling\n\nIn this chapter, we will learn:\n+ What input focus is\n+ What an event controller is\n+ How to connect an event controller to any widget\n+ How to react to any user input event\n\n--- \n\n## Introduction: Event Model\n\nSo far, we have been able to react to a user interacting with the GUI through pre-made widgets. For example, if the user clicks a `Button`, that button will emit the signal `clicked`, which can trigger our custom behavior. While this mechanism works, it can also be fairly limiting. Pre-defined widgets will only have pre-defined ways of interacting with them. We cannot react to the user pressing the space bar with just `Button`. To do that, we will need an **event controller**. \n\n### What is an Event?\n\nWhen the user interacts with a computer in the physical world, they will control some kind of device, for example, a mouse, keyboard, touchpad, webcam, stylus, etc. Through a driver, the device will send data to the operating system, which then processes it into what is called an **event**. \n\nPressing a keyboard key is an event; releasing the key is a new event. Moving the cursor by one unit is an event; pressing a stylus against a touchpad is an event, etc. Mousetrap is based on GTK4, which has very powerful and versatile event abstraction. We don't have to deal with OS-specific events directly, instead, the GTK backend will automatically transform and distribute events for us, regardless of the operating system or peripheral manufacturer.\n\nTo receive events, we need an [`EventController`](@ref). The `EventController` type is abstract, meaning we cannot use it directly. Instead, we deal with one or more of its subtypes, each of which handles one conceptual type of event. \n\n**For an event controller to be able to capture events, it needs to be connected to a widget**. Once connected, if the widget holds **input focus**, events are forwarded to the event controller, whose signals we can then connect custom behavior to.\n\n## Input Focus\n\nThe concept of [**input focus**](https://en.wikipedia.org/wiki/Focus_(computing)) is important to understand. In Mousetrap (and GUIs in general), each widget has a hidden property that indicates whether the widget currently **holds focus**. If a widget holds focus, all its children hold focus as well. For example, if the focused widget is a `Box`, all widgets inside that box also hold focus, while the widget the box is contained within does not.\n\n**Only a widget holding focus can receive input events**. Which widget acquires focus is controlled by a somewhat complex heuristic, usually using things like which window is on top and where the user last interacted with the GUI. For most situations, this mechanism works very well and we don't have to worry about it much, in the rare cases we do, we can control the focus directly.\n\n### Preventing Focus\n\nOnly `focusable` widgets can hold focus. We can make a widget focusable by calling [`set_is_focusable!`](@ref). Not all widgets are focusable by default. To know which are and are not focusable, we can use [`get_is_focusable`](@ref), or common sense: Any widget that has a way of interacting with it (such as a `Button`, `Entry`, `Scale`, `SpinButton`, etc.) will be focusable by default. Widgets that have no native way of interacting with them are not focusable unless we specifically request them to be. This includes widgets like `Label`, `ImageDisplay`, `Separator`, etc.\n\nIf a container widget has at least one focusable child, it itself is focusable.\n\nTo prevent a widget from being able to gain focus, we can either disable the widget entirely by setting [`set_can_respond_to_input!`](@ref) to `false`, or we can declare it as not focusable using [`set_is_focusable!`](@ref).\n\n### Requesting Focus\n\n[`grab_focus!`](@ref) will make a widget attempt to gain input focus, stealing it from whatever other widget is currently focused. If this is not possible, for example, because the widget is disabled, not yet shown, or is not focusable, nothing will happen. We can check if a widget currently has focus by calling [`get_has_focus`](@ref).\n\nMany widgets will automatically grab focus if interacted with. For example, if the user places the text cursor inside an `Entry`, that entry will grab focus. Pressing enter now activates the entry, even if another widget held focus before. If a `SpinButton` is clicked, it will usually grab focus. We can make any widget, even those that do not require interaction, grab focus when clicked by setting [`set_focus_on_click!`](@ref) to `true` after they have been made focusable.\n\nThe user can also decide which widget should hold focus, usually by pressing the tab key. If [`set_focus_visible!`](@ref) was set to `true` for the top-level window, the focused widget will be highlighted using a transparent border.\n\n---\n\n## Event Controllers\n\n### Connecting a Controller\n\nUsing our newly gained knowledge about focus, we'll create our first event controller: [`FocusEventController`](@ref). This controller reacts to a widget gaining or losing input focus.\n\nAfter creating an event controller, it will not yet react to any events. We need to **add the controller to a widget**. For this chapter, we will assume that this widget is the top-level window, called `window` in the code snippets henceforth.\n\nWe create and connect a `FocusEventController` like so:\n\n```julia\nfocus_controller = FocusEventController()\nadd_controller!(window, focus_controller)\n```\n\nWhile the controller will now receive events, nothing else will happen. We need to connect to one or more of its signals, using the familiar signal handler mechanism.\n\n## Gaining / Losing Focus: FocusEventController\n\n`FocusEventController` has two signals:\n\n```@eval\nusing Mousetrap\nreturn Mousetrap.@signal_table(FocusEventController,\n    focus_gained,\n    focus_lost\n)\n```\n\nAfter connecting to these signals:\n\n```julia\nfunction on_focus_gained(self::FocusEventController) ::Nothing\n    println(\"focus gained\")\nend\n\nfunction on_focus_lost(self::FocusEventController) ::Nothing\n    println(\"focus lost\")\nend\n\nfocus_controller = FocusEventController()\nconnect_signal_focus_gained!(on_focus_gained, focus_controller)\nconnect_signal_focus_gained!(on_focus_lost, focus_controller)\nadd_controller!(window, focus_controller)\n```\n\nWe have successfully created our first event controller. Now, whenever `window` gains focus, a message will be printed.\n\n---\n\n## Keyboard Keys: KeyEventController\n\nMonitoring focus is rarely necessary, for something much more commonly used, we turn to keyboard keystrokes. Events of this type are emitted whenever a key on a keyboard goes from not-pressed to pressed (down), or pressed to not-pressed (up). We capture events of this type using  [`KeyEventController`](@ref). \n\n### Key Identification\n\nFrom the [chapter on actions](./03_actions.md) we recall that keyboard keys are split into two groups, **modifiers** and **non-modifiers**. Like with shortcut triggers, these are handled separately.\n\nEach non-modifier key has a key code, which will be a constant defined by `Mousetrap`. A full list of constants is available [here](https://github.com/Clemapfel/mousetrap.jl/blob/main/src/key_codes.jl), or as the global `Mousetrap.key_codes`, which provides a human-readable way to identify keys. \n\nFor example, to refer to the space key, Mousetrap uses a hard-coded integer internally. We as developers do not need to remember this value, instead, we use the `KEY_space` constant:\n\n```@repl\nusing Mousetrap\nKEY_space\n```\n\n### KeyEventController Signals\n\nNow that we know how to identify keys, we can instance `KeyEventController`, which has 3 signals:\n\n```@eval\nusing Mousetrap\nreturn Mousetrap.@signal_table(KeyEventController,\n    key_pressed,\n    key_released,\n    modifiers_changed\n)\n```\n\nWe see that pressing and releasing a non-modifier key are handled in separate signals. These can be used to track whether a key is currently up or down. Lastly, pressing or releasing any modifier key (such as control, alt, shift, etc.) is handled by the `modifiers_changed` signal. Note that for `KeyEventController`, the left and right mouse buttons are also considered modifiers.\n\nThe signal handler for any of the three signals is handed two arguments, a `KeyCode`, which is the key code constant of the keyboard key that triggered the signals, as well as `ModifierState`. This is an object that holds information about which modifiers are currently pressed. To query it, we use [`control_pressed`](@ref), [`shift_pressed](@ref), [`alt_pressed`](@ref), [`mouse_button_01_pressed`](@ref), and [`mouse_button_02_pressed`](@ref), which return `true` or `false` depending on whether the modifier is currently down.\n\nFor example, to test whether the user pressed the space key while the shift key is held, we could do:\n\n```julia\nfunction on_key_pressed(self::KeyEventController, code::KeyCode, modifier_state::ModifierState) ::Nothing\n    if code == KEY_space && shift_pressed(modifier_state)\n        println(\"shift + space pressed\")\n    end\nend\n\nkey_controller = KeyEventController()\n\nconnect_signal_key_pressed!(on_key_pressed, key_controller)\nadd_controller!(window, key_controller)\n```\n\nWhile we would connect to the `KeyEventController`s other signals like so:\n\n```julia\nfunction on_key_released(self::KeyEventController, code::KeyCode, modifier_state::ModifierState) ::Nothing\n    # handle key here\nend\n\nfunction on_modifiers_changed(self::KeyEventController, modifier_state::ModifierState) ::Nothing\n    # handle modifiers here\nend\n\nconnect_signal_key_released!(on_key_released, key_controller)\nconnect_signal_modifiers_changed!(on_modifiers_changed, key_controller)\n```\n\n`KeyEventController` should be used if we have to monitor both pressing **and releasing** a key or key combination. If all we want to do is trigger behavior when the user presses a combination once, we should use `ShortcutEventController` instead.\n\n## Detecting Key Bindings: ShortcutEventController\n\nRecall that in the [chapter on actions](./03_actions.md), we learned that a shortcut trigger, or keybinding, is made up of any number of modifier keys, along with exactly one non-modifier. \n\nTo react to the user pressing such a shortcut, we should use [`ShortcutEventController`](@ref), which has an easier-to-use and more flexible interface than `KeyEventController`. \n\nWe first need an `Action`, which we associate a shortcut trigger with:\n\n```julia\naction = Action(\"shortcut_controller.example\", app) do self::Action\n    println(\"shift + space pressed\")\nend\nadd_shortcut!(action, \"<Shift>space\")\n```\n\nWhere `app` is an `Application` instance.\n\nWe can then create a `ShortcutEventController` instance and call `add_action!`, after which it will listen for the associated keybinding of that action. Any number of actions can be added to a `ShortcutEventController`, and they can be removed at any time using [`remove_action!`](@ref).\n\nFor the shortcut controller to start receiving events, we also need to connect it to a widget:\n\n```julia\nshortcut_controller = ShortcutEventController()\nadd_action!(shortcut_controller, action)\nadd_controller!(window, shortcut_controller)\n```\n\nWhere `window` is the top-level window. Note that `ShortcutEventController` does not have any signals to connect to it. Instead, it automatically listens for shortcuts depending on which `Action` we added.\n\nThis mechanism is far more automated than having to manually check from within a `KeyEventController` signal handler if the current combination of buttons should trigger a shortcut action. This is why `ShortcutEventController` should be preferred in situations where we do not care about individual keystrokes.\n\n---\n\n## Cursor Motion: MotionEventController\n\nNow that we know how to handle keyboard events, we will turn our attention to mouse-based events. There are two types\nof events a mouse can emit, **cursor motion**  and **mouse button presses**. These are handled by two different controllers.\n\nFor cursor motion, the event controller is called [`MotionEventController`](@ref), which has 3 signals:\n\n```@eval\nusing Mousetrap\nreturn Mousetrap.@signal_table(MotionEventController,\n    motion_enter,\n    motion,\n    motion_leave\n)\n```\n\n`motion_enter` is emitted once when the cursor enters the widget area, then, once per frame, `motion` is emitted to update us about the current cursor position. Lastly, when the cursor leaves the widget's allocated area, `motion_leave` is emitted exactly once.\n\n`motion_enter` and `motion` supply us with the current cursor position, which is relative to the current widget's origin, in pixels. That is, for a widget whose top-left corner (its origin) is at position `(widget_position_x, widget_position_y)`,  if the coordinate supplied by the signals is `(x, y)`, then the cursor is at position `(widget_position_x + x, widget_position_y + y)`, in pixels. \n\nShown here is an example of how to connect to these signals, where we forwarded `window`, the host widget of the controller, as the `data` argument of signal `motion`, to calculate the absolute position of the cursor on screen.\n\n```julia\nfunction on_motion(::MotionEventController, x::AbstractFloat, y::AbstractFloat, data::Widget) ::Nothing\n    widget_position = get_position(data)\n    cursor_position = Vector2f(x, y)\n\n    println(\"Absolute Cursor Position: $(widget_position + cursor_position)\")\nend\n\nwindow = Window(app)\nmotion_controller = MotionEventController()\n\nconnect_signal_motion!(on_motion, motion_controller, window)\nadd_controller!(window, motion_controller)\n```\n\nWhile we would connect to `MotionEventController`s other signals like so:\n\n```julia\nfunction on_motion_enter(::MotionEventController, x::AbstractFloat, y::AbstractFloat) ::Nothing\n    # handle cursor enter\nend\n\nfunction on_motion_leave(::MotionEventController) ::Nothing\n    # handle cursor leave\nend\n\nconnect_signal_motion_enter!(on_motion_enter, motion_controller, window)\nconnect_signal_motion_leave!(on_motion_leave, motion_controller, window)\n```\n\n---\n\n## Mouse Button Presses: ClickEventController\n\nWith cursor movement taken care of, we now turn our attention to handling the other type of mouse event: button presses.\n\nA mouse button is... any button on a mouse, which is less intuitive than it sounds. Mice are hugely varied, with some having exactly one button, while some mice have six or more buttons. If the user uses no mouse at all, for example, when choosing to control the app with a trackpad, trackball, or touchscreen, touchscreen \"taps\" will be registered as if the left mouse button was pressed.\n\nWe track mouse button presses with [`ClickEventController`](@ref) which has 3 signals:\n\n```@eval\nusing Mousetrap\nreturn Mousetrap.@signal_table(ClickEventController,\n    click_pressed,\n    click_released,\n    click_stopped\n)\n```\n\nMuch like with `MotionEventController`, the signals provide the handler with `x` and `y`, the position of the cursor at the time the click happened, relative to the host widget's origin, in pixels.\n\nThe first argument for two of the signals `click_pressed` and `click_released`, `n_presses`, is the number of presses in the current click sequence. For example, `n_presses = 2` means that this is the second time a mouse button was pressed in sequence, `click_stopped` is emitted when that sequence of clicks has ended. It may be helpful to consider an example:\n\nLet's say the user clicks the left mouse button two times total, then stops clicking. This will emit the following events in this order:\n\n| Order | Signal ID | `n_pressed` value |\n|--------|-----------|------------------|\n| 1 | `click_pressed` | 1 |\n| 2 | `click_released` | 1 |\n| 3 | `click_pressed` | 2 |\n| 4 | `click_released` | 2 |\n| 5 | `click_stopped`| (none) |\n\nThanks to the `n_pressed` argument, we can easily handle double clicks without any external function keeping track of how often the user has clicked so far. The delay after which a click sequence stops is system-dependent and usually decided by the user's operating system, not Mousetrap.\n\n### Differentiating Mouse Buttons\n\n`ClickEventController` is one of a few event controllers that are also a sub-type of [`SingleClickGesture`](@ref). This interface provides functionality that lets us distinguish between different mouse buttons. Mousetrap supports up to 9 different mouse buttons, identified by the enum [`ButtonID`](@ref):\n\n+ `BUTTON_ID_BUTTON_01` is usually the left mouse button (or a touchpad tap)\n+ `BUTTON_ID_BUTTON_02` is usually the right mouse button\n+ `BUTTON_ID_ANY` is used as a catch-all for all possible mouse buttons\n+ `BUTTON_ID_BUTTON_03 - BUTTON_ID_BUTTON_09` are additional mouse-model-specific buttons\n+ `BUTTON_ID_NONE` is none of the above\n\nTo check which mouse button was pressed, we use [`get_current_button`](@ref) on the event controller instance from within the signal handler, which returns an ID as stated above.\n\nIf we only want signals to be emitted for certain buttons, we can use [`set_only_listens_to_button!`](@ref) to restrict the choice of button. [`set_touch_only!`](@ref) filters all click events except those coming from touch devices.\n\nAs an example, if we want to check if the user pressed the left mouse button twice, we can do the following:\n\n```julia\nfunction on_click_pressed(self::ClickEventController, n_presses::Integer, x::AbstractFloat, y::AbstractFloat) ::Nothing\n    if n_presses == 2 && get_current_button(self) == BUTTON_ID_BUTTON_01\n        println(\"double click registered at ($x, $y)\")\n    end\nend\n\nclick_controller = ClickEventController()\nconnect_signal_click_pressed!(on_click_pressed, click_controller)  \nadd_controller!(window, click_controller)\n```\n\nWhile we would connect to `ClickEventController`s other signals like so:\n\n```julia\nfunction on_click_released(self::ClickEventController, n_presses::Integer, x::AbstractFloat, y::AbstractFloat) ::Nothing\n    # handle button up\nend\n\nfunction on_click_stopped(self::ClickEventController) ::Nothing\n    # handle end of a series of clicks\nend\n\nconnect_signal_click_released!(on_click_released, click_controller)\nconnect_signal_click_stopped!(on_click_stopped, click_controller)\n```\n\nWhile `ClickEventController` gives us full control over one or more clicks, there is a more specialized\ncontroller for a similar, but slightly different gesture: *long presses*.\n\n---\n\n## Long-Presses: LongPressEventController\n\n[`LongPressEventController`](@ref) reacts to a specific sequence of events, called a **long press** gesture. This gesture is recognized when the user presses a mouse button, and then keeps that button depressed without moving the cursor. After enough time has passed, `LongPressEventController` will emit its signals:\n\n```@eval\nusing Mousetrap\nreturn Mousetrap.@signal_table(LongPressEventController,\n    pressed,\n    press_cancelled\n)\n```\n\nWhere `pressed` is emitted in the same frame the long press is recognized. If the user releases the button before this time threshold is met, `press_cancelled` is emitted instead.\n\nSimilar to `clicked`, `LongPressEventController` provides us with the location of the cursor, relative to the host widget's origin.\n\n`LongPressEventController`, like `ClickEventController`, subtypes `SingleClickGesture`, which allows us to differentiate between different mouse buttons or a touchscreen, just as before.\n\n```julia\nfunction on_pressed(self::LongPressEventController, x::AbstractFloat, y::AbstractFloat) ::Nothing\n    println(\"long press registered at ($x, $y)\")\nend\n\nfunction on_press_cancelled(self::LongPressEventController) ::Nothing\n    println(\"long press aborted\")\nend\n\nlong_press_controller = LongPressEventController()\nconnect_signal_pressed!(on_pressed, long_press_controller)\nconnect_signal_press_cancelled!(on_press_cancelled, long_press_controller)\nadd_controller!(window, long_press_controller)\n```\n\n---\n\n## Click-Dragging: DragEventController\n\nA long press is a gesture in which a user clicks a mouse button, does not move the cursor, then **holds that position** for an amount of time. In contrast, **click-dragging** is slightly different: the user clicks a mouse button, holds it, but **does move the cursor**. This is often used to \"drag and drop\" an UI element, such as dragging a file icon from one location to another or dragging the knob of a `Scale`-like widget.\n\nClick-dragging gestures are automatically recognized by [`DragEventController`](@ref), which has three signals:\n\n```@eval\nusing Mousetrap\nreturn Mousetrap.@signal_table(DragEventController,\n    drag_begin,\n    drag,\n    drag_end\n)\n```\n\nWhen a click-drag is first recognized, `drag_begin` is emitted. In each frame the drag is taking place, `drag` is emitted once per frame to update us about the cursor position. Once the gesture ends, `drag_end` is emitted exactly once. \n\nAll three signals supply two additional arguments with signal-specific meaning:\n\n+ `scroll_begin` supplies the cursor location, relative to the host widget's origin, in pixels\n+ `scroll` and `scroll_end` supply the **offset** between the current cursor position, and the position at the start of the gesture, in pixels\n\nTo get the current position of the cursor, we have to add the offset from `scroll` or `scroll_end` to the initial position. We can get the initial position either in `scroll_begin`, or by calling [`get_start_position`](@ref).\n\nTo track the cursor position during a drag gesture, we can connect to `DragEventController`s signals like so:\n\n```julia\nfunction on_drag_begin(self::DragEventController, start_x::AbstractFloat, start_y::AbstractFloat) ::Nothing\n    println(\"drag start: ($start_x, $start_y)\")\nend\n\nfunction on_drag(self::DragEventController, x_offset::AbstractFloat, y_offset::AbstractFloat) ::Nothing\n    start = get_start_position(self)\n    println(\"drag update: ($(start.x + x_offset), $(start.y + y_offset)))\")\nend\n\nfunction on_drag_end(self::DragEventController, x_offset::AbstractFloat, y_offset::AbstractFloat) ::Nothing\n    start = get_start_position(self)\n    println(\"drag end: ($(start.x + x_offset), $(start.y + y_offset)))\")\nend\n\ndrag_controller = DragEventController();\nconnect_signal_drag_begin!(on_drag_begin, drag_controller)\nconnect_signal_drag!(on_drag, drag_controller)\nconnect_signal_drag_end!(on_drag_end, drag_controller)\n\nadd_controller!(window, drag_controller)\n```\n---\n\n## Panning: PanEventController\n\n**Panning**  is similar to dragging, in that the user presses the mouse button (or touchscreen), and then holds the button while moving the cursor to a different location. The difference between panning and click-dragging is that **panning can only occur along exactly one of the two axes**: left-right (the x-axis) or top-bottom (the y-axis). This is commonly used in touchscreen UIs, for example, the user may scroll horizontally by using a pan gesture as opposed to a scroll wheel or 2-finger swipe.\n\nPanning is handled by the appropriately named [`PanEventController`](@ref), which is the first controller in this chapter that takes an argument to its constructor. We supply an [`Orientation`](@ref), which decides along which axis the controller should listen to panning for, `ORIENTATION_HORIZONTAL` for the x-axis, `ORIENTATION_VERTICAL` for the y-axis.\n\nA pan controller cannot listen to both axes at once, though we can connect two controllers, one for each axis, to the same widget to emulate this behavior.\n\n`PanEventController` only has one signal:\n\n```@eval\nusing Mousetrap\nreturn Mousetrap.@signal_table(PanEventController,\n    pan\n)\n```\n\nWhich is emitted once per frame while the gesture is active.\n\n[`PanDirection`](@ref) is an enum with four values: `PAN_DIRECTION_LEFT`, `PAN_DIRECTION_RIGHT`, `PAN_DIRECTION_UP` and `PAN_DIRECTION_DOWN`. If the orientation was set to `ORIENTATION_HORIZONTAL`, only `PAN_DIRECTION_LEFT` and `PAN_DIRECTION_RIGHT` can occur, and vice-versa for `ORIENTATION_VERTICAL`.\n\nThe second argument is the current offset, that is, the distance between the current position of the cursor and the position at which the gesture was first recognized, relative to the host widget's origin, in pixels.\n\n```julia\nfunction on_pan(self::PanEventController, direction::PanDirection, offset::AbstractFloat) ::Nothing\n    if direction == PAN_DIRECTION_LEFT\n        println(\"panning left by $offset\")\n    elseif direction == PAN_DIRECTION_RIGHT\n        println(\"panning right by $offset\")\n    end\nend\n\npan_controller = PanEventController(ORIENTATION_HORIZONTAL)\nconnect_signal_pan!(on_pan, pan_controller)\nadd_controller!(window, pan_controller)\n```\n\n---\n\n## Mousewheel-Scrolling: ScrollEventController\n\nOther than clicking and cursor movement, many mice have a third function: scrolling. This is usually done with a designated wheel, though some operating systems also recognize scroll gestures using a trackpad or touchscreen. Either way, scroll events are registered by [`ScrollEventController`](@ref), which has four signals:\n\n```@eval\nusing Mousetrap\nreturn Mousetrap.@signal_table(ScrollEventController,\n    scroll_begin,\n    scroll,\n    scroll_end,\n    kinetic_scroll_decelerate\n)\n```\n\nThree of these are fairly straightforward, when the user starts scrolling, `scroll_begin` is emitted once. As the user keeps scrolling, `scroll` is emitted every frame, updating us about the current position of the scroll wheel. The two additional arguments to the `scroll` signal handler, `delta_x` and `delta_y` are the vertical and horizontal offset, relative to the cursor position at which the scroll gesture was first recognized. \n\nMany systems support both vertical and horizontal scrolling (usually by clicking the mouse scroll wheel), which is why we get two offsets. \n\nWhen the user stops scrolling, `scroll_end` is emitted once.\n\nIf we want to keep track of how far the user has scrolled a widget that had a `ScrollEventController` connect, we do the following:\n\n```julia\n# variable to keep track of distance scrolled\ndistance_scrolled = Ref{Vector2f}(Vector2f(0, 0))\n\n# at the start of scroll, set sum to 0\nfunction on_scroll_begin(self::ScrollEventController) ::Nothing\n    global distance_scrolled[] = Vector2f(0, 0)\n    println(\"scroll start\")\nend\n\n# each frame, increase distance scrolled\nfunction on_scroll(self::ScrollEventController, x_delta::AbstractFloat, y_delta::AbstractFloat) ::Nothing\n    global distance_scrolled[] += Vector2f(x_delta, y_delta)\n    return nothing\nend\n\n# at the end of scroll, print distance\nfunction on_scroll_end(self::ScrollEventController) ::Nothing\n    println(\"scrolled ($(distance_scrolled[].x), $(distance_scrolled[].y))\")\nend\n\n# instance controller and connect signals\nscroll_controller = ScrollEventController()\nconnect_signal_scroll_begin!(on_scroll_begin, scroll_controller)\nconnect_signal_scroll!(on_scroll, scroll_controller)\nconnect_signal_scroll_end!(on_scroll_end, scroll_controller)\n\nadd_controller!(window, scroll_controller)\n```\n\nWhere we used a global [`Ref`](https://docs.julialang.org/en/v1/base/c/#Core.Ref) to safely reference the value of `distance_scrolled` from within the signal handlers.\n\n### Kinetic Scrolling\n\n`ScrollEventController` has a fourth signal that reacts to **kinetic scrolling**. Kinetic scrolling is a feature of modern UI, where the scroll movement simulates inertia. When the user triggers scrolling, the widget will continue scrolling even when the user is no longer touching the screen. The \"friction\" of the scrolling widget will slowly reduce the scroll velocity until it comes to a stop. `kinetic_scroll_decelerate` is emitted during this period, after the user has stopped touching the screen, but before the widget has a scroll velocity of 0. Its additional arguments `x_velocity` and `y_velocity` inform us of the velocity the widget should currently be scrolling at.\n\nTo allow for kinetic scrolling, we need to enable it using [`set_kinetic_scrolling_enabled!`](@ref), then connect to the appropriate signal:\n\n```julia\n# enable kinetic scrolling\nset_kinetic_scrolling_enabled!(scroll_controller, true)\n\n# signal handler\nfunction on_kinetic_scroll_decelerate(::ScrollEventController, x_velocity::AbstractFloat, y_velocity::AbstractFloat) ::Nothing\n    println(\"kinetically scrolling with velocity of ($x_velocity, $y_velocity)\")\nend\n\n# connect handler\nconnect_signal_kinetic_scroll_decelerate!(on_kinetic_scroll_decelerate, scroll_controller)\n```\n\n---\n\n## Pinch-Zoom: PinchZoomEventController\n\nWhile `MotionEventController`, `ClickEventController`, etc. recognize both events from a mouse and touchscreen, Mousetrap offers some touch-only gestures, though many trackpads also support them. These are usually gestures performed using two fingers, the first of which is **pinch-zoom**. Pinch-zoom is when the user places two fingers on the touchscreen, then moves either, such that the distance between the fingers changes. This gesture is commonly used to zoom a view in or out. \n\nIt is recognized by [`PinchZoomEventController`](@ref), which only has one signal:\n\n```@eval\nusing Mousetrap\nreturn Mousetrap.@signal_table(PinchZoomEventController,\n    scale_changed\n)\n```\n\nThe argument `scale` is a *relative* scale, where `1` means no change between the distance of the fingers when compared to the distance at the start of the gesture, `0.5` means the distance halved, `2` means the distance doubled.\n\n`scale_changed` is usually emitted once per frame after the gesture is first recognized. Applications should react to every tick, as opposed to only the last. This will make the application feel more responsive and create a better user experience.\n\nTo detect whether a user is currently zooming out (pinching) or zooming in, we could do the following:\n\n```julia\nfunction on_scale_changed(self::PinchZoomEventController, scale::AbstractFloat) ::Nothing\n    if scale < 1\n        println(\"zooming in\")\n    elseif scale > 1\n        pintln(\"zooming out\")\n    end\nend\n\nzoom_controller = PinchZoomEventController()\nconnect_signal_scale_changed!(on_scale_changed, zoom_controller)\nadd_controller!(window, zoom_controller)\n```\n\n---\n\n## 2-Finger Rotate: RotateEventController\n\nAnother touch-only gesture is the **two-finger-rotate**. With this gesture, the user places two fingers on the touchscreen and then rotates them around a constant point in between the two fingers. This is commonly used to change the angle of something.\n\nThis gesture is handled by [`RotateEventController`](@ref), which has one signal:\n\n```@eval\nusing Mousetrap\nreturn Mousetrap.@signal_table(RotateEventController,\n    rotation_changed\n)\n```\n\nIt takes two arguments: `angle_absolute` and `angle_delta`. `angle_absolute` provides the current angle between the two fingers. `angle_delta` is the difference between the current angle and the angle at the start of the gesture.  Both `angle_absolute` and `angle_delta` are provided in radians, to convert them we can use [`Mousetrap.Angle`](@ref):\n\n```julia\nfunction on_rotation_changed(self::RotateEventController, angle_delta, angle_absolute) ::Nothing\n    # convert to unit-agnostic Mousetrap.Angle\n    absolute = radians(angle_absolute)\n    delta = radians(angle_delta)\n\n    println(\"Current Angle: $(as_degrees(absolute))°\")\nend\n\nrotation_controller = RotateEventController()\nconnect_signal_rotation_changed!(on_rotation_changed, rotation_controller)\nadd_controller!(window, rotation_controller)\n```\n\n---\n\n## Swipe: SwipeEventController\n\nThe last touch-only gesture is **swiping**, which is very similar to click-dragging, except with two fingers. Swiping is often used to \"flick\" through pages, for example, those of a `Stack`.\n\nSwiping is recognized by [`SwipeEventController`](@ref), which also only has one signal:\n\n```@eval\nusing Mousetrap\nreturn Mousetrap.@signal_table(SwipeEventController,\n    swipe\n)\n```\n\nThe signal handler provides two arguments, `x_velocity` and `y_velocity`, which describe the velocity along both the x- and y-axis. \n\nTo illustrate how to deduce the direction of the swipe, consider this example:\n\n```julia\nfunction on_swipe(self::SwipeEventController, x_velocity::AbstractFloat, y_velocity::AbstractFloat) ::Nothing\n    print(\"swiping \")\n    if (y_velocity < 0)\n        print(\"up \")\n    elseif (y_velocity > 0)\n        print(\"down \")\n    end\n    \n    if (x_velocity < 0)\n        println(\"left\")\n    elseif (x_velocity > 0)\n        println(\"right\")\n    end\nend\n\nswipe_controller = SwipeEventController()\nconnect_signal_swipe!(on_swipe, swipe_controller)  \nadd_controller!(window, swipe_controller)\n```\n\nUIs should react to both the direction and magnitude of the vector, even though the latter is ignored in this example.\n\n---\n\n## Touchpad & Stylus: StylusEventController\n\nLastly, we have a highly specialized event controller. Common in illustration and animation, the **touchpad stylus** is usually a pen-like device that is used along with a **touchpad**, which may or may not be the screen of the device. \n\nLike with mice, there is a huge variety of stylus models. Shared among all models is a detection mechanism for whether the pen is **currently touching**, **not touching**, or **about to touch** the touchpad, along with a way to determine the position of the tip of the stylus.\n\nAdditional features such as pressure or angle detection are manufacturer-specific. Many of these additional features are also recognized by [`StylusEventController`](@ref), the event controller used to handle these kinds of devices.\n\n`StylusEventController` has four signals:\n\n```@eval\nusing Mousetrap\nreturn Mousetrap.@signal_table(StylusEventController,\n    stylus_up,\n    stylus_down,\n    proximity,\n    motion\n)\n```\n\nWe recognize signal `motion` from `MotionEventController`. It behaves [exactly the same](#cursor-motion-motioneventcontroller), where `x` and `y` are the cursor position, in widget space. \n\nThe three other signals are used to react to the physical distance between the stylus and touchpad. `stylus_down` is emitted when the pen's tip makes contact with the touchpad, `stylus_up` is emitted when this contact is broken, `proximity` is emitted when the stylus is about to touch the touchpad, or just left the touchpad.\n\n```julia\nfunction on_stylus_up(self::StylusEventController, x::AbstractFloat, y::AbstractFloat) ::Nothing\n    println(\"stylus is no longer touching touchpad, position: ($x, $y)\")\nend\n\nfunction on_stylus_down(self::StylusEventController, x::AbstractFloat, y::AbstractFloat) ::Nothing\n    println(\"stylus is ow touching touchpad, position: ($x, $y)\")\nend\n\nfunction on_proximity(self::StylusEventController, x::AbstractFloat, y::AbstractFloat) ::Nothing\n    println(\"stylus entered proximity range, position: ($x, $y)\")\nend\n\nfunction on_motion(self::StylusEventController, x::AbstractFloat, y::AbstractFloat) ::Nothing\n    println(\"stylus position: ($x, $y)\")\nend\n\nstylus_controller = StylusEventController()\nconnect_signal_stylus_up!(on_stylus_up, stylus_controller)\nconnect_signal_stylus_down!(on_stylus_down, stylus_controller)\nconnect_signal_proximity!(on_proximity, stylus_controller)\nconnect_signal_motion!(on_motion, stylus_controller)\n\nadd_controller!(window, stylus_controller)\n```\n\n### Stylus Axis\n\nNone of the above-mentioned signals provide information about additional stylus sensors. Because not all devices share these features, the mechanism for querying these are different. Each sensor provides a floating point value within a given range. This range is called a **device axis**. Axes are described by the enum [`DeviceAxis`](@ref), whose values identify all types of axes recognized by Mousetrap.\n\nWe can query the value of each axis using [`get_axis_value`](@ref). This function will return 0 if the axis is not present. If it is, it will return the device-supplied axis-specific value. \n\nTo check whether a device has a specific axis, we use [`has_axis`](@ref).\n\nMousetrap recognizes the following axes:\n\n| `DeviceAxis` value | Meaning                                                           | \n|--------------------|-------------------------------------------------------------------|\n| `DEVICE_AXIS_X`                | x-position of the pen                                          |\n| `DEVICE_AXIS_Y`                | y-position of the pen                                          |\n| `DEVICE_AXIS_DELTA_X`          | delta of horizontal scrolling                                     |\n| `DEVICE_AXIS_DELTA_Y`          | delta of vertical scrolling                                       |\n| `DEVICE_AXIS_PRESSURE`         | pressure sensor of the stylus' tip                                 |\n| `DEVICE_AXIS_X_TILT`           | angle relative to the screen, x-axis                                            | \n| `DEVICE_AXIS_Y_TILT`           | angle relative to the screen, y-axis                                            |\n| `DEVICE_AXIS_WHEEL`            | state of the stylus' wheel (if present)                           | \n| `DEVICE_AXIS_DISTANCE`         | current distance between the touchpad surface and the stylus' tip |\n| `DEVICE_AXIS_ROTATION`         | angle of the pen around the normal                                |\n| `DEVICE_AXIS_SLIDER`           | value of the pen's slider (if present)                             |\n\n### Stylus Mode\n\nSome styluses have a \"mode\" function, where the user can choose between different pen modes. This is driver-specific, and not all devices support this feature. For those that do, we can use [`get_tool_type`](@ref) to check which mode is currently selected. The recognized modes are:\n\n| `ToolType` value | Meaning                   | \n|--------------------|---------------------------|\n| `TOOL_TYPE_PEN`              | basic drawing tip         |\n| `TOOL_TYPE_ERASER`           | eraser                    |\n| `TOOL_TYPE_BRUSH`            | variable-width drawing tip |\n| `TOOL_TYPE_PENCIL`           | fixed-width drawing tip   |\n| `TOOL_TYPE_AIRBRUSH`         | airbrush tip              |\n| `TOOL_TYPE_LENS`             | zoom tool                 | \n| `TOOL_TYPE_MOUSE`            | cursor tool               |\n| `TOOL_TYPE_UNKNOWN`          | none of the above         |\n\nIf the stylus does not support modes, `TOOL_TYPE_UNKNOWN` will be returned.\n\n"
  },
  {
    "path": "docs/src/01_manual/06_image.md",
    "content": "# Chapter 6: Images\n\nIn this chapter, we will learn:\n+ How to use colors in Mousetrap\n+ How to present the user with a color chooser dialog\n+ How to load, store, modify, and display 2D images\n\n---\n\n## Introduction\n\nMousetrap was originally written as the GUI engine for an unreleased frame-by-frame animation app. This history is why it contains a fully-featured image processing suite, making it well-suited for image-editing applications right out of the box.\n\n## Colors\n\nMousetrap offers two color representations, [`RGBA`](@ref) and [`HSVA`](@ref), which have the following components:\n\n| representation | component   | meaning         |\n|----------------|-------------|-----------------|\n| `RGBA`         | `r`         | red             |\n| `RGBA`         | `g`         | green           |\n| `RGBA`         | `b`         | blue            |\n| `RGBA`         | `a`         | opacity (alpha) |\n| ----------     | ----------- | --------        |\n| `HSVA`         | `h`         | hue             |\n| `HSVA`         | `s`         | saturation      |\n| `HSVA`         | `v`         | value (chroma)  |\n| `HSVA`         | `a`         | opacity (alpha) |\n\nFor more information on these color systems, see [here for RGBA](https://en.wikipedia.org/wiki/RGBA_color_model) and [here for HSVA](https://en.wikipedia.org/wiki/HSL_and_HSV).\n\nFor both representations, all components are 32-bit floats in `[0, 1]`. The **alpha** component is also called **opacity**, which is the inverse of *transparency*. An alpha value of 1 means the color is fully opaque, a value of 0 means it is fully transparent, making it invisible when displayed on screen.\n\n### Converting Colors\n\nWe can freely convert between `RGBA` and `HSVA`. To do this, we use [`rgba_to_hsva`](@ref) and [`hsva_to_rgba`](@ref):\n\n```julia\nrgba = RGBA(0.1, 0.2, 0.3, 0.4)\nas_hsva = rgba_to_hsva(rgba)\nas_rgba = hsva_to_rgba(as_hsva)\n@assert rgba == as_rgba # true\n```\n\n### Color to Hexadecimal\n\nMousetrap offers a function to convert `RGBA` to its HTML color code. This code is a a string of the form `#RRGGBB`, where `RR` is the red, `GG` the green, and `BB` the blue component, in unsigned 8-bit hexadecimal. For example, the color `RGBA(1, 0, 1, 1)` would have the HTML-code `#FF00FF`, where the alpha component was omitted. Using `html_code_to_rgba` and `rgba_to_html_code`, we can freely convert between a colors in-memory and hexadecimal representation.\n\nFor example, if we want to use an `Entry` for the user to be able to enter a color as an HTML color code, we could do the following:\n\n```julia\nentry = Entry()\nconnect_signal_activate!(entry) do self::Entry\n    text = get_text(self)\n    println(text)\n    if is_valid_html_code(text)\n        println(\"User entered: $(html_code_to_rgba(text))\")\n    else\n        # handle malformatted string\n    end\n    return nothing\nend\n```\n\nIf parsing was successful, `is_valid_html_code` will return `true`, at which point we can be sure that `html_code_to_rgba` will return a valid color.\n\n---\n\n## Color Chooser\n\nWhile manual entry like this works, it is hardly very user-friendly. For a more intuitive way to have our users select a color, Mousetrap offers a purpose-built dialog: [`ColorChooser`](@ref). \n\n`ColorChooser`s constructor takes the title of the window as its only argument. After initialization, we can show the dialog to the user by calling `present!`, just like with a `Window`:\n\n```julia\ncolor_chooser = ColorChooser(\"Choose Color\")\npresent!(color_chooser)\n```\n\n![](../assets/color_chooser_selection.png)\n\nIf the user clicks on the `+` in the bottom left corner, they are taken to a new page that lets them select each component of the color directly:\n\n![](../assets/color_chooser_custom_color.png)\n\nTo actually trigger behavior once the user selects a color, we need to register a *callback*. `ColorChooser` has two callbacks, one invoked when the user makes the selection by clicking \"select\", and another when the user dismisses the dialog, for example by closing its window.\n\nWe register the former using `on_accept!`, which requires a function with the signature:\n```\n(::ColorChooser, color::RGBA, [::Data_t]) -> Nothing\n```\n\nWhere `color` will be the color the user selected.\n\nThe function called when the dialog is dismissed is registered using `on_cancel!`, which requires a callback with the signature:\n```\n(::ColorChooser, [::Data_t]) -> Nothing\n```\n\nWe would use these two functions like so:\n\n```julia\ncolor_chooser = ColorChooser(\"Choose Color\")\n\n# react to user selection\non_accept!(color_chooser) do self::ColorChooser, color::RGBA\n    println(\"Selected $color\")\nend\n\n# react to use dismissing the dialog\non_cancel!(color_chooser) do self::ColorChooser\n    println(\"color selection canceleld\")\nend\n\npresent!(color_chooser)\n```\n \nAt any point, we can also access the last selected color by calling [`get_color`](@ref) on the `ColorChooser` instance.\n\n---\n\n## Images\n\nNow that we know how to handle colors, we continue onto images. In general, an image is a two-dimensional matrix of colors. Each element in the matrix is called a **pixel**. An image of size 400x300 will have 400 * 300 = 120000 pixels. Each pixel is a color in `RGBA` format.\n\nImages are represented by the [`Image`](@ref) class. This class is not a widget or signal emitter; it is simply a way to manage memory (in the form of a pixel matrix) in RAM. If we want to show an image on screen, we need the help of other widgets.\n\n### Creating an Image\n\n#### Loading an Image from Disk\n\nMost commonly, we will want to load an image from an already existing file. This can be achieved with [`create_from_file!`](@ref), which takes the path as a string:\n\n```julia\nimage = Image()\ncreate_from_file!(image, \"/path/to/image.png\");\n```\n\n#### Supported Image Formats\n\nThe following image formats are supported:\n\n| Format Name             | File Extensions |\n|-------------------------|-----------------|\n| PNG                     | `.png`  |\n| JPEG                    | `.jpeg` `.jpe` `.jpg`  |\n| JPEG XL image           | `.jxl`  |\n| Windows Metafile        | `.wmf` `.apm`  |\n| Windows animated cursor | `.ani`  |\n| BMP                     | `.bmp`  |\n| GIF                     | `.gif`  |\n| MacOS X icon            | `.icns`  |\n| Windows icon            | `.ico` `.cur`  |\n| PNM/PBM/PGM/PPM         | `.pnm` `.pbm` `.pgm` `.ppm`  |\n| QuickTime               | `.qtif` `.qif`  |\n| Scalable Vector Graphics | `.svg` `.svgz` `.svg.gz`  |\n| Targa                   | `.tga` `.targa`  |\n| TIFF                    | `.tiff` `.tif`  |\n| WebP                    | `.webp`  |\n| XBM                     | `.xbm`  |\n| XPM                     | `.xpm`  |\n\nIt is recommended to use `Image` only for raster-based file types. While possible, for vector-graphics-based types like `.ico` and `.svg`, Mousetrap offers another more specialized class, [`Icon`](@ref), which we will learn about in the [next chapter](./07_os_interface.md). \n\n#### Creating an Image from Scratch\n\nSometimes, we want to fill an image with our custom image data programmatically. For this, we use [`create!`](@ref), which allocates an image of a given size and fills each pixel with the color supplied as an optional argument. \n\nFor example, the following allocates an image of size 400x300, then sets every pixel to red (`RGBA(1, 0, 0, 1)`):\n\n```cpp\nimage = Image()\ncreate!(image, 400, 300, RGBA(1, 0, 0, 1));\n```\n\nIf unspecified, the image will be filled with `RGBA(0, 0, 0, 0)`, making it appear fully transparent when displayed using a widget.\n\n### Modifying an Image\n\nAn all-red image will usually not be very useful, of course. To overwrite a single pixel, we use [`set_pixel!`](@ref), which takes as its first argument the pixel coordinate (1-based, with the origin at the top-left, just like a matrix), along with a color.\n\nWe can access any pixel using [`get_pixel`](@ref), which only takes the pixel coordinates. If the coordinates are out of range, `RGBA(0, 0, 0, 0)` will be returned.\n\n```julia\n# set the alpha component of the pixel at 32, 32 to zero\nimage = # ...\ncolor = get_pixel(image, 32, 32)\ncolor.a = 0\nset_pixel!(image, 32, 32, color)\n```\n\n### Scaling\n\nTo change an image's size, we have two options: **scaling**  the image or **cropping** it. These operations work identically to those in common image-manipulation programs, such as GIMP or Photoshop.\n\nTo scale an image, we call [`as_scaled`](@ref). This function returns a new image; it **does not modify the original image**. For example, scaling our 400x300 image to 800x600:\n\n```cpp\nimage = // ... 400x300 image\nscaled = as_scaled(image, 800, 600);\n```\n\nOnly `scaled` will be of size `800x600`, `image` has not changed.\n\n#### Interpolation \n\nWhen scaling, we have a choice of scaling algorithm that fills in newly generated pixels through **interpolation**. Mousetrap offers four interpolation types, supplied by the enum [`InterpolationType`](@ref). Below, we see how each type affects the final image, where the image labeled with `1x` is the original image with a resolution of 10x10 pixels.\n\n![](../assets/interpolation_nearest.png) \n`INTERPOLATION_TYPE_NEAREST`\n\n![](../assets/interpolation_bilinear.png) \n`INTERPOLATION_TYPE_BILINEAR`\n\n![](../assets/interpolation_hyperbolic.png) \n`INTERPOLATION_TYPE_HYPERBOLIC`\n\n![](../assets/interpolation_tiles.png) \n`INTERPOLATION_TYPE_TILES`\n\nThe main difference between `INTERPOLATION_TYPE_BILINEAR` and `INTERPOLATION_TYPE_HYPERBOLIC` is that of performance. Hyperbolic interpolation offers superior smoothing but does so at about 1.5 times the speed when compared to bilinear interpolation, meaning hyperbolic is about 50% slower. For an image this small, this will hardly matter, but when working with very large images, this can be a difference of seconds.\n\nIf no interpolation type is specified when calling `as_scaled`, `INTERPOLATION_TYPE_TILES` will be chosen as the default.\n\n### Cropping\n\nTo crop an image, we use [`as_cropped`](@ref). Similar to `as_scaled`, this function returns a newly allocated image, it does not modify the original image.\n\n`as_cropped` takes 4 arguments, the new width and height, and the x- and y-**offset**. The offset specifies which pixel is used as the new top-left coordinate of the cropped image. This offset can be negative. We can also specify a new resolution greater than that of the current image. Any newly allocated space that is not part of the original image will be filled with `RGBA(0, 0, 0, 0)`:\n\n```julia\nimage = # ...\n# add a 10 px empty border around the image, keeping the original centered\ncurrent_size = get_size(image)\nresized_image = as_cropped(-10, -10, current_size.x + 10, current_size.y + 10)\n```\n\nCropping like this is often called \"Resize Canvas\" in common image manipulation apps.\n\n### Flipping\n\nLastly, we have [`as_flipped`](@ref) which flips the image along the x- and/or y-axes. Just like before, `as_flipped` returns a newly allocated image and does not modify the original. It takes two booleans, indicating along which axis the image should be flipped:\n\n```julia\nimage = # ...\nhorizontally_flipped = as_flipped(image, true, false)\nvertically_flipped = as_flipped(image, false, true)\n```\n\n### Saving an Image to Disk\n\nHaving edited our image, we can store it on the users' disk using [`save_to_file`](@ref). This function takes a path as a string and returns a boolean indicating whether the operation was successful. The resulting image format will be deduced based on the file extension. For example, to save an image as a `.png` to the location `/assets/export`, we would do:\n\n```julia\nimage = # ...\nsave_to_file(image, \"/assets/export/out.png\")\n```\n\n---\n\n## Displaying Images\n\nNow that we know how to load and manipulate images in memory, we will most likely want a way to display them. We've already seen a widget capable of this: [`ImageDisplay`](@ref). \n\nSo far, we have been using `create_from_file!` to load the image directly from the disk. To create an `ImageDisplay` from an image in memory we use [`create_from_image!`](@ref).\n\n`ImageDisplay` [deep-copies](https://docs.julialang.org/en/v1/base/base/#Base.deepcopy) the contents of the image, the underlying data cannot be modified after this point. This means if we change the original `Image`, `ImageDisplay` **will not change**. To update the `ImageDisplay`, we need to call `create_from_image!` again. \n\nBy default, `ImageDisplay` will expand according to the widget property of the same name. The graphical component of all widgets is expanded using linear interpolation, which may blur images in an undesirable way. To make sure `ImageDisplay` is always at the correct resolution and displays an image 1-to-1 (that is, 1 pixel of the image is exactly 1 pixel on the screen), we can use the following trick:\n\n```julia\nimage = Image()\ncreate_from_file!(image, #= load image of size 400x300 =#)\n\nimage_display = ImageDisplay()\ncreate_from_image!(image_display, image)\n\n# prevent expansion along both dimensions\nset_expand!(image_display, false)\n\n# set minimum size to images original resolution\nset_size_request!(image_display, get_size(image_display))\n```\n\nwhere `get_size` returns the resolution of the image the `ImageDisplay` was created from.\n\nBecause expansion is disabled, `ImageDisplay` will always be exactly the size of its size request, which we set as the original resolution of the underlying image, making it so it will always be exactly 400x300 pixels on screen.\n\n## Updating Images on Screen\n\n`create_from_image!` is a costly operation and would be insufficient to, for example, fluently display an animation at 60fps. We would have to call `create_from_image!` every frame, which is not feasible on most machines.\n\nIn situations like this, we should instead use a custom render widget to display the image as an **OpenGL texture**, which has no problems rendering large, frequently updated images in a performant manner. We will learn more about textures in the [chapter on native rendering](./09_native_rendering.md).\n"
  },
  {
    "path": "docs/src/01_manual/07_os_interface.md",
    "content": "# Chapter 7: Operating System Interface\n\nIn this chapter, we will learn:\n+ What other features `Application` has\n+ How to properly do logging\n+ How to copy / move / create / delete files \n+ How to automatically open a file or URL for the user\n+ How to access a file's metadata\n+ How to monitor a file changing\n+ How to open a dialog that lets users select files\n+ How to open an alert dialog that the user has to dismiss\n+ How to store arbitrary objects in an .ini file\n+ How to load and use icons \n\n---\n\n## Application\n\nWe have already used it many times so far, but [`Application`](@ref), which is a signal emitter, offers several additional functionalities other than just storing our actions for us and keeping track of windows.\n\nIt provides the following signals:\n\n```@eval\nusing Mousetrap\nMousetrap.@signal_table(Application,\n    activate,\n    shutdown\n)\n```\n\nAll initialization of widgets and anything else our app uses should be done inside the `activate` signal handler, while `shutdown` can be used to safely free assets. \n\n[`main`](@ref), which we have used so far instead of connecting to signals, is actually just a convenience function that wraps the following behavior:\n\n```julia\nmain(\"com.example\") do app::Application\n    # behavior here\nend\n\n# is mostly equivalent to\napp = Application(\"com.example\")\nconnect_signal_activate!(app) do app::Application\n    # behavior here\n    return nothing\nend\nrun!(app)\n```\n\n### ID\n\nIn the previous code snippet, `\"com.example\"` is the **application ID**. This ID will be used to identify the application on the user's OS. It should be unique, meaning that no other application on the user's operating system shares this ID. \n\nThe application ID has to contain at least one `.` and should be a human-readable identifier in [RDNN format](https://docs.flatpak.org/en/latest/conventions.html#application-ids). For example, if our app is called \"Foo Image Manipulation Program\" and the app's website domain is `fimp.org`, we should use `org.fimp` as our ID.\n\n!!! warning \"Running two apps with the same ID\"\n    If two applications with the same ID are active at the same time, **they will share assets**.\n\n    This may introduce side effects, if both instances modify the same internal variable or widget, it may create a [race-condition](https://en.wikipedia.org/wiki/Race_condition#In_software).\n\n### Starting / Ending Runtime\n\n[`run!`](@ref) starts the application. This function initializes the various back-ends needed to show widgets, after which it emits signal `activate`. This is why we cannot initialize a widget before `run!` is called, or we will get an error:\n\n```\njulia> label = Label()\n\n(process:15357): Mousetrap-ERROR **: 21:22:29.003: \n    Attempting to construct a widget, but the GTK4 backend has not yet been initialized.\n    (...)\n    You have most likely attempted to construct a widget outside of `main` while using Mousetrap interactively.\n```\n\nWe can force initialization without an `Application` instance with `Mousetrap.detail.initialize()`, though this is usually not recommended.\n\nAt any point, we can attempt to end runtime by calling [`quit!`](@ref). This will usually cause the application to emit its signal `shutdown`. We should always `quit!` before calling Julias `exit()`, otherwise the app's process will be killed immediately, which can lead to undefined behavior.\n\n### Holding & Busy\n\nEach app on a user's system has two boolean flags: whether it is currently **holding** and whether it is currently **busy**.\n\nHolding means that the application will attempt to prevent exiting in any way. We set this flag by calling [`hold!`](@ref), which should be used to prevent the user from accidentally ending runtime while an important process is running. \n\nWe have to make sure to call [`release!`](@ref) to undo a previous `hold!`, after which the app can exit normally again.\n\nBeing **busy** marks the app so that the OS recognizes that it is currently busy. This will prevent the \"`app` is not responding\" dialog many OS will trigger automatically when an app freezes. Sometimes, freezing is unavoidable because a costly operation is taking place. During times like this, we should call [`mark_as_busy!`](@ref), which notifies the OS that everything is still working as intended, it will just take a while. Once the expensive task is complete, [`unmark_as_busy!`](@ref) reverts the flag.\n\n## Logging\n\n### Introduction\n\nWhen shipping applications, stability is paramount. Nobody will use an app if it keeps crashing, especially if that crash may corrupt important files.\n\nThe best way to prevent crashes is to follow [proper testing procedures](https://www.globalapptesting.com/blog/software-testing). For a small team, it is inevitable that some things will slip through the cracks. When an end-user comes to us with a problem or bug, they most likely will not be able to precisely describe the state of the application, and, depending on the user base, they may not be able to describe the problem at all.\n\nThis is where objective information about what exactly was happening right before the crash is invaluable. **Logging** is the act of creating this information. Information about the current and past states of the application is stored in a file. This way, when a crash or bug occurs, we can simply ask the user to provide us with the log file to analyze ourselves.\n\nWhen working through past chapters, we may have already encountered some logging information. For example, if we try to do the following:\n\n```cpp\nbox = Box()\npush_back!(box, box)\n```\n\nWe get the following message, printed to our console:\n\n```\n(example_target:45245): Mousetrap-CRITICAL **: 16:44:27.065: In Box::push_back!: Attempting to insert widget into itself. This would cause an infinite loop\n```\n\nWe cannot insert a widget into itself, Mousetrap prevented this action and printed a log message to inform us of this instead. This protects the applications' stability from potential developer errors. Any and all functions should follow this philosophy: **prevent the error or bug, print a log message instead**. \n\n### Log Message Properties\n\nLet's go through each part of the above message, one-by-one:\n\n#### Application ID\n\nFirst, we have `(example_target:45245)`, which is the identification of our application. During normal runtime, this information may not be very useful. Once the log is stored in a system-level log file, however, many applications may log at the same time to the same file. Knowing which log message came from which application is integral in this context.\n\n#### Log Domain\n\nNext, we have `Mousetrap-CRITICAL`. The word before the `-` is the **log domain**. This is a developer-defined identification that should state which part of the application or library caused the logging message. Pre-defined domains include `Mousetrap` and `Mousetrap.jl` for Mousetrap-specific warnings, `GTK` for GTK-based warnings, `GLib`, `Gio`, etc. \n\nAs a user of Mousetrap, we should choose a new log domain. For example, if we create a new application called \"Foo Image Manipulation Program\", we should choose a descriptive log domain, such as `foo_image_manipulation_program`, `FIMP`, or `foo`.\n\n#### Log Levels\n\n`CRITICAL` is the messages **log level**. Mousetrap offers the following log levels:\n\n+ `DEBUG` is for messages that should not appear when the end user operates the application, they are **only meant for developers**. These messages will not be stored or printed to the console unless we specifically request the logging suite to do so\n+ `INFO` is for **benign status updates**, for example, `successfully opened file at (...)`. We should not overuse these, as they can clutter up log files.\n+ `WARNING` is for messages that should attempt to **prevent undesirable but not critical behavior before it occurs**. For example, when attempting to close a file while it is still being written to, a warning should be printed and the closing should be postponed until the writing is done.\n+ `CRITICAL` is for errors. In many languages, an error means the end of runtime, which is unacceptable for GUI applications. If the application throws a Julia exception, that exception [should be caught](https://docs.julialang.org/en/v1/manual/control-flow/#Exception-Handling) and printed as a `CRITICAL` log message instead. \n+ `FATAL` is the most severe log level and should only be used as an absolute last resort. Once a `FATAL` warning is printed, the application exits immediately. These should be reserved for issues that make it impossible to run an application, for example, `no graphics card detected. quitting...`\n\nWe see that our message from before was designated as `CRITICAL`. This is because adding a widget to itself would effectively deadlock the application, ending runtime. This makes it an issue too severe for a `WARNING`, but it is still recoverable (by preventing the insertion), therefore `FATAL` would be inappropriate. \n\n`WARNING`s may be triggered by users. If a user can trigger a `CRITICAL` log message, this inherently means we as developers failed to prevent the user from doing so. An issue like this should be addressed by redesigning the application, as opposed to educating users. For a large enough user base, the latter will inevitably fail and cause the error to happen anyway.\n\n#### Time Stamp\n\nAfter the log level, we have `16:44:27.065`, this is the **time stamp** of the log message, with millisecond precision. When storing the message to a file, the current date and year are also appended to the time stamp.\n\n#### Message\n\nLastly, we have the **log message**. Log messages should contain the name of the function they are called from; for example, in the above message, it says `In Box::push_back!`, telling developers that the error happened in that function. This makes debugging easier.\n\nMessages should not end with a `\\n` (a newline), as one is automatically appended to the end of the message.\n\n### Printing Messages\n\nAll interaction with the log is handled by only a few functions. To print a log message of a given log level, we use `log_debug`, `log_info`, `log_warning`, `log_critical`, and `log_fatal`. These functions take as their first argument the log domain and as their second argument the message as a string.\n\nAs mentioned before, messages of level `DEBUG` are only printed if we specifically request them to do so. We enable these on a per-log-domain basis, [`set_surpress_debug!`](@ref), while we can choose to suppress message with log level `INFO` using [`set_surpress_info!`](@ref). \n\nFor example, if our log domain is `foo`:\n\n```julia\n# define custom domain\nconst FOO_DOMAIN = \"foo\"\n\n# print `DEBUG` level message but nothing will happen because it is suppressed by default\nlog_debug(FOO_DOMAIN, \"Surpressed message\")\n\n# enable `INFO` level messages\nset_surpress_debug!(FOO_DOMAIN, false)\n\n# message will be printed\nlog_debug(FOO_DOMAIN, \"No longer suppressed message\")\n```\n\nNote that logging will only work once our `Application` instance is initialized. This is because the logging system needs a valid application ID, which is only registered once `Application` emits its `activate` signal.\n\n### Logging to a File\n\nIf the operating system is Linux, many log messages will be written to the default location, usually `/var/log`. On other operating systems, messages may not be stored at all.\n\nRegardless of OS, we can forward all logging, including that of Mousetrap itself, to a file using [`set_log_file!`](@ref), which takes the file path as a string. If the file already exists, it will be appended to (as opposed to being overwritten). If the file does not yet exist, it will be created. On successfully opening the file, `true` will be returned. We should react to the function's result, as not being able to log should be considered a fatal error.\n\nWhen stored to a file, logging messages will have a different format that may or may not list additional information when compared to logging to a console. The philosophy behind this is that it is better to log as much information as possible, and then use second-party software to filter it, as opposed to missing crucial information for the sake of brevity:\n\n```cpp\nconst LogDomain FOO_DOMAIN = \"foo\"\nif !set_log_file(FOO_DOMAIN, \"example_log.txt\")\n    log_fatal(FOO_DOMAIN, \"In set_log_file: Unable to create file at `example_log.txt`\")\nend\n\nlog_warning(FOO_DOMAIN, \"Example Message\")\n```\n\nWill add the following lines to a `example_log.txt`\n\n```\n[23-05-06 23:01:34,920]: In example.main: Example Message\n    GLIB_DOMAIN foo\n    MOUSETRAP_LEVEL WARNING\n    PRIORITY 4\n```\n\nMousetraps logging system should be preferred over the native Julia one. Sending a message with Julias `@info`, will not be printed to the Mousetrap log file and will not be accessible by a Mousetrap application.\n\n---\n\n## File System\n\nMost GUI applications on desktops are centralized around modifying files. A text or image editor will often want to export files, while a video game will want to create or load a save file. Conversely, Mousetrap offers a robust, operating-system-agnostic way of interacting with the user's file system.\n\nThere are two kinds of objects in a file system: **files**, which contain arbitrary data, and **directories**, which contain other files and/or other directories. We also call a directory a **folder**. \n\nExamples of files that are not folders include `.png`, `.txt`, `.jl` text files, shared libraries, binaries, or executable files.\n\nA **path** is a string, made up of folder names separated by `/`, (or `\\` on windows, though this should be avoided). Examples include `/var/log`, `~/Desktop`, etc. A path starting at root (`/` on Unix, usually `C:/` on Windows) is called an **absolute path**, while any other path is called a **relative path**. \n\nAn **URI** (universal resource identifier) is another way to express the location of the file. It follows a [strict scheme](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier), which is followed by most internet browsers, and should be preferred to regular paths for file transfers between different machines, or when referring to files on the internet.\n\n### FileDescriptor\n\nWhen querying information about a file, we use [`FileDescriptor`](@ref), which represents information about a file or folder. This object is **non-mutating**, meaning it is incapable of changing anything about the actual file on the disk. In other words, **`FileDescriptor` is read-only**.\n\nWe can create a file descriptor from a path like so:\n\n```cpp\nreadonly = FileDescriptor()\ncreate_from_path!(readonly, \"/home/user/Desktop/example.txt\");\n```\n\nWhere the argument to [`create_from_path!`](@ref) will be automatically detected as either a relative or absolute path. If it is not an absolute path, it will be prefixed with the application's runtime directory. For example, if we create a `FileDescriptor` from path `\"assets/image.png\"`, and our application is located in `/usr/bin/foo`, then the path will be treated as `/usr/bin/foo/assets/image.png`.\n\n`FileDescriptor` does not make sure the underlying file or folder exists or that it is a valid file. Creating a descriptor from an invalid path or a path that does not point to a file or folder works just fine, and we won't get a warning. To check whether a file descriptor points to a valid file or folder, we have to use [`exists`](@ref). \n\nTo check whether a `FileDescriptor` points to a file (as opposed to a directory), we use [`is_file`](@ref) or [`is_folder`](@ref), respectively. If the file pointed to by `FileDescriptor` does not exist, both of these functions will return `false`.\n\n`FileDescriptor` allows us to query a variety of information about the file or folder, including, but not limited to:\n\n+ [`get_path`](@ref) returns the location of the file as a path, eg. `~/Desktop/example.txt`\n+ [`get_uri`](@ref) returns the location as an URI, eg. `file://~/Desktop/example.txt`\n+ [`get_file_extension`](@ref) returns the file extension, eg. `txt`\n+ [`is_executable`](@ref) checks whether the file is executable\n+ [`get_content_type`](@ref) returns the [MIME type](https://en.wikipedia.org/wiki/Media_type), eg. `text/plain`\n\nFor less common metadata information, we can use [`query_info`](@ref), which takes an **attribute identifier** as a string. A list of identifiers can be found [here](https://gitlab.gnome.org/GNOME/glib/-/blob/main/gio/gfileinfo.h#L46), though, depending on the type of file and operating system, not all of these attributes may have a corresponding value.\n\nIf the file is a folder, we can use [`get_children`](@ref) to get all files and/or directories inside that folder. `get_children` takes a boolean as its other argument, which specifies whether it should list all children recursively.\n\n---\n\n## Manipulating the Disk\n\n`FileDescriptor` being non-mutating means we need a different part of Mousetrap in order to modify files on the users' disk. For file input/output, such as reading the contents of files, we should use the [Julia standard library](https://docs.julialang.org/en/v1/base/file/), which is well-suited for this task. \n\nFor manipulating files as a whole, as opposed to their contents, Mousetrap offers multiple functions for common tasks:\n\n### Creating Files\n\n[`create_file_at!`](@ref) creates a file at a given location. It takes a file descriptor as its only argument. If `should_replace` is set to `false` and the file already exists, no operation will be performed\n\n```julia\nif create_file_at!(FileDescriptor(\"/absolute/path/to/file.txt\"), replace = false)\n    # use file\nend\n```\n\n[`create_directory_at!`](@ref) performs a similar action, except it creates a directory instead of a file.\n\n### Deleting Files\n\nTo permanently delete a file, we use [`delete_at!`](@ref), which takes a file descriptor as its argument. This immediately deletes the file, making it unable to be recovered. In some cases, this may be too risky, in which case should use [`move_to_trash!`](@ref) instead:\n\n```julia\nto_delete = FileDescriptor(\"/path/to/delete/file.txt\")\nif !move_to_trash(to_delete)\n    log_warning(FOO_DOMAIN, \"In example: Unable to delete file at `$(get_path(to_delete))`\")\nend\n```\n\n### Moving / Copying File\n\nTo move a file from one location to another, we use [`move!`](@ref). If we want to copy a file or directory instead of moving it, we use [`Mousetrap.copy!`](@ref):\n\n```julia\nfrom = FileDescriptor(\"/path/from/file.txt\")\nto = FileDescriptor(\"/different_path/to/file.txt\")\nif !move!(from, to)\n    log_warning(FOO_DOMAIN, \"In example: Unable to move file from `$(get_path(from))` to `$(get_path(to))`\")\nend\n```\n\n### Changing File Metadata\n\n!!! info \n    (this feature is not yet implemented)\n\n### Opening a File or URL\n\nOften, we will want to open an external file for the user, for example, showing the license of our app in a text editor or opening a donation page from a menu. Mousetrap offers three functions well-suited for this.\n\n[`open_file`](@ref) will open a file on disk, usually presenting a user with a number of applications that can open the file. For example, when opening a `.txt` file, the user will be presented with a list of text editors installed on their system. When they select one, that application will be started and open the file.\n\nSimilarly, [`show_in_file_explorer`](@ref) will open the user's file explorer to the enclosing folder of the file. \n\nLastly, [`open_url`](@ref) takes a URL as a string, opening the user's default internet browser to show that page.\n\nAll of these functions are designed to work on all operating systems, making them a convenient way to perform what would be quite a complex task otherwise.\n\n---\n\n### Monitoring File Changes\n\nOften, when writing a GUI, we want the graphical interface to reflect the contents of a file on the disk. A good example would be a text editor. We can modify the file from inside our application, however, if the file is modified by a third entity, such as another application, a conflict may arise. In this case, we will usually want to update the state of our application such that it reflects the state of the file on disk. This should happen whenever the underlying file changes. \n\nThis is made possible by [`FileMonitor`](@ref), which monitors a file or directory for changes.\n\n`FileMonitor` cannot be created directly, instead, we first create a `FileDescriptor`, then call [`create_monitor`](@ref), which returns the `FileMonitor` instance.\n\n`FileMonitor` works similarly to a signal emitter. To register a function that is called whenever the file changes, we use [`on_file_changed!`](@ref), which expects a function with the signature \n\n```julia\n(::FileMonitor, event::FileMonitorEvent, self::FileDescriptor, other::FileDescriptor, [::Data_t]) -> Nothing\n```\n\nwhere\n\n+ `event` is a [`FileMonitorEvent`](@ref), describing the type of action performed, see below\n+ `self` is a descriptor pointing to the file or folder that is being monitored\n+ `other` is a descriptor that may or may not point to the other relevant file, see below\n+ `Data_t` is optional arbitrary data\n\nThe following monitor events are supported:\n\n| `FileMonitorEvent`                     | Meaning                      | value of `self`          | value of `other`   |\n|----------------------------------------|------------------------------|--------------------------|--------------------|\n| `FILE_MONITOR_EVENT_CHANGED`           | File's content was modified in any way | modified file            | none               |\n| `FILE_MONITOR_EVENT_DELETED`           | File was deleted             | monitored file or folder | deleted file       |\n| `FILE_MONITOR_EVENT_CREATED`           | File was created             | monitored file or folder | newly created file |\n| `FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED` | File metadata changed        | changed file             | none               |\n| `FILE_MONITOR_EVENT_RENAMED`           | File's name changed           | changed file             | none               |\n| `FILE_MONITOR_EVENT_MOVED_IN`          | File was moved into self     | monitored folder         | moved file         |\n| `FILE_MONITOR_EVENT_MOVED_OUT`         | File was moved out of self   | monitored folder         | moved file         | \n\nFor example, if we want to trigger an action whenever `/path/to/file.txt` changes its content, we could do the following:\n\n```julia\nto_watch = FileDescriptor(\"/path/to/file.txt\")  # equivalent to create_from_path!\nmonitor = create_monitor(to_watch)\n\nfunction on_file_changed_callback(monitor::FileMonitor, event::FileMonitorEvent, self::FileDescriptor, other::FileDescriptor)\n    if event == FILE_MONITOR_EVENT_CHANGED\n        println(\"File at $(get_path(self)) changed.\")\n    end\nend\non_file_changed!(on_file_changed_callback, monitor)\n```\n\nIf we no longer want to monitor a file, we can call [`cancel!`](@ref), at which point the `FileMonitor` instance may be safely deallocated.\n\n---\n\n## File Chooser Dialog\n\nOpening a dialog to allow a user to select a file or folder is a task so common, that most operating systems provide a native widget just for this purpose. Mousetrap, conversely, also has an object tailor-made for this: [`FileChooser`](@ref)\n\n`FileChooser` is not a widget, and it cannot emit any signals. It is what's called a **dialog**, which is a graphical object that can only exist in its own window.\n\nIts constructor takes two arguments: a [`FileChooserAction`](@ref) and the resulting dialog window's title. `FileChooserAction` is an enum whose value determines which **mode** the `FileChooser` will perform in:\n\n| `FileChooserAction` value | Users may select...         |\n|---------------------------|-----------------------------|\n| `FILE_CHOOSER_ACTION_OPEN_FILE` | exactly one file            |\n| `FILE_CHOOSER_ACTION_OPEN_MULTIPLE_FILES` | one or more files           |\n| `FILE_CHOOSER_ACTION_SELECT_FOLDER` | zero or one folder          |\n| `FILE_CHOOSER_ACTION_SELECT_MULTIPLE_FOLDERS` | zero or more folders        |\n| `FILE_CHOOSER_ACTION_SAVE` | file name and location |\n\nDepending on which `FileChooserAction` we choose, `FileChooser` will automatically change its layout and behavior. After creating the object, we can show it to the user using `present!`:\n\n```cpp\nfile_chooser = FileChooser(FILE_CHOOSER_ACTION_OPEN_MULTIPLE_FILES)\npresent!(file_chooser)\n```\n\n![](../assets/file_chooser.png)\n\nTo react to the user making a selection or canceling the operation, we need to register a **callback** with the file chooser.\n\n[`on_accept!`](@ref) takes a function that is invoked when the user makes a file selection, for example by pressing the \"Open\" button. This function is required to have the signature\n\n```julia\n(::FileChooser, files::Vector{FileDescriptor}, [::Data_t]) -> Nothing\n```\nWhere `files` may contain zero or more file descriptors, depending on the `FileChooserAction` used when creating the dialog.\n\nThe callback registered using [`on_cancel!`](@ref) is called when the user cancels or otherwise closes the dialog. This function requires a different signature:\n\n```julia\n(::FileChooser, [::Data_t]) -> Nothing\n```\n\nUsing these, we can trigger custom behavior if / when the user makes a selection:\n\n```julia\nfile_chooser = FileChooser(FILE_CHOOSER_ACTION_OPEN_MULTIPLE_FILES)\n\non_accept!(file_chooser) do self::FileChooser, files::Vector{FileDescriptor}\n    println(\"User chose files at $files\")\nend\non_cancel!(file_chooser) do self::FileChooser\n    println(\"User cancelled the dialog\")\nend\n\npresent!(file_chooser)\n```\n### FileFilter\n\nLooking again at the previous screenshot in this section, we see that in the bottom right corner of the dialog, a drop-down with the currently selected item `(None)` is seen. This is the currently active **filter**, where `(None)` means no filter is active.\n\nBy adding a filter, we make it so only files that pass that filter will be shown - and thus be selectable. This is useful when we want to limit file selection to only a certain type of file; for example, an image manipulation application would only allow loadable image files as the file type for an `Open...` dialog.\n\nWe construct a [`FileFilter`](@ref) by first choosing a name. This string will be used as the title of the filter, which is shown in the `FileChooser`s drop-down:\n\n```julia\nfile_filter = FileFilter(\"Julia Files\")\n```\n\nWe now have to specify which files should pass the filter. `FileFilter` offers multiple functions for this:\n\n| `FileFilter` Method                             | Argument       | Resulting Allowed Files                                   |\n|-------------------------------------------------|----------------|-----------------------------------------------------------|\n| [`add_allowed_suffix!`](@ref)                   | `jl`           | files ending in `.jl`                                     |\n| [`add_allow_all_supported_image_formats!`](@ref) | (no argument)  | file types that can be loaded by [`ImageDisplay`](@ref)s  |\n| [`add_allowed_mime_type!`](@ref)                 | `text/plain`   | files classified as plain text, for example `.txt`        |\n| [`add_allowed_pattern!`](@ref)                    | `*.jl`         | files whose name match the given regular expression  |\n\nA table with the allowed image formats is available in [the chapter on images](06_image.md#supported-image-formats).\n\nAfter having set up our filter, we simply add it to the `FileChooser` instance using [`add_filter!`](@ref):\n\n```julia\nfilter = FileFilter(\"*.jl\")\nadd_allowed_suffix!(filter, \"jl\")\nadd_filter!(file_chooser, filter)\n```\n\n![](../assets/file_chooser_filter.png)\n\nBy default, no `FileFilter`s will be registered, which means the `FileChooser` will display all possible file types. We can control which filter is active when the dialog opens using [`set_initial_filter!`](@ref).\n\n!!! info\n    We can call `set_initial_filter!` with a filter that has not yet been added with `add_filter!`. This will make it so the initial filter is active when the dialog opens, but cannot be changed, as only filters added with `add_filter!` will be selectable from the dialogs dropdown.\n\n---\n\n## Alert Dialog\n\nA very common task for an application that manipulates files is to make sure the user knows they are overwriting a file, for example, when the user selects a location using a `FileChooser` whose action is `FILE_CHOOSER_ACTION_SAVE_FILE`.\n\nWhile we could construct a custom widget for this purpose, put that widget in a `Window`, and then present that window to the user, a task as common as this should be possible in only a few lines. For this purpose, Mousetrap offers [`AlertDialog`](@ref), which is a dialog that shows a message to the user, along with one or more buttons they can click.\n\nEach `AlertDialog` has a **message**, a **detailed description**, which we during `AlertDialogs`constructor:\n\n```julia\noverwrite_file_warning_dialog = AlertDialog(\n    \"A file with this name already exists, continue?\", # message\n    \"The original file will be overwritten, this cannot be undone.\" # detail description\n)\n```\n\nWith just this, the only way for the user to interact with the dialog is to press escape, which closes it. We will most likely want to add buttons, which we accomplish using [`add_button!`](@ref)\n\n```julia\nadd_button!(overwrite_file_warning_dialog, \"Continue\")\nadd_button!(overwrite_file_warning_dialog, \"Cancel\")\n```\n\nWhile we could `present!` this dialog to the user now:\n\n![](../assets/alert_dialog.png)\n\nWe haven't yet connected any behavior to the user pressing a button. To do this, we use [`on_selection!`](@ref), which takes a callback with the following signature:\n\n```julia\n(::AlertDialog, button_index::Integer, [::Data_t]) -> Nothing\n```\n\nWhere `button_index` is the index of the button, from left to right (1-based), or `0` if the dialog was dismissed without pressing a button.\n\nContinuing our example of warning the user when they're about to overwrite a file, we would do the following:\n\n```julia\n# create the dialog\noverwrite_file_warning_dialog = AlertDialog(\n    \"A file with this name already exists, continue?\", # message\n    \"The original file will be overridden, this cannot be undone.\" # detailed description\n)\n\nadd_button!(overwrite_file_warning_dialog, \"Continue\")\nadd_button!(overwrite_file_warning_dialog, \"Cancel\")\n\n# add a callback\non_selection!(overwrite_file_warning_dialog) do self::AlertDialog, button_index::Integer\n    if button_index == 1 # \"Continue\" pressed\n        # write file\n    elseif button_index == 2  # \"Cancel\" pressed\n        # ...\n    else # dialog closed without pressing a button\n        # ...\n    end\nend\n\n# show the dialog, because it is modal, this will pause all other windows\npresent!(overwrite_file_warning_dialog)\n```\n\nNote that we do not need to close the dialog from within the `on_selection!` callback, it is closed automatically.\n\nOn top of buttons, `AlertDialog` furthermore has a spot for a custom widget, which will be displayed underneath the detail message. We choose this widget with [`set_extra_widget!`](@ref), which gives us some additional flexibility when we want a dialog that has more than just text.\n\nWith `AlertDialog`, we have a vastly simplified mechanism for showing short, message-style dialogs to the user. Each of these dialogs will be *modal*  by default, meaning all other windows and actions will be paused until the dialog is dismissed.\n\n## Popup Messages\n\nMousetrap offers a less intrusive way of showing a message to a user, facilitated by [`PopupMessageOverlay`](@ref). This widget is a container that has a single child. It adds no graphical element to its child, instead, we can show a small popup message which will be shown above the chosen child:\n\n![](../assets/popup_message.png)\n\n!!! details \"How to generate this image\"\n    ```julia\n    using Mousetrap\n    main() do app::Application\n        window = Window(app)\n\n        child = Separator()\n        message_overlay = PopupMessageOverlay()\n        set_child!(message_overlay, child)\n\n        show_message_action = Action(\"example.show_message\", app)\n        set_function!(show_message_action, message_overlay) do self::Action, message_overlay::PopupMessageOverlay\n            message = PopupMessage(\"This is a message\")\n            set_button_label!(message, \"OK\")\n            set_button_action!(message, self)\n            show_message!(message_overlay, message)\n        end\n\n        button = Button()\n        set_opacity!(button, 0)\n        set_action!(button, show_message_action)\n        push_front!(get_header_bar(window), button)\n    \n        set_child!(window, message_overlay)\n        present!(window)\n    end\n    ```\n\nTo send a message, we first need to instance an object of type [`PopupMessage`](@ref), which, unlike `PopupMessageOverlay`, is **not** a widget, though it is a `SignalEmitter`.\n\nA popup message always has a title, which we supply to its constructor. It will furthermore always have a close button the user can use to hide the message. \n\nAfter instancing the `PopupMessage`, we present it to the user using [`show_message!`](@ref):\n\n```julia\nmessage_overlay = PopupMessageOverlay()\nset_child!(message_overlay, widget)\n\npopup_message = PopupMessage(\"Message Text\")\nshow_message!(message_overlay, popup_message)\n```\nWe can set the message to hide itself automatically by setting [`set_timeout!`](@ref) to anything other than `0`. Only one message can be shown at a time. We can make sure an important message is jumped to the front of the queue by setting its priority using [`\nset_is_high_priority!`](@ref).\n\nThis is enough for a simple notification, but `PopupMessage` also supports interactivity beyond just the close button.\n\nA `PopupMessage` has one optional button. To show this button, we need to choose a title using [`set_button_label!`](@ref). To react to the user pressing this button, we can either assign it an action using [`set_button_action!`](@ref), or we can connect to `PopupMessages` signals, `button_clicked` and `dismissed`, which are emitted when the optional button and close button are pressed, respectively. Both signals expect a signal handler with signature `(::PopupMessage, [::Data_t]) -> Nothing`.\n\nFor example, to trigger a function when the user clicks the \"OK\" button of our `PopupMessage`, we could do:\n\n```julia\npopup_message = PopupMessage(\"Message Text\")\n\n# connect to button press\nset_button_label!(popup_message, \"OK\")\nconnect_signal_button_clicked!(popup_message) do self::PopupMessage\n    println(\"OK clicked\")\nend\n\nshow_message!(message_overlay, popup_message)\n```\n\n## Popup Messages\n\nMousetrap offers a less intrusive way of showing a message to a user, facilitated by [`PopupMessageOverlay`](@ref). This widget is a container that has a single child. It adds no graphical element to its child, instead, we can present a small popup message to the user, which will be shown above the chosen child:\n\n![](../assets/popup_message.png)\n\n!!! details \"How to generate this image\"\n    ```julia\n    using Mousetrap\n    main() do app::Application\n        window = Window(app)\n\n        child = Separator()\n        message_overlay = PopupMessageOverlay()\n        set_child!(message_overlay, child)\n\n        show_message_action = Action(\"example.show_message\", app)\n        set_function!(show_message_action, message_overlay) do self::Action, message_overlay::PopupMessageOverlay\n            message = PopupMessage(\"This is a message\")\n            set_button_label!(message, \"OK\")\n            set_button_action!(message, self)\n            show_message!(message_overlay, message)\n        end\n\n        button = Button()\n        set_opacity!(button, 0)\n        set_action!(button, show_message_action)\n        push_front!(get_header_bar(window), button)\n    \n        set_child!(window, message_overlay)\n        present!(window)\n    end\n    ```\n\nTo send a message, we first need to instance an object of type [`PopupMessage`](@ref), which, unlike `PopupMessageOverlay`, is **not** a widget, though it is a `SignalEmitter`.\n\nA popup message always has a title, which is supplied to its constructor. It will furthermore always have a close button the user can use to hide the message. \n\nAfter instancing the `PopupMessage`, we push it to the overlay using [`show_message!`](@ref):\n\n```julia\nmessage_overlay = PopupMessageOverlay()\nset_child!(message_overlay, widget)\n\npopup_message = PopupMessage(\"Message Text\")\nshow_message!(message_overlay, popup_message)\n```\nWe can set the message to hide itself automatically by setting [`set_timeout!`](@ref) to anything other than `0`. \n\nOnly one message can be shown at a time. We can make sure an important message is put to the front of the queue by setting marking it as high priority using [`set_is_high_priority!`](@ref).\n\nThis is enough for a simple notification, but `PopupMessage` also supports interactivity beyond just the close button.\n\nA `PopupMessage` can have one optional button. To show this button, we need to choose the button's label using [`set_button_label!`](@ref). \n\nTo react to the user pressing this button, we can either assign it an action using [`set_button_action!`](@ref), or we can connect to one of `PopupMessages` signals, `button_clicked` and `dismissed`, which are emitted when the optional button and close button are pressed, respectively. Both signals expect a signal handler with signature `(::PopupMessage, [::Data_t]) -> Nothing`.\n\nFor example, to trigger a function when the user clicks the \"OK\" button of a `PopupMessage`, we could do:\n\n```julia\npopup_message = PopupMessage(\"Message Text\")\n\n# connect to button press\nset_button_label!(popup_message, \"OK\")\nconnect_signal_button_clicked!(popup_message) do self::PopupMessage\n    println(\"OK clicked\")\nend\n\nshow_message!(message_overlay, popup_message)\n```\n\n`PopupMessage` should be used for relatively insignificant messages that do not require immediate user action. Otherwise, we should `AlertDialog`, which is modal by default and will only go away once the user chooses to dismiss the dialog.\n\n---\n\n## GLib Keyfiles\n\nFor many objects like images, Mousetrap [offers ways to store them on the disk](@ref save_to_file). For custom objects, such as the state of our application, we have no such option. While it may sometimes be necessary, for most purposes we do not need to create a custom file type, instead, we can use the [**GLib KeyFile**](https://docs.gtk.org/glib/struct.KeyFile.html), whose syntax is heavily inspired by Windows `.ini` settings files.\n\nKeyfiles are human-readable and easy to edit, which makes them better suited for certain purposes when compared to [json](https://docs.fileformat.com/web/json/) or [xml](https://docs.fileformat.com/web/xml/) files.\n\nThanks to [`Mousetrap.KeyFile`](@ref), loading, saving, and modifying key files is easy and convenient.\n\n### GKib Keyfile Syntax\n\nIn a keyfile, every line is one of four types:\n\n+ **Empty**, it has no characters or only control characters and spaces\n+ **Comment**, it begins with `#`\n+ **Group**, has the form `[group_name]`, where `group_name` is any name not containing a space\n+ **Key**, has the form `key=value`, where `key` is any name and `value` is of a format discussed below\n\nFor example, the following is a valid key file:\n\n```txt\n# keybindings \n[image_view.key_bindings]\n\n# store current file\nsave_file=<Control>s\n\n# miscellanous config\n[image_view.window]\n\n# default window size\nwidth=400\nheight=300\n\n# default background color\ndefault_color_rgba=0.1;0.7;0.2;1\n```\n\nKey-value pairs belong to the group that was last opened. Groups cannot be nested, they always have a depth of 1 and every key-value pair has to be inside exactly one group.\n\n### Accessing Values\n\nIf the above key file is stored at `assets/example_key_file.txt`, we can access the values of the above-named keys like so:\n\n```julia\n# load file\nfile = KeyFile()\nload_from_file!(file, \"assets/example_key_file.txt\")\n\n# retrieve value as String\nsave_file_keybinding = get_value(file, \"image_view.key_binding\", \"save_file\", String)\n\n# retrieve values as int\nwidth = get_value(file, \"image_view.window\", \"width\", Int32)\nheight = get_value(file, \"image_view.window\", \"height\", Int32)\n\n# retrieve value as RGBA\ndefault_color = get_value(file, \"image_view.window\", \"default_color_rgba\", RGBA)\n```\n\nWe see that the general syntax to access a `KeyFile` value is \n```\nget_value(<file>, <group_name>, <key_name>, <type>)\n``` \nwhere `type` is one of the following:\n\n| Type                      | Example Value                                | Format                            |\n|---------------------------|----------------------------------------------|-----------------------------------|\n| Bool                      | `true`                                       | `true`                            |\n| Vector{Bool}              | `[true, false, true]`                        | `true;false;true`                 |\n| Cint (Int32)              | `32`                                         | `32`                              |\n| Vector{Cint}              | `[12, 34, 56]`                               | `12;34;56`                        |\n| Csize_t (UInt64)          | `984`                                        | `948`                             |\n| Vector{Csize_t}           | `[124, 123, 192]`                            | `124;123;192`                     |\n| Cfloat (Float32)          | `3.14`                                       | `3.14`                            |\n| Vector{Cfloat}            | `[1.2, 3.4, 5.6]`                            | `1.2;3.4;5.6`                     |\n| Cdouble (Float64)         | `3.14569`                                    | `3.14569`                         |\n| Vector{Cdouble}           | `[1.123, 0.151, 3.121]`                      | `1.123;0.151;3.121`               |\n| String                    | `\"foo\"`                                      | `foo`                             |\n| Vector{String}            | `[\"foo\", \"lib\", \"bar\"]`                      | `foo;lib;bar`                     |\n| RGBA                      | `RGBA(0.1, 0.9, 0, 1)`                       | `0.1;0.9;0.0;1.0`                 |\n| Image                     | `RGBA(1, 0, 1, 1), RGBA(0.5, 0.5, 0.7, 0.0)` | `1.0;0.0;1.0;1.0;0.5;0.5;0.7;0.0` | \n\nWe can also interact with comments from Julia. To access the comment above a group ID or key-value pair, we use [`get_comment_above`](@ref) which takes either just a group name for comments above groups or a group- and key-name for comments above key-value pairs.\n\n### Storing Values\n\nWe use [`set_value!`](@ref) to modify the value of a `KeyFile` entry. Thanks to method dispatch, we do not have to specify the type of value in Julia for `set_value!`.\n\n```julia\nset_value!(file, \"image_view.window\", \"default_color_rgba\", RGBA(1, 0, 1, 1));\n```\n\nTo modify comments, we use [`set_comment_above!`](@ref), which, just like before, takes only a group ID to modify the comment above a group declaration, or both a group ID and key to modify the comment above a key-value pair.\n\nWhen writing to an instance of `KeyFile`, only the file in memory is modified, **not the file on the disk**. To update the actual stored file, we need to call [`save_to_file`](@ref). \n\n---\n\n## Icons\n\nWe've seen before how to load and display an image using `Image`. One of the most common applications for this is to use the resulting picture as the label of a `Button`. In common language, this picture is often called an **icon**.\n\nIn modern desktop applications, we may have dozens of these images used as visual labels for widgets. While it is possible to simply store all these images as `.png`s and load them manually with `Image`, this is hardly very scalable. Furthermore, this method does not allow us to modify all pictures at the same time, which may be necessary to, for example, increase icon size to aid visually impaired users.\n\nA better way to handle images in a context like this is provided by [`Icon`](@ref).\n\n`Icon` is similar to an image file, though it usually does not contain pixel data. Instead, it points to a file on disk. `Icon`s can be loaded from `.png` files, but also allows `.svg` (vector graphics), and `.ico` (web browser icons). \n\n### Creating and Viewing Icons\n\nThe simplest way to create an `Icon` is to load it as if it were an `Image`. If we have a vector graphics file at `assets/save_icon.svg`, we would load it like this:\n\n```cpp\nicon = Icon()\ncreate_from_file!(icon, \"assets/save_icon.svg\", 48);\n```\n\nWhere `48` means the icon will be initialized with a resolution of 48x48 pixels. `Icon`s can only be square.\n\nIf we want to display this icon using a widget, we can use `ImageDisplay`s [`create_from_icon!`](@ref). This is similar to images, however, because `Icon` will usually not be raster-based, the `ImageDisplay` will scale much more smoothly to any resolution as no interpolation has to take place.\n\nOther than with `ImageDisplay`, we have other ways to use icons in graphical widgets: We can directly make the icon a child of a `Button` or `ToggleButton` using [`set_icon!`](@ref). Icons can furthermore be inserted to the left or right of an entry using [`set_primary_icon!`](@ref), and [`set_secondary_icon!`](@ref), and they can be used in menus, which we will learn about in the next chapter.\n\n## Icon Themes\n\n!!! compat \n    This section is not yet complete, see the documentation for [`IconTheme`](@ref), and the [Freedesktop Icon Theme Specification](https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html) instead.\n"
  },
  {
    "path": "docs/src/01_manual/08_menus.md",
    "content": "# Chapter 8: Menus\n\nIn this chapter, we will learn:\n+ How to create complex, nested menus\n+ How to display menus using `PopoverMenu` and `MenuBar`\n+ Best-practice style guides for menus\n\n---\n\nIn the [chapter on actions](./03_actions.md) we learned that we can trigger an action using [`Button`](@ref), by assigning an action to it using [`set_action!`](@ref).\nThis works if we want to have a GUI element that has one or maybe a few actions. In practice, an application can have hundreds of different actions. Asking users to trigger these using an equal number of buttons would be unsustainable. For situations like these, we should instead turn to **menus**.\n\n## MenuModel Architecture\n\nIn Mousetrap, menus follow the [model-view architectural pattern](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller). To create a menu, we need\nthe menu model, which holds information about how the menu is structured, along with a view, which takes the models and transforms them into an interactable\nwidget users can manipulate. Changing the model changes the view.\n\n!!! details \"Running Snippets from this Section\"\n\n    To follow along with code snippets from this section, we can use the following `main.jl` file:\n    ```julia\n    using Mousetrap \n    main() do app::Application\n\n        window = Window(app)\n        \n        # create dummy action\n        action = Action(\"dummy.action\", app) do x \n            println(\"triggered.\")\n        end\n\n        # model that we will be modifying in the snippet\n        root = MenuModel()\n\n        # snippet goes here\n\n        # display menu\n        add_submenu!(root, \"Title\", model)\n        view = PopoverButton(PopoverMenu(root))\n        set_child!(window, view)\n        present!(window)\n    end\n    ```\n\n## Menu Items\n\n[`MenuModel`](@ref), the model component of Mousetrap menus, is a list of **menu items** in a specific order. If item `A` is added before item `B` at runtime, then `A` will appear above item `B`. There are multiple different types of menu items, all with their own purpose. The function we choose to add an item to the model determines the item's type. There are four types of menu items, which we will go over in this section.\n\n### Item Type #1: Action\n\nThe first and most simple item type is called \"action\". Added via [`add_action!`](@ref), which takes both a label and an [`Action`](@ref) instance, this item is a simple button with text. If the user clicks the button, the action is executed.\n\nSimilar to `Button`, if the action is disabled (via [`set_enabled!`](@ref)) or does not yet have a callback registered, the menu item will appear \"greyed out\" and cannot be activated.\n\n```cpp\nadd_action!(model, \"Action Item #1\", action)\nadd_action!(model, \"Action item #2\", action)\nadd_action!(model, \"Action item #3\", action)\n```\n\n![](../assets/menu_model_actions.png)\n\n### Item Type #2: Widgets\n\nSecondly, we have perhaps the most powerful type of item: A custom widget. We add an item of this type using [`add_widget!`](@ref), which only takes a single argument, the widget itself. This widget can be arbitrarily complex, though it is usually not advisable to insert an entire `ColumnView` into a tiny menu. Good-practice examples include `Button`, `Entry`, `CheckButton`, `ToggleButton`, `Switch`, and `SpinButton`, all of which are intractable.\n\nWe do not supply an `Action` instance with this item, if we want the user interacting with the menu item to trigger behavior, we will have to connect that behavior to the signals of the widget we inserted into the menu, or any of its event controllers.\n\n```cpp\nadd_widget!(model, hbox(Label(\"Choose Value:  \"), SpinButton(0, 1, 0.01)))\nadd_widget!(model, Scale(0, 1, 0.01, ORIENTATION_HORIZONTAL))\nadd_widget!(model, hbox(Label(\"Enter Text:  \"), Entry()))\n```\n\n![](../assets/menu_model_widgets.png)\n\nWidgets are the most flexible type of menu item. They should be used with caution, and only if absolutely necessary. It is often better to create an action that opens a separate [`Window`](@ref) that contains the widget, as opposed to directly adding the widget to the menu.\n\n### Item Type #3: Submenu\n\n`MenuModel` can be **nested**, which means we can insert a `MenuModel` into another `MenuModel`. This is similar to `ListView` or file-system tree: a folder can contain regular files (menu items), or it can contain other folders (menu models), which in turn can also contain another file or folder, etc. `MenuModel` can similarly be infinitely nested, though it is usually not recommended to go deeper than two or three levels.\n\nWe call a `MenuModel` that is nested within another model a **submenu**. It will show up as a button with a label, along with an indicator that it is a submenu, usually a `>` shape. Clicking this item will reveal the submenu.\n\nTo add this type of submenu item, we use [`add_submenu!`](@ref), which takes a title and another menu model:\n\n```julia\nsubmenu_01 = MenuModel()\nadd_widget!(submenu_01, submenu_content())\nadd_submenu!(model, \"Submenu #1\", submenu_01)\n\nsubmenu_02 = MenuModel()\nadd_widget!(submenu_02, submenu_content())\nadd_submenu!(model, \"Submenu #2\", submenu_02)\n```\n![](../assets/menu_model_submenu_outer.png)\n\nClicking on of these items will reveal the submenu content:\n\n![](../assets/menu_model_submenu_inner.png)\n\nWhere `submenu_content()` returns a simple place-holder widget, in reality, this will be many other menu items or other submenus.\n\n### Item Type #4: Icons\n\nA prettier analog to the \"action\"-type menu item, an \"icon\"-type item. Added via [`add_icon!`](@ref), it takes an [`Icon`](@ref), along with an action. The entire menu item will be just the icon, it may not have any text. If the icon is clicked, the action is executed.\n\n```julia\nadd_icon!(model, Icon(#=...=#), action)\nadd_icon!(model, Icon(#=...=#), action)\nadd_icon!(model, Icon(#=...=#), action)\nadd_icon!(model, Icon(#=...=#), action)    \n```\n![](../assets/menu_model_icons.png)\n\nWhere we used the default Gnome icons for weather indicators as placeholders.\n\nWhen creating a menu item with an action, we have to decide whether to use a simple text label or an icon. We may not have both.\n\n### Item Type #5: Sections\n\nLastly, we have **sections**. Sections are like submenus, in that they are a menu inside another menu. The difference is in the way this inner menu is displayed.\n\nWhen adding a submenu with `add_submenu!`, a single new item will appear in the outer menu. When clicked, the menu \"slides\" to reveal the submenu. \n\nWith sections, the inner menu is instead inserted directly into the outer menu; both are shown at the same time. \n\nWe add a \"section\"-type item using [`add_section!`](@ref), which takes another menu model and a title, which will be used as a header for the section:\n\n```julia\nsection = MenuModel()\nadd_action!(section, \"Section Item #01\", #= action =#)\nadd_action!(section, \"Section Item #02\", #= action =#)\nadd_action!(section, \"Section Item #03\", #= action =#)\nadd_section!(model, \"Section Label\", section)\n```\n![](../assets/menu_model_section.png)\n\n\nWe see that the section label, `\"Section Label\"` in this case, is displayed above all its items, which are inserted into the outer menu. In this way, sections can be helpful to group multiple menu items, which makes a menu easier to parse without adding another nested level via a submenu.\n\n#### Section Formats\n\n[`add_section!`](@ref) takes one additional, optional argument, which is a [`SectionFormat`](@ref). This enum has a number of values that govern how the section is displayed:\n\n+ `SECTION_FORMAT_CIRCULAR_BUTTONS` displays all its elements in circular buttons\n+ `SECTION_FORMAT_HORIZONTAL_BUTTONS`: display its elements as a row of rectangular buttons\n+ `SECTION_FORMAT_INLINE_BUTTONS`: display all buttons right of the section heading\n\nThe following shows these section formats:\n\n```julia\n# generate a menu model with the 4 weather icons, then add as section with given format\nfunction add_icon_section(title::String, format::SectionFormat)\n        \n    section = MenuModel()\n    add_icon!(section, Icon(#=...=#), action)\n    add_icon!(section, Icon(#=...=#), action)\n    add_icon!(section, Icon(#=...=#), action)\n    add_icon!(section, Icon(#=...=#), action)    \n    add_section!(icon_model, title, section, format)\nend\n\nadd_icon_section(\"Normal\", SECTION_FORMAT_NORMAL)\nadd_icon_section(\"Horizontal Buttons\", SECTION_FORMAT_HORIZONTAL_BUTTONS)\nadd_icon_section(\"Inline Buttons:  \", SECTION_FORMAT_INLINE_BUTTONS)\nadd_icon_section(\"Circular Buttons\", SECTION_FORMAT_CIRCULAR_BUTTONS)\n```\n![](../assets/menu_model_section_formats.png)\n\nUsing `SectionFormat` and mixing several menu item types, we can construct arbitrarily complex menus. We should realize that the highest priority when constructing menu items is the **user experience**. Presenting users with a giant, deeply nested mess of buttons may be very functional, but it may not be very usable to anyone but the developers themself.\n\n---\n\n## Displaying Menus\n\nNow that we have learned to construct the menu **model**, we should turn our attention to the **view**, a widget displaying a `MenuModel`.\n\n### PopoverMenu\n\n[`PopoverMenu`](@ref) is a sibling of [`Popover`](@ref). It consists of a small dialog that is only displayed when we ask it to. While `Popover` displays an arbitrary widget, `PopoverMenu` displays a menu model.\n\nFor ease of use, it's easiest to connect the `PopoverMenu` to a [`PopoverButton`](@ref), just like we did with `Popover`:\n\n```julia\nmodel = MenuModel()\n\n# fill `model` here\n\npopover_menu = PopoverMenu(model)\npopover_button = PopoverButton(popover_menu)\n\n# add the button to the window so we can click it\nset_child!(window, popover_button)\n```\n\nThe `PopoverMenu`-`PopoverButton` combo should be reserved for **context menus**, which are menus that act on some local part of the application. For a menu that affects the entire application and should be accessible at all times, we should use the next menu model view instead.\n\n### MenuBar\n\nFamiliar to any user of a modern desktop GUI, [`MenuBar`](@ref) is a widget that is usually displayed at the top of the main application window:\n\n![](../assets/menu_bar.png)\n\nWe see that `MenuBar` is a horizontal list of items. When the user clicks on one of the items, a nested menu opens. Just like before, menus can be nested a theoretically infinite number of times.\n\nUnlike `PopoverMenu`, `MenuBar` requires its `MenuModel` to have a certain structure: **all top-level items have to be submenus**.\n\nWhat does this mean? Let's work through the menu shown in the image above. We created it using the following snippet:\n\n```julia\nroot = MenuModel()\n\nfile_submenu = MenuModel()\nadd_action!(file_submenu, \"Open\", #= action =#)    \n\nfile_recent_submenu = MenuModel()\nadd_action!(file_recent_submenu, \"Project 01\", #= action =#)\nadd_action!(file_recent_submenu, \"Project 02\", #= action =#)\nadd_action!(file_recent_submenu, \"Other...\", #= action =#)\nadd_submenu!(file_submenu, \"Recent...\", file_recent_submenu)\n\nadd_action!(file_submenu, \"Save\", #= action =#)\nadd_action!(file_submenu, \"Save As...\", #= action =#)\nadd_action!(file_submenu, \"Exit\", #= action =#)\n\nhelp_submenu = MenuModel()\nadd_submenu!(root, \"File\", file_submenu)\nadd_submenu!(root, \"Help\", help_submenu)\n\nmenubar = MenuBar(model)\n```\n\nWhere in a real application, each item will have a different action.\n\nThis code can be quite hard to read. To make the nesting easier to understand, we'll write it out as if it were a file system folder structure:\n\n```\nmodel \\\n    File \\\n        Open\n        Recent... \\\n            Project 01\n            Project 02\n            Other...\n        Save\n        Save As\n        Exit\n    Help \\\n        (...)\n```\n\nWhere any line suffixed with a `\\` is a submenu.\n\nWe see that we have four models in total. The top-most menu model is called `root`, it is what `MenuBar` will be initialized with.\nNext, we have the model called `file_submenu`, which has the title `File`. It contains five items, titled `Open`, `Recent...`, `Save`, `Save As`, and `Exit`. `Recent...` is a submenu-type item, created from a `MenuModel` called `file_recent_submenu` in the above code. This model, in turn, has three items.  On the same level as `File`, we have a second menu `Help`.\n\nThe **top-level** menu is `root`. It is used as the argument for the constructor of `MenuBar`. We see that all direct children of `root` (`File` and `Help`) **are themselves submenus** (they were added using `add_submenu!`). \n\nNo direct child of `root` is an \"action\"-, \"widget\"-, \"icon\"- or \"section\"-type item. This is what is required for `MenuBar`. All top-level items have to be submenus.\n\n!!! Warning\n    Due to a bug in the backend, as of `v0.3.0`, a menu model used for a `MenuBar` **may not have a \"widget\"-type item in a submenu of a submenu**.\n\n    This means we *can* add a widget to any submenu of `root`, but we may not add \n    a widget to any submenu that is nested any deeper than a direct child of `root`.\n\n    This bug does not affect `PopoverMenu`, for whom we can put a widget at any \n    depth. `PopoverMenu` has no requirement as to the structure of its menu model, while `MenuBar` requires that all top-level items are submenus and that no submenu of a submenu may have a \"widget\"-type item.\n\n---\n\n## Style End Note\n\nMenus are extremely powerful and complex to construct. With practice and good software / UI design, we can create deep, complex menus that are still easy to understand and use. We, as developers, should make this our priority. \n\nSome additional notes:\n\n### Ellipses\n\nSome may be curious as to why some menu items have `...` added at the end of their labels, while others do not. This is not a universal standard, but it is common for `...` to indicate that clicking this item will open another window, submenu, or dialog. If clicking an item simply triggers an action (such as `Save` or `Exit`), `...` is omitted. If the item opens a window, widget, or submenu, `...` is appended to the menu label, as is the case with `Recent...` above.\n\n### Maximum Menu Depth\n\nRegarding menu depth, the best practice is to never go deeper than three levels. The above example with `File > Recent... > Project 01` shows a good situation in which a 3-level-deep menu may be justified. Going any deeper is rarely a good course of action. Adding a section should always be considered *before* deciding to add a submenu.\n\n### Section Grouping\n\nLastly, some schools of UI design believe that **every menu item should be inside a section**. For example, if we were to follow this philosophy for our above `MenuBar` example, we would redesign it like this:\n\n![](../assets/menu_bar_with_sections.png)\n\nThis adds considerable complexity to the code (adding 4 models, one for each section, making our total 8). In return, items are grouped logically, and each item gets a \"heading\", which helps make long menus easier to understand. For this small example, this is most likely unnecessary, but it will be more attractive for a menubar with dozens of items.\n\nIn the end, each UI designer should decide for themselves which technique to use. What all should agree on, however, is that ease of use for the end-user is the most important thing. It should trump ease of development in every case. If something is harder for us developers but makes it easier for our users, we should go through the effort of doing it.\n"
  },
  {
    "path": "docs/src/01_manual/09_native_rendering.md",
    "content": "# Chapter 9: Native Rendering\n\nIn this chapter we will learn:\n+ How to use `RenderArea`\n+ How to draw any shape\n+ How to efficiently show an image as a texture\n+ How to change the currently used blend mode\n+ How to apply a 3D transform to a shape GPU-side\n+ How to compile a GLSL Shader and set its uniforms\n\n!!! compat \n    In `v0.2.0` and earlier, features from this chapter were not available on MacOS. Since `v0.3.0`, 100% of Mousetrap is portable, meaning MacOS is now fully supported.\n\n---\n\n!!! details \"Native Rendering on Linux Wayland\"\n    Linux users using the Wayland windowing system may encounter the following error message when starting Mousetrap:\n\n    ```\n    In gdk_window_create_gl_context: Failed to create EGL display\n    ```\n\n    This means we would be unable to use the `RenderArea` widget, which this chapter centers around.\n\n    To address this, we need to [locate](https://linuxize.com/post/locate-command-in-linux/) the directory `egl_vendor.d`, which has to be non-empty and contain a JSON file\n\n    ```\n    locate egl_vendor.d\n    ```\n    ```\n    /etc/glvnd/egl_vendor.d\n    /usr/share/glvnd/egl_vendor.d\n    /usr/share/glvnd/egl_vendor.d/50_mesa.json\n    ```\n\n    Here we choose `/usr/share/glvnd/egl_vendor.d` instead of `/etc/glvnd/egl_vendor.d`, because only the former contains a JSON file, `50_mesa.json` in this case.\n\n    Armed with this path, we then execute, in a terminal, **before** Julia is started:\n\n    ```\n    export __EGL_VENDOR_LIBRARY_DIRS=/usr/share/glvnd/egl_vendor.d\n    ```\n\n    After which any OpenGL-related features from this chapter will become available. \n    \n    To make this change permanent, we can paste the above line into the `~/.bashrc` text file, which will be loaded automatically anytime a terminal starts. \n\n!!! details \"Manually Disabling the OpenGL Component\"\n    We can disable all features from this chapter by setting the environment variable `MOUSETRAP_DISABLE_OPENGL_COMPONENT` to `TRUE`. This may be necessary for machines that do not have an OpenGL 3.3-compatible graphics card driver. See [here](https://github.com/Clemapfel/Mousetrap.jl/issues/25#issuecomment-1731349366) for more information.\n\n---\n\nIn the [chapter on widgets](04_widgets.md), we learned that we can create new widgets by combining already predefined widgets as a *compound widget*. We can create a new widget that has a `Scale`, but we cannot render our own scale with, for example, a square knob. In this chapter, this will change.\n\nBy using the native rendering facilities Mousetrap provides, we are free to create any shape we want, assembling new widgets pixel-by-pixel, line-by-line, then adding interactivity using the [event controller system](./05_event_handling.md).\n\n## RenderArea\n\nThe central widget of this chapter is [`RenderArea`](@ref), which is a canvas used to display native graphics. At first, it may seem very simple:\n\n```julia\nrender_area = RenderAre()\n```\n\nThis will render as a transparent area, because `RenderArea` has no graphic properties of its own. Instead, we need to create separate **shapes**, then **bind them for rendering**, after which `RenderArea` will display the shapes for us. Still, `RenderArea` will follow its properties just like any other widget, for example, it will have an allocated size that follows size-hinting and the expansion property.\n\n## Shapes\n\nIn general, shapes are defined by a number of **vertices**. A vertex has a position in 2D space, a color, and a texture coordinate. In this chapter, we will learn what each of these properties does and how they coalesce to form a shape.\n\n### Vertex Coordinate System\n\nA shape's vertices define where inside the `RenderArea` it will be drawn. The coordinate system used by shapes is different from the one we use for widgets. OpenGL, on which the native rendering component of Mousetrap is based, uses the **right-hand coordinate system**, which is familiar from traditional math:\n\n![](https://learnopengl.com/img/getting-started/coordinate_systems_right_handed.png)\n\n(source: [learnopengl.com](https://learnopengl.com/img/getting-started))\n\nWe assume the z-coordinate for any vertex is set to 0, reducing the coordinate system to a 2D plane. \n\nWe will refer to this coordinate system as **GL coordinates**, while the widget coordinate system is used for `ClickEventController` and the like as **widget space coordinates**.\n\nTo further illustrate the difference between GL and widget space coordinates, consider this table, where `w` is the widgets' width, `h` is the widgets' height, in pixels:\n \n| Conceptual Position | GL Coordinates | Widget Space Coordinates |\n|---------------------|---------------|-------------------------|\n| top left            | `(-1, +1)`    | `(0, 0)` |\n| top                 | `( 0, +1)`    | `(w / 2, 0)`|\n| top right           | `(+1, +1)`    | `(w, 0)`|\n| left                | `(-1,  0)`    | `(0, y / 2)`|\n| center              | `( 0,  0)`    | `(w / 2, y / 2)`|\n| right               | `(+1,  0)`    | `(w, y / 2)`|\n| bottom left         | `(-1, -1)`    | `(0, y)`|\n| bottom              | `( 0, -1)`    | `(w / 2, y)`|\n| bottom right        | `(+1, -1)`    | `(w, y)`|\n\nWe see that the OpenGL coordinate system is **normalized**, meaning the values of each coordinate are inside `[-1, 1]`, while the widget-space coordinate system is **absolute**, meaning the values of each coordinate take the allocated size of the widget into account, being inside `[0, w]` and `[0, h]` for the x- and y-coordinates, respectively, in pixels.\n\nThe gl coordinate system is anchored at the center of the render area's allocated area, while widget space is anchored at the top left.\n\nAt any point, we can convert between the coordinate systems using [`from_gl_coordinates`](@ref) and [`to_gl_coordinates`](@ref), which take the `RenderArea` as their first argument. These functions convert gl-to-widget-space and widget-space-to-gl coordinates, respectively. Of course, the widget space coordinates depend on the current size of the `RenderArea`. When it is resized, the old coordinates may be out of date, which is why using the *normalized* GL system is preferred in an application where the canvas can change size frequently.\n\n### Rendering Shapes\n\nWe'll now create our first shape, which is a point. A point is always exactly one pixel in size.\n\n```julia\nshape = Shape()\nas_point!(shape, Vector2f(0, 0))\n```\n\nWhere we use `Vector2f(0, 0)` as the point's position, meaning it will appear at the origin of the `RenderArea`, its center.\n\nThe above is directly equivalent to the following:\n\n```julia\nshape = Point(Vector2f(0, 0))\n```\n\nWe see that\n\n```julia\ntypeof(shape)\n```\n```\nMousetrap.Shape\n```\n\nThe variable `shape` is still of type `Shape`. [`Point`](@ref) is simply a convenience function for initializing a shape, then calling [`as_point!`](@ref) on that instance.\n\nFor this shape to show up on screen, we need to **bind it for rendering**. To do this, we create a [`RenderTask`](@ref) which wraps the shape, then use [`add_render_task!`](@ref) to add it to the scheduled tasks of our `RenderArea`. From this point onwards, anytime the `RenderArea` goes through a render cycle, it will also draw all its tasks, including our point:\n\n```julia\nshape = Point(Vector2f(0, 0))\nadd_render_task!(render_area, RenderTask(shape))\n```\n\n![](../assets/shape_hello_world.png)\n\n!!! details \"How to generate this Image\"\n    ```julia\n    using Mousetrap\n    main() do app::Application\n\n        set_current_theme!(app, THEME_DEFAULT_DARK)\n\n        window = Window(app)\n        set_title!(window, \"Mousetrap.jl\")\n        render_area = RenderArea()\n\n        shape = Point(Vector2f(0, 0))\n        add_render_task!(render_area, RenderTask(shape))\n\n        frame = AspectFrame(1.0, render_area)\n        set_size_request!(frame, Vector2f(150, 150))\n        set_margin!(frame, 10)\n        set_child!(window, frame)\n        present!(window)\n    end\n    ```\n\nIf we want to remove a task from our render area, we need to call [`clear_render_tasks!`](@ref), then add all other render tasks again.\n\n### Shape Types\n\nMousetrap offers a wide variety of pre-defined shape types. Thanks to this, we don't have to manually adjust each vertex position.\n\n#### Point\n\nAs we've seen, [`Point`](@ref) is always exactly one pixel in size. Its constructor takes a single `Vector2f`:\n\n```julia\npoint = Point(Vector2f(0, 0))\n```\n\n![](../assets/shape_point.png)\n\n#### Points\n\n[`Points`](@ref) is a number of points. Instead of taking a single `Vector2f`, its constructor takes `Vector{Vector2f}`:\n\n```julia\npoints = Points([\n    Vector2f(-0.5, 0.5), \n    Vector2f(0.5, 0.5), \n    Vector2f(0.0, -0.5)\n])\n```\n\n![](../assets/shape_points.png)\n\nRendering several points using `Points` is much more performant, a four-vertex `Points` is much faster than rendering four `Point` with one vertex each.\n\n#### Line \n\nA [`Line`](@ref) is defined by two points, between which a 1-pixel thick line will be drawn:\n\n```julia\nline = Line(\n    Vector2f(-0.5, +0.5), \n    Vector2f(+0.5, -0.5)\n)\n```\n\n![](../assets/shape_line.png)\n\n#### Lines\n\n[`Lines`](@ref) will draw unconnected lines. It takes a vector of point pairs. For each of these, a 1-pixel thick line will be drawn between them.\n\n```julia\nlines = Lines([\n    Vector2f(-0.5, 0.5) => Vector2f(0.5, -0.5),\n    Vector2f(-0.5, -0.5) => Vector2f(0.5, 0.5)\n])\n```\n\n![](../assets/shape_lines.png)\n\n#### LineStrip\n\n[`LineStrip`](@ref), not to be confused with `Lines`, is a *connected* series of lines. Thus, it takes a vector of points, as opposed to a vector of point pairs. \n\nA line will be drawn between each successive pair of coordinates, meaning the last vertex of the previous line will be used as the first vertex of the current line. If the supplied vector of points is `{a1, a2, a3, ..., a(n)}` then `LineStrip` will render as a series of lines with coordinate pairs `{a1, a2}, {a2, a3}, ..., {a(n-1), a(n)}`\n\n```julia\nline_strip = LineStrip([\n    Vector2f(-0.5, +0.5),\n    Vector2f(+0.5, +0.5),\n    Vector2f(+0.5, -0.5),\n    Vector2f(-0.5, -0.5)\n])\n```\n\n![](../assets/shape_line_strip.png)\n\n#### Wireframe\n\n[`Wireframe`](@ref) is similar to a `LineStrip`, except that it also connects the last and first vertex. For a supplied vector of points `{a1, a2, a3, ..., an}`, the series of lines will be `{a1, a2}, {a2, a3}, ..., {a(n-1), a(n)}, {a(n), a1}`, the last vertex-coordinate pair is what distinguishes it from a `LineStrip`. As such, `Wireframe` is sometimes also called a **line loop**.\n\n```julia\nwireframe = Wireframe([\n    Vector2f(-0.5, +0.5),\n    Vector2f(+0.5, +0.5),\n    Vector2f(+0.5, -0.5),\n    Vector2f(-0.5, -0.5)\n])\n```\n\n![](../assets/shape_wireframe.png)\n\nNote how this shape takes the same coordinates as `LineStrip`, but draws one more line, connecting the last to the first vertex.\n\n#### Triangle\n\nA [`Triangle`](@ref) is constructed as one would expect, using three `Vector2f`, one for each of its vertices:\n\n```julia\ntriangle = Triangle(\n    Vector2f(-0.5, 0.5),\n    Vector2f(+0.5, 0.5),\n    Vector2f(0.0, -0.5)\n)\n```\n\n![](../assets/shape_triangle.png)\n\n#### Rectangle\n\nA [`Rectangle`](@ref) has four vertices. It is defined by its top-left point and its width and height. As such, it is always axis-aligned.\n\n```julia\nrectangle = Rectangle(\n    Vector2f(-0.5, 0.5), # top left\n    Vector2f(1, 1)       # width, height\n)\n```\n\n![](../assets/shape_rectangle.png)\n\n#### Circle\n\nA [`Circle`](@ref) is constructed from a center point and radius. We also need to specify the number of outer vertices used for the circle. This number will determine how \"smooth\" the outline is. For example, a circle with 3 outer vertices is an equilateral triangle; a circle with 4 outer vertices is a square; a circle with 5 is a pentagon, etc. \n\nAs the number of outer vertices increases, the shape approaches a mathematical circle, but will also require more processing power.\n\n```julia\ncircle = Circle(\n    Vector2f(0, 0), # center\n    0.5,            # radius\n    32              # n outer vertices\n)\n```\n\n![](../assets/shape_circle.png)\n\n#### Ellipse\n\nAn [`Ellipse`](@ref) is a more generalized form of a `Circle`. It has two radii, the x- and y-radius:\n\n```julia\nellipse = Ellipse(\n    Vector2f(0, 0), # center\n    0.6,            # x-radius\n    0.4,            # y-radius\n    32              # n outer vertices\n)\n```\n\n![](../assets/shape_ellipse.png)\n\n#### Polygon\n\nThe most general form of convex shapes, [`Polygon`](@ref) is constructed using a vector of vertices, which will be sorted clockwise, then their [outer hull](https://en.wikipedia.org/wiki/Convex_hull) will be calculated, which results in the final convex polygon:\n\n```julia\npolygon = Polygon([\n    Vector2f(0.0, 0.75),\n    Vector2f(0.75, 0.25),\n    Vector2f(0.5, -0.75),\n    Vector2f(-0.5, -0.5),\n    Vector2f(-0.75, 0.0)\n])\n```\n\n![](../assets/shape_polygon.png)\n\nWe note that a 4-vertex polygon is a rectangle. Therefore, if we want to render a non-axis-aligned rectangle, we should instead use `Polygon` with four vertices.\n\n#### Rectangular Frame\n\nA [`RectangularFrame`](@ref) takes a top-left vertex, a width, a height, and the x- and y-width, the latter of which is the thickness of the frame along the x- and y-axes:\n\n```julia\nrectangular_frame = RectangularFrame(\n    Vector2f(-0.5, 0.5),  # top-left\n    Vector2f(1, 1),       # width, height\n    0.15,                 # x-thickness\n    0.15,                 # y-thickness\n)\n```\n![](../assets/shape_rectangular_frame.png)\n\nNote how the top left and size govern the position and size of the outer perimeter of the rectangular frame.\n\n#### Circular Ring\n\nFor the round equivalent of a rectangular frame, we have [`CircularRing`](@ref), which takes a center, the radius of the outer perimeter, as well as the ring's thickness. Like `Circle` and `Ellipse`, we have to specify the number of outer vertices, which decides the smoothness of the ring:\n\n```julia\ncircular_ring = CircularRing(\n    Vector2f(0, 0),  # center\n    0.5,             # radius of outer circle\n    0.15,            # thickness\n    32               # n outer vertices\n)\n```\n\n![](../assets/shape_circular_ring.png)\n\nAs before, the center and radius determine the position and size of the outer perimeter.\n\n#### Elliptical Ring\n\nA generalization of `CircularRing`, [`EllipticalRing`](@ref) has an ellipse as its outer shape. Its thickness along the horizontal and vertical dimensions are supplied separately, making it more flexible than `CircularRing`.\n\n```julia\nelliptcal_ring = EllipticalRing(\n    Vector2f(0, 0),  # center\n    0.6,             # x-radius\n    0.4,             # y-radius\n    0.15,            # x-thickness\n    0.15,            # y-thickness\n    32               # n outer vertices\n)\n```\n\n![](../assets/shape_elliptical_ring.png)\n\n#### Outline\n\nLastly, we have a special shape. [`Outline`](@ref) does not take any vertex positions for its constructor. Instead, we construct an `Outline` shape from **another shape**. It will then generate a wireframe for the outer perimeters of the original shape.\n\nAs the name suggests, this is useful for generating outlines of another shape. By rendering the `Outline` on top of the original shape, we can make it so it better stands out from the background.\n\n```julia\noutline = Outline(triangle)\n```\n![](../assets/shape_triangle_outline.png)\n\n```julia\noutline = Outline(rectangle)\n```\n![](../assets/shape_rectangle_outline.png)\n\n```julia\noutline = Outline(circle)\n```\n![](../assets/shape_circle_outline.png)\n\n```julia\noutline = Outline(ellipse)\n```\n![](../assets/shape_ellipse_outline.png)\n\n```julia\noutline = Outline(polygon)\n```\n![](../assets/shape_polygon_outline.png)\n\n```julia\noutline = Outline(rectangular_frame)\n```\n![](../assets/shape_rectangular_frame_outline.png)\n\n```julia\noutline = Outline(circular_ring)\n```\n![](../assets/shape_circular_ring_outline.png)\n\n```julia\noutline = Outline(elliptical_ring)\n```\n![](../assets/shape_elliptical_ring_outline.png)\n\nWhile possible, it doesn't make much sense to create an `Outline` from a shape that does not have a volume, such as `Point` or `Wireframe`.\n\nRendering white outlines on top of a white shape would make little sense, of course. To achieve the desired effect, we need to make the outline another color, which brings us to the additional properties of `Shape`.\n\n### Shape Properties\n\n#### Vertex Properties\n\nShapes are made up of vertices, whose properties we can manually edit. To set the property of a single vertex of a shape, we use [`set_vertex_color!`](@ref), [`set_vertex_position!`](@ref), and [`set_vertex_texture_coordinate!`](@ref). Each of these takes an index, which is the index of the vertex in clockwise order, 1-based. To know how many vertices a shape actually has, we use [`get_n_vertices`](@ref).\n\nWe will rarely need to modify individual vertices, as working on the `Shape` as a whole is much more convenient.\n\n##### Centroid\n\nThe **centroid** of a shape is the intuitive \"center of mass\". In mathematical terms, it is the component-wise mean of all vertex coordinates. In practice, for many symmetrical shapes such as rectangles, triangles, circles, and ellipses, the centroid will be the \"center\" of the shape, as it is defined in common language. \n\nWe can access the centroid using [`get_centroid`](@ref). To move a shape a certain distance, we move its centroid by that distance by calling [`set_centroid!`](@ref), which will automatically move all other vertices of the shape such that its new centroid is as specified.\n\n#### Rotation\n\nWe can rotate all of a `Shape`'s vertices around a point in GL coordinates by calling [`rotate!`](@ref), which takes an `Angle` as its first argument:\n\n```julia\n# rotate shape around its center\nrotate!(shape, degrees(90), get_centroid(shape))\n```\n\n#### Color\n\nTo change the color of a shape as a whole, we use [`set_color!`](@ref). This simply calls `set_vertex_color!` on all of a shape's vertices. By default, a shape's color will be `RBGA(1, 1, 1, 1)`, white.\n\n#### Visibility\n\nWe can prevent a shape from being rendered by setting [`set_is_visible!`](@ref) to `false`. This is different from making all vertices of a shape have an opacity of `0`. `is_visible` directly hooks into the shape's render function and **prevents it from being called**, as opposed to it completing rendering and not being visible on screen. This means making a shape invisible completely removes any performance penalty that would have been incurred during the render step, which is also called [culling](https://en.wikipedia.org/wiki/Hidden-surface_determination).\n\n#### Bounding Box\n\nWe can access the [axis-aligned bounding box](https://en.wikipedia.org/wiki/Bounding_volume) of a shape with [`get_bounding_box`](@ref), which returns an [`AxisAlignedRectangle`](@ref). This is the smallest axis-aligned rectangle that still contains all of a shape's vertices.\n\nUsing this, we can query the top-left coordinate and size of the bounding box.\n\n---\n\nLastly, each shape has an optional **texture**, which is what the texture coordinate properties of each vertex are used for. If a shape does not have a texture, it will be rendered as a solid color. If it does, the color of each pixel in the texture will be multiplied by the shape's color.\n\n## Textures\n\nIn the chapter on widgets, we learned that we can use the `ImageDisplay` widget to display static images. This works, but has a number of disadvantages:\n\n+ Image data is costly to update\n+ Downloading image data is impossible\n+ Scaling the image will always trigger linear interpolation\n+ The image is always shown in full, as a rectangle\n\nIf we need the additional flexibility, we should instead use a `Shape` along with a [`Texture`](@ref), which represents an image living on the graphics card.\n\nWe create a texture from an `Image` like so:\n\n```julia\nimage = Image()\nload_from_file!(image, \"path/to/image.png\")\n\ntexture = Texture()\ncreate_from_image!(texture, image)\n```\n\nOnce `create_from_image!` is called, the image data is uploaded to the graphics cards' RAM, so we can safely discard the `Image` instance.\n\nTo display the texture on screen, we need to bind it to a shape, and then render that shape:\n\n```julia\ntexture_shape::Shape = Rectangle(Vector2f(-1, 1), Vector2f(2, 2))\nset_texture!(texture_shape, texture)\n\nadd_render_task!(render_area, RenderTask(texture_shape))\n```\n\nHow and where the texture is displayed depends on the shape's vertices **texture coordinate**. These coordinates are in `([0, 1], [0, 1])`, meaning the x- and y- component are in `[0, 1]` each. We will call this coordinate system **texture space**. \n\n| Conceptual Position | Texture Space Coordinates |\n|---------------------|---------------|\n| top left            | `(0, 0)`    |\n| top                 | `(0.5, 0)`    |\n| top right           | `(1, 0)`    |\n| left                | `(0, 0.5)`    |\n| center              | `(0.5, 0.5)`    |\n| right               | `(1, 0.5)`    |\n| bottom left         | `(0, 1)`    |\n| bottom              | `(0.5, 1)`    |\n| bottom right        | `(1, 1)`    |\n\nWe see that, due to the normalized nature of this coordinate system, a texture coordinate is unable to reference a specific pixel. Instead, we use a floating-point coordinate, for which the graphics card will return an **interpolated color**. This is the color any specific pixel on the monitor should assume when the shape is displayed.\n\n#### Scale Mode\n\nSimilar to `Image`s [`as_scaled`](@ref), we have options as to how we want the texture to behave when scaled to a size other than its native resolution. Mousetrap offers the following texture scale modes, which are represented by the enum [`TextureScaleMode`](@ref):\n\n| `TextureScaleMode` | Meaning | Equivalent `InterpolationType` |\n|--------------------|---------|-----|\n| `TEXTURE_SCALE_MODE_NEAREST` | Nearest Neighbor Scaling | `INTERPOLATION_TYPE_NEAREST` |\n| `TEXTURE_SCALE_MODE_LINEAR` | Linear Interpolation | `INTERPOLATION_TYPE_BILINEAR` |\n\nWhile the resulting image behaves similarly to how `InterpolationType` will result in the final image, operating on a texture is much, much more performant. Rescaling a texture is essentially free when done by the graphics card, which is in stark contrast to the capabilities of a CPU, as would be needed for `Image`s `as_scaled`.\n\n#### Wrap Mode\n\nWrap mode governs how the texture behaves when a vertice's texture coordinate components are outside `[0, 1]`. Mousetrap offers the following wrap modes, which are all part of the enum [`TextureWrapMode`](@ref):\n\n| `TextureWrapMode` | Pixel will be filled with |\n|-------------------|--------|\n| `TEXTURE_WRAP_MODE_ZERO` | `RGBA(0, 0, 0, 0)` | \n| `TEXTURE_WRAP_MODE_ONE`| `RGBA(1, 1, 1, 1)` |\n| `TEXTURE_WRAP_MODE_STRETCH` | Nearest outer Edge |\n| `TEXTURE_WRAP_MODE_REPEAT` | Equivalent pixel in `([0, 1], [0, 1]) ` |\n| `TEXTURE_WRAP_MODE_MIRROR` | Equivalent pixel in `(1 - [0, 1], 1 - [0, 1])`| \n\n![](../assets/texture_wrap_modes.png)\n\n!!! details \"How to generate this Image\"\n    ```julia\n    using Mousetrap\n\n    # compound widget that displays a texture with a label\n    struct TexturePage <: Widget\n        center_box::CenterBox\n        label::Label\n        render_area::RenderArea\n        texture::Texture\n        shape::Shape\n\n        function TexturePage(label::String, image::Image, wrap_mode::TextureWrapMode)\n            out = new(\n                CenterBox(ORIENTATION_VERTICAL),\n                Label(\"<tt>\" * label * \"</tt>\"),\n                RenderArea(),\n                Texture(),\n                Rectangle(Vector2f(-1, 1), Vector2f(2, 2))\n            )\n\n            set_expand!(out.render_area, true)\n            set_size_request!(out.render_area, Vector2f(150, 150))    \n\n            set_start_child!(out.center_box, AspectFrame(1.0, Frame(out.render_area)))\n            set_end_child!(out.center_box, out.label)\n            set_margin!(out.label, 10)\n\n            create_from_image!(out.texture, image)\n            set_wrap_mode!(out.texture, wrap_mode)\n            \n            set_texture!(out.shape, out.texture)\n\n            # zoom out texture coordinates by 1 unit\n            set_vertex_texture_coordinate!(out.shape, 1, Vector2f(-1, -1))\n            set_vertex_texture_coordinate!(out.shape, 2, Vector2f(2, -1))\n            set_vertex_texture_coordinate!(out.shape, 3, Vector2f(2, 2))\n            set_vertex_texture_coordinate!(out.shape, 4, Vector2f(-1, 2))\n\n            add_render_task!(out.render_area, RenderTask(out.shape))\n            return out\n        end\n    end\n    Mousetrap.get_top_level_widget(x::TexturePage) = x.center_box\n\n    main() do app::Application\n\n        window = Window(app)\n        set_title!(window, \"Mousetrap.jl\")\n\n        render_area = RenderArea()\n    \n        image = Image()\n        create_from_file!(image, \"docs/src/assets/logo.png\")\n            # this assumes the script is run in `Mousetrap.jl` root\n\n        # replace RGBA(0, 0, 0, 0) pixels with rainbow color\n        size = get_size(image)\n        hue_step = 1 / size.x\n        for i in 1:size.y\n            for j in 1:size.x\n                if get_pixel(image, i, j).a == 0\n                    set_pixel!(image, i, j, HSVA(j * hue_step, 1, 1, 1))\n                end\n            end\n        end\n\n        box = Box(ORIENTATION_HORIZONTAL)\n        set_spacing!(box, 10)\n        set_margin!(box, 10)\n\n        push_back!(box, TexturePage(\"ZERO\", image, TEXTURE_WRAP_MODE_ZERO))\n        push_back!(box, TexturePage(\"ONE\", image, TEXTURE_WRAP_MODE_ONE))\n        push_back!(box, TexturePage(\"STRETCH\", image, TEXTURE_WRAP_MODE_STRETCH))\n        push_back!(box, TexturePage(\"REPEAT\", image, TEXTURE_WRAP_MODE_REPEAT))\n        push_back!(box, TexturePage(\"MIRROR\", image, TEXTURE_WRAP_MODE_MIRROR))\n\n        set_child!(window, box)\n        present!(window)\n    end\n    ```\n\nWhere the default wrap mode is `TEXTURE_WRAP_MODE_REPEAT`.\n\nBy being able to modify the vertex coordinates for any of a shape's vertices, we have much more control over how image data is displayed on screen. Only the part of the texture that conceptually overlaps a shape will be displayed, which is governed by that shape's texture coordinates. \n\n---\n\n## RenderArea Size\n\nBecause shapes do not take into account the size and aspect ratio of their `RenderArea`, we, as developers, should take care that shapes are displayed correctly when this size changes.  \n\nConsider the following example:\n\n```julia\nrender_area = RenderArea()\n\nshape = Ellipse(Vector2f(0, 0), 0.5, 0.5, 32)\nadd_render_task!(render_area, RenderTask(shape))\n\nset_child!(window, render_area)\n```\n\n![](../assets/render_area_stretched.png)\n\nWhere an ellipse with identical x- and y-radii is a circle.\n\nDespite defining the shape as a circle, on screen, it appears stretched. This is because the shape's vertices use the GL coordinate system, which is **normalized**. Thus, how long the x- and y-radii of a circle are depends on the width and height of the `RenderArea` canvas. By widening the window, the render area expands, increasing its width and thus stretching our circle.\n\nWe have two ways to correct this. The easiest of which is putting the render area inside an `AspectFrame`, which forces it to always maintain the correct aspect ratio, square in this case:\n\n```julia\nrender_area = RenderArea()\n\nshape = Circle(Vector2f(0, 0), 0.5, 32)\nadd_render_task!(render_area, RenderTask(shape))\n\nset_child!(window, AspectFrame(1.0, render_area)) # force 1:1 aspect ratio\n```\n\nWhile this corrects our circle, the entire `RenderArea` is now restrained in size, making this solution unviable for applications where we need a `RenderArea` to fill its entire area regardless of its aspect ratio.\n\nThe other way to correct the issue is to modify our circle when `RenderArea` changes shape. This is made possible by the `resize` signal of `RenderArea`, which is emitted whenever its allocated area changes:\n\n`resize` requires a signal handler with the following signature:\n```julia\n(::RenderArea, width::Integer, height::Integer, [::Data_t]) -> void\n```\nWhere `width` and `height` are the new sizes of the `RenderArea` widget, in pixels.\n\nUsing this information and some simple geometry, we can change the x- and y-radius dynamically whenever the `RenderArea` changes aspect ratio:\n\n```julia\n# define resize callback\nfunction on_resize(::RenderArea, width::Integer, height::Integer, shape::Shape)\n\n    # calculate y-to-x-ratio\n    new_ratio = height / width\n\n    # resize the shape by adjusting x-radius\n    as_ellipse!(shape, \n        Vector2f(0, 0),     # old center\n        0.5 * new_ratio,    # new x-radius\n        0.5,                # old y-radius\n        32                  # n vertices\n    )\nend\n\nmain() do app::Application\n    window = Window(app)\n    render_area = RenderArea()\n\n    shape = Ellipse(Vector2f(0, 0), 0.5, 0.5, 32)\n    add_render_task!(render_area, RenderTask(shape))\n\n    # connect callback, providing our shape as `Data_t` argument\n    connect_signal_resize!(on_resize, render_area, shape)\n\n    set_child!(window, render_area)\n    present!(window)\nend\n```\n\n![](../assets/render_area_destretched.png)\n\nHere, the `RenderArea` has a non-square aspect ratio, yet the shape is still displayed as a proper circle. Using signal `resize` like this, we can protect ourselves against side effects from the normalized nature of GL coordinates.\n\n---\n\n## Anti Aliasing\n\nWhen shapes are drawn to the screen, they are *rasterized*, which is when the graphics card takes the mathematical shape in memory and transforms it such that it can be displayed using a limited number of pixels. This process is imperfect, as no number of pixels will be able to draw a perfect curve. One artifact that can appear during this process is **aliasing**, which, in non-technical terms, is when lines appear \"jagged\":\n\n![](https://learnopengl.com/img/advanced/anti_aliasing_zoomed.png)\n\n(Source: [learnopengl.com](https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing))\n\nTo address the unsightly nature of this issue, several remedies are available, the most appropriate of which is called [multi-sampled anti-aliasing (MSAA)](https://www.khronos.org/opengl/wiki/Multisampling). Users of Mousetrap are not required to understand the algorithm behind it, only that it causes jagged edges to appear smoother. \n\nTo enable MSAA, we provide an enum value of type [`AntiAliasingQuality`](@ref) to `RenderArea`s constructor:\n\n```julia\nmsaa_on = RenderArea(ANTI_ALIASING_QUALITY_BETTER)\nmsaa_off = RenderArea(ANTI_ALIASING_QUALITY_OFF)\n```\n\n| `AntiAliasingQuality` Value | # MSAA Samples | \n|-----------------------------|------------|\n| `ANTI_ALIASING_QUALITY_OFF` | 0 (no MSAA) |\n| `ANTI_ALIASING_QUALITY_MINIMAL` | 2 | \n| `ANTI_ALIASING_QUALITY_GOOD` | 4 | \n| `ANTI_ALIASING_QUALITY_BETTER` | 8 | \n| `ANTI_ALIASING_QUALITY_BEST` | 16 | \n\nWhere `ANTI_ALIASING_QUALITY_OFF` will be used when calling the `RenderArea` constructor with no arguments, as we have so far.\n\nThe higher the number of samples, the better the smoothing will be. MSAA comes at a cost: any quality other than `OFF` will induce the `RenderArea` to take about twice as much space in the graphic card's memory. Furthermore, the higher the number of samples, the more time each render step will take.\n\nIt's difficult to convey the result of MSAA using just pictures on a web page due to compression. Instead, readers are encouraged to run the following `main.jl`, which will show off the anti-aliasing in high resolution on the screen:\n\n![](../assets/msaa_comparison.png)\n\n!!! details \"How to generate this Image\"\n    ```julia\n    main() do app::Application\n\n        window = Window(app)\n        set_title!(window, \"Mousetrap.jl\")\n\n        # create render areas with different MSAA modes\n        left_area = RenderArea(ANTI_ALIASING_QUALITY_OFF)\n        right_area = RenderArea(ANTI_ALIASING_QUALITY_BEST)\n\n        # paned that will hold both areas\n        paned = Paned(ORIENTATION_HORIZONTAL)\n\n        # create singular shape, which will be shared between areas\n        shape = Rectangle(Vector2f(-0.5, 0.5), Vector2f(1, 1))\n        add_render_task!(left_area, RenderTask(shape))\n        add_render_task!(right_area, RenderTask(shape))\n\n        # rotate shape 1° per frame\n        set_tick_callback!(paned) do clock::FrameClock\n\n            # rotate shape \n            rotate!(shape, degrees(1), get_centroid(shape))\n\n            # force redraw for both areas\n            queue_render(left_area) \n            queue_render(right_area)\n\n            # continue callback indefinitely\n            return TICK_CALLBACK_RESULT_CONTINUE\n        end\n\n        # setup window layout for viewing\n        for area in [left_area, right_area]\n            set_size_request!(area, Vector2f(150, 150))\n        end\n\n        # caption labels\n        left_label = Label(\"<tt>OFF</tt>\")\n        right_label = Label(\"<tt>BEST</tt>\")\n\n        for label in [left_label, right_label]\n            set_margin!(label, 10)\n        end\n\n        # format paned\n        set_start_child_shrinkable!(paned, false)\n        set_end_child_shrinkable!(paned, false)\n        set_start_child!(paned, vbox(AspectFrame(1.0, left_area), left_label))\n        set_end_child!(paned, vbox(AspectFrame(1.0, right_area), right_label))\n\n        # present\n        set_child!(window, paned)\n        present!(window)\n    end\n    ```\n---\n\n## Render Task\n\n!!! tip \n    The rest of this chapter will assume that readers are familiar with the basics of OpenGL, how to write GLSL shaders, what a shader uniform is, how blending works, and how a linear transform allows us to move a point in 3D space.\n\n    With what we have learned so far in this chapter, we are already well-equipped to be able to accomplish most tasks that require the native rendering component, such as displaying static images or rendering shapes. **Any information after this point should be considered optional to learn**.\n\nSo far, we have registered render tasks using `add_render_task!(render_area, RenderTask(shape))`. Sometimes, we will have to deal with [`RenderTask`](@ref) on its own. Its constructor actually has the following signature:\n\n```julia\nRenderTask(::Shape ; [shader::Shader, transform::GLTransform, blend_mode::BlendMode])\n```\n\nWhere any name after the `;` are [keyword arguments](https://docs.julialang.org/en/v1/devdocs/functions/#Keyword-arguments), which are optional.\n\nWe see that a `RenderTask` actually bundles the following objects:\n+ a `Shape`, which is the shape being rendered\n+ a `Shader`, which is a shader program containing a vertex- and fragment-shader\n+ a `GLTransform`, which is a spatial transform that will be applied to the shape using the vertex shader\n+ a `BlendMode`, which governs which type of blending will take place during the [blit step](https://en.wikipedia.org/wiki/Bit_blit)\n\nUsing these four components, `RenderTask` gathers all objects necessary to render a shape to the screen. All components except for the `Shape` are *optional*. If not specified, a default value will be used instead. This is what allows less experienced users to fully ignore shaders, transforms, and blend modes, simply calling `RenderTask(shape)` will take care of everything for us.\n\n---\n\n## Transforms\n\n[`GLTransform`](@ref) is an object representing a spatial transform. It is called **GL**Transform, because it **uses the GL coordinate system**. Applying a `GLTransform` to a vector in widget- or texture-space will produce incorrect results. They should only be applied to the position attribute of a `Shape`'s vertices.\n\nInternally, a `GLTransform` is a 4x4 matrix of 32-bit floats. It is of size 4x4 because it's intended to be applied to OpenGL positions, which are vectors in 3D space. In Mousetrap, the last coordinate of a spatial position is assumed to be `0`, but it is still part of each vector's data.\n\nAt any time, we can directly access the underlying matrix of a `GLtransform` using `getindex` or `setindex!`:\n\n```julia\ntransform = GLTransform()\nfor i in 1:4\n    for j in 1:4\n        print(transform[i, j], \" \")\n    end\n    print(\"\\n\")\nend\n```\n```\n1 0 0 0\n0 1 0 0\n0 0 1 0\n0 0 0 1\n```\n\nWe see that after construction, `GLTransform` is initialized as the identity transform. No matter the current state of the transform, we can reset it back to this identity matrix by calling [`reset!`](@ref).\n\n`GLTransform` has many common spatial transforms already available as convenient functions, which means we rarely have to modify its values manually. \n\nIt provides the following transformations, which behave identically to those familiar from linear algebra:\n\n+ [`translate!`](@ref), translates in 3D space\n+ [`scale!`](@ref), scales along the x- and y-axis\n+ [`rotate!`](@ref), rotates around a point in 3D space\n\nWe can combine two transforms using [`combine_with`](@ref). If we wish to apply the transform CPU-side to a `Vector2f` or `Vector3f`, we can use [`apply_to`](@ref). \n\nWhile we could apply the transform to each vertex of a `Shape` manually, then render the shape, it is much more performant to do this kind of math GPU-side. By registering the transform with a `RenderTask`, the transform will be forwarded to the vertex shaders, which, for the default vertex shader, is then applied to the shape's vertices automatically:\n\n```julia\nshape = Shape() \n\ntransform = Transform()\ntranslate!(transform, Vector2f(-0.5, 0.1))\nrotate!(transform, degrees(180))\n\ntask = RenderTask(shape; transform = transform)\n```\n\nWhere we used the `transform` keyword argument to specify the transform while leaving the other render task component unspecified. This means the transform is applied automatically during rendering, allowing us to take advantage of the increased performance gained from the GPU architecture.\n\n---\n\n## Blend Mode\n\nAs the third component of a render task, we have the **blend mode**. This governs how two colors behave when rendered on top of each other. \n\nLet the color currently in the frame buffer be `destination`, while the newly added color will be `origin`.  Each [`BlendMode`](@ref), then, behaves as follows:\n\n| `BlendMode`        | Resulting Color                     |\n|--------------------|-------------------------------------|\n| `BLEND_MODE_NONE`             | `origin.rgb + 0 * destination.rgb`  | \n| `BLEND_MODE_NORMAL`           | [traditional alpha-blending](https://en.wikipedia.org/wiki/Alpha_compositing)          |\n| `BLEND_MODE_ADD`              | `origin.rgba + destination.rgba`    |\n| `BLEND_MODE_SUBTRACT`         | `origin.rgba - destination.rgba`    |\n| `BLEND_MODE_REVERSE_SUBTRACT` | `destination.rgba - origin.rgba`    | \n| `BLEND_MODE_MULTIPLY`         | `origin.rgba * destination.rgba`    |\n| `BLEND_MODE_MIN`              | `min(origin.rgba, destination.rgba)` |\n| `BLEND_MODE_MAX`              | `max(origin.rgba, destination.rgba)` | \n\nWhere `+`, `*`, and `-` are component-wise operations.\n\nUsers may be familiar with the names of the blend modes from traditional image editors such as GIMP or Photoshop.\n\nIf left unspecified, `RenderTask` will use `BLEND_MODE_NORMAL`.\n\n---\n\n## Shaders\n\nAs the last component of a `RenderTask`, we have [`Shader`](@ref), which represents an OpenGL shader program that contains already compiled **fragment** and **vertex** shaders. These shaders are written in [GLSL](https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL), which will not be taught in this manual. \n\n### Compiling Shaders\n\nTo create a shader, we first instantiate a [`Shader`](@ref), then use [`create_from_file!`](@ref) or [`create_from_string!`](@ref) to compile the shader from GLSL code. After which the component is automatically bound it to the shader program, overriding whatever shader component was there before. \n\nWe use `SHADER_TYPE_FRAGMENT` or `SHADER_TYPE_VERTEX` to specify which of the two shader types we are targeting.\n\n```julia\nshader = Shader()\n\n# compile fragment shader\ncreate_from_string!(shader, SHADER_TYPE_FRAGMENT, \"\"\"\n    #version 330\n        \n    in vec4 _vertex_color;\n    in vec2 _texture_coordinates;\n    in vec3 _vertex_position;\n\n    out vec4 _fragment_color;\n\n    void main()\n    {\n        vec2 pos = _vertex_position.xy;\n        _fragment_color = vec4(pos.y, dot(pos.x, pos.y), pos.x, 1);\n    }\n\"\"\")\n\n# bind with render task, which will automatically apply it to each fragment of `shape`\ntask = RenderTask(shape; shader = shader)\nadd_render_task!(render_area, task)\n```\n\n![](../assets/shader_hello_world.png)\n\n!!! details \"How to generate this Image\"\n    ```julia\n    using Mousetrap\n    main() do app::Application\n\n        window = Window(app)\n        set_title!(window, \"Mousetrap.jl\")\n        render_area = RenderArea()\n        shape = Rectangle(Vector2f(-1, 1), Vector2f(2, 2))\n\n        shader = Shader()\n        create_from_string!(shader, SHADER_TYPE_FRAGMENT, \"\"\"\n            #version 330\n                \n            in vec4 _vertex_color;\n            in vec2 _texture_coordinates;\n            in vec3 _vertex_position;\n        \n            out vec4 _fragment_color;\n        \n            void main()\n            {\n                vec2 pos = _vertex_position.xy;\n                _fragment_color = vec4(pos.y, dot(pos.x, pos.y), pos.x, 1);\n            }\n        \"\"\")\n        \n        task = RenderTask(shape; shader = shader)\n        add_render_task!(render_area, task)\n\n        frame = AspectFrame(1.0, Frame(render_area))\n        set_size_request!(frame, Vector2f(150, 150))\n        set_margin!(frame, 10)\n        set_child!(window, frame)\n        present!(window)\n    end\n    ```\n\nIf we do not initialize the vertex- or fragment shader, the **default shader component will be used**. It may be instructive to see how the default shaders are defined, as any user-defined shader should build upon them.\n\n### Default Vertex Shader\n\nThis is the default vertex shader, used whenever we do not supply a custom vertex shader for a `Shader` instance:\n\n```glsl\n#version 330\n\nlayout (location = 0) in vec3 _vertex_position_in;\nlayout (location = 1) in vec4 _vertex_color_in;\nlayout (location = 2) in vec2 _vertex_texture_coordinates_in;\n\nuniform mat4 _transform;\n\nout vec4 _vertex_color;\nout vec3 _vertex_position;\nout vec2 _texture_coordinates;\n\nvoid main()\n{\n    gl_Position = _transform * vec4(_vertex_position_in, 1.0);\n    _vertex_color = _vertex_color_in;\n    _vertex_position = _vertex_position_in;\n    _texture_coordinates = _vertex_texture_coordinates_in;\n}\n```\n\nWhere any variable name prefixed with `_` signals that it was defined outside of `main`.\n\nWe see that it requires OpenGL 3.3 due to the `location` syntax. In terms of behavior, this shader simply forwards the interpolated vertex attributes to the fragment shader.\n\nThe current vertices' position is supplied via `_vertex_position_in`, the vertices' texture coordinates as `_vertex_color_in`, and the vertices' texture coordinates are `_vertex_texture_coordinates`. These values will contain the data from the Julia-side `Shape`. We should take care that the `location` attribute exactly matches this order, `0` for vertex position, `1` for vertex color, `2` for texture coordinate.\n\nThe output variables of the vertex shader are `_vertex_color`, `_texture_coordinates`, and `_vertex_position`, which need to be assigned with results gained from within the vertex shader. The shader has furthermore access to the uniform `_transform`, which holds the `GLTransform` the current `RenderTask` associates with the current `Shape`. \n\n### Default Fragment Shader\n\n```glsl\n#version 330\n\nin vec4 _vertex_color;\nin vec2 _texture_coordinates;\nin vec3 _vertex_position;\n\nout vec4 _fragment_color;\n\nuniform int _texture_set;\nuniform sampler2D _texture;\n\nvoid main()\n{\n    if (_texture_set != 1)\n        _fragment_color = _vertex_color;\n    else\n        _fragment_color = texture2D(_texture, _texture_coordinates) * _vertex_color;\n}\n```\n\nThe fragment shader is handed `_vertex_color`, `_texture_coordinate`, and `_vertex_position`, which we recognize as the output of the vertex shader. The output of the fragment shader is `_fragment_color`.\n\nThe default fragment shader respects two uniforms, `_texture`, which is the texture of the shape we are currently rendering, and `_texture_set`, which is `1` if we have called `set_texture!` on the current `Shape` instance, `0` otherwise.\n\nUsers aiming to experiment with shaders should take care to not modify the name or `location` of any of the `in` or `out` variables of either shader. These names, along with the required shader version, should not be altered.\n\n### Binding Uniforms\n\nBoth the vertex and fragment shaders make use of **uniforms**. We've seen that `_transform`, `_texture`, and `_texture_set` are assigned automatically. Mousetrap users can furthermore freely add new uniforms, conveniently assigning them using `RenderTask`.\n\nTo bind a uniform, we first need a CPU-side value that should be uploaded to the graphics card. Let's say we want to use a certain color in our fragment shader, replacing the shape's fragment color with that color. We would write the fragment shader as follows:\n\n```glsl\n#version 330\n\nin vec4 _vertex_color;\nin vec2 _texture_coordinates;\nin vec3 _vertex_position;\n\nout vec4 _fragment_color;\n\nuniform vec4 _color_rgba; // new uniform\n\nvoid main()\n{\n    _fragment_color = _color_rgba; // set fragment color to uniform\n}\n```\nTo set the value of `_color_rgba`, we use [`set_uniform_rgba!`](@ref), which is called on the **render task**, not the shader itself. This will make the render task store the CPU-side value as long as it is needed, automatically forwarding it to the shader during rendering.\n\n`set_uniform_rgba!` is one of many `set_uniform_*!` functions that allow us to bind Julia-side values to OpenGL shader uniforms:\n\nThe following types can be assigned this way:\n\n| Julia Type    | `RenderTask` function   | GLSL Uniform Type |\n|---------------|-------------------------|-------------------|\n| `Cfloat`      | `set_uniform_float`     | `float`           |\n| `Cint`        | `set_uniform_int`       | `int`             |\n| `Cuint`       | `set_uniform_uint`      | `uint`            |\n| `Vector2f`    | `set_uniform_vec2`      | `vec2`            |\n| `Vector3f`    | `set_uniform_vec3`      | `vec3`            |\n| `Vector4f`    | `set_uniform_vec4`      | `vec4`            |\n| `GLTransform` | `set_uniform_transform` | `mat4x4`          |\n| `RGBA`        | `set_uniform_rgba`      | `vec4`            |\n| `HSVA`        | `set_uniform_hsva`      | `vec4`            |\n\nWe would therefore set the `_color_rgba` uniform value like so:\n\n```julia\n# create shader\nshader = Shader()\ncreate_from_string!(shader, SHADER_TYPE_FRAGMENT, \"\"\"\n    #version 330\n\n    in vec4 _vertex_color;\n    in vec2 _texture_coordinates;\n    in vec3 _vertex_position;\n\n    out vec4 _fragment_color;\n\n    uniform vec4 _color_rgba; // uniform we want to assign\n\n    void main()\n    {\n        _fragment_color = _color_rgba;\n    }\n\"\"\")\n\n# create shape and task\nshape = Shape()\ntask = RenderTask(shape; shader = shader) # shader bound to `shader` keyword argument\n\n# set uniform\nset_uniform_rgba!(task, \"_color_rgba\", RGBA(1, 0, 1, 1))\n```\n\n![](../assets/shader_rgba_uniform.png)\n\n!!! details \"How to generate this Image\"\n    ```julia\n    using Mousetrap\n    main() do app::Application\n\n        window = Window(app)\n        set_title!(window, \"Mousetrap.jl\")\n        render_area = RenderArea()\n        shape = Rectangle(Vector2f(-1, 1), Vector2f(2, 2))\n\n        shader = Shader()\n        create_from_string!(shader, SHADER_TYPE_FRAGMENT, \"\"\"\n            #version 330\n                \n            in vec4 _vertex_color;\n            in vec2 _texture_coordinates;\n            in vec3 _vertex_position;\n        \n            out vec4 _fragment_color;\n\n            uniform vec4 _color_rgba;\n        \n            void main()\n            {\n                _fragment_color = _color_rgba;\n            }\n        \"\"\")\n        \n        task = RenderTask(shape; shader = shader)\n        set_uniform_rgba!(task, \"_color_rgba\", RGBA(1, 0, 1, 1))\n\n        add_render_task!(render_area, task)\n\n        frame = AspectFrame(1.0, Frame(render_area))\n        set_size_request!(frame, Vector2f(150, 150))\n        set_margin!(frame, 10)\n        set_child!(window, frame)\n        present!(window)\n    end\n    ```\n\nWhere the name used in `set_uniform_*!` has to exactly match the variable name in GLSL.\n\nWith this, we have a convenient way to specify shader uniforms without having to manually update the shader each time it is bound for rendering. `RenderTask` does this for us.\n\n---\n\n## Rendering to a Texture\n\n!!! compat\n    This feature is not yet implemented, this section is incomplete.\n"
  },
  {
    "path": "docs/src/01_manual/10_theme_customization.md",
    "content": "# Chapter 10: Theme & Widget Customization\n\nIn this chapter, we will learn:\n+ How to swap between light- and dark- mode\n+ How to create custom animations\n+ How to apply a spatial transform to any widget\n+ How to change the look of a widget using CSS\n+ How to change the default colors used for the global theme\n+ How to animate widgets using CSS\n\n!!! compat \n    These features are only available in Mousetrap v0.2.0 or newer\n\n---\n\nAs our app grows and becomes closer to what we envisioned for our project, we want to not only customize the layout and functionality of widgets but also how each widget looks. This can range from small changes such as changing something that is blue by default to green, or large sweeping changes that affect the entire application, such as moving to a light- or dark-, low- or high-contrast theme, or even using a completely custom theme.\n\nMousetrap allows for all of these options. Using its very powerful theme customization component, we can customize our app to a point where most will not be able to tell it was ever Mousetrap- / GTK4-based at all.\n\n## Switching between Dark- and Light Mode\n\nThe most common task that almost any app will want to offer is for the user to be able to swap between light and dark mode. This is a ubiquitous feature of modern UI, as such, Mousetrap offers a very simple way of changing the global theme.\n\nMousetrap supports four default application-wide themes, which are values of enum [`Theme`](@ref):\n\n+ `THEME_DEFAULT_LIGHT`\n+ `THEME_DEFAULT_DARK`\n+ `THEME_HIGH_CONTRAST_LIGHT`\n+ `THEME_HIGH_CONTRAST_DARK`\n\nAt any point after the back-end has been initialized, we can swap the global theme using [`set_current_theme!`](@ref). This will immediately change the look of all widgets and windows, allowing apps to change the entire GUI with just one function call at runtime.\n\nFor example, to create a window that has a button to switch between light and dark themes in its header bar, we could do the following:\n\n```julia\nmain() do app::Application\n\n    window = Window(app)\n\n    # add theme swap button to windows header bar\n    header_bar = get_header_bar(window)\n    swap_button = Button()\n    set_tooltip_text!(swap_button, \"Click to Swap Themes\")\n    connect_signal_clicked!(swap_button, app) do self::Button, app::Application\n\n        # get currently used theme\n        current = get_current_theme(app)\n\n        # swap light with dark, preservng whether the theme is high contrast\n        if current == THEME_DEFAULT_DARK\n            next = THEME_DEFAULT_LIGHT\n        elseif current == THEME_DEFAULT_LIGHT\n            next = THEME_DEFAULT_DARK\n        elseif current == THEME_HIGH_CONTRAST_DARK\n            next = THEME_HIGH_CONTRAST_LIGHT\n        elseif current == THEME_HIGH_CONTRAST_LIGHT\n            next = THEME_HIGH_CONTRAST_DARK\n        end\n\n        # set new theme\n        set_current_theme!(app, next)\n    end\n    push_front!(header_bar, swap_button)\n    present!(window)\nend\n```\n\n---\n\n## Animation\n\nWe've seen in the chapter on widgets that certain kind of widgets animate their children. For example, when switching between two pages of a [`Stack`](@ref), or when a [`Revealer`](@ref) reveals its child, an animation is played depending on which enum value was chosen using [`set_transition_type!`](@ref). \n\nMousetrap offers a convenient mechanism for implementing animations like this from scratch, which this section will demonstrate.\n\nIf we want to animate a widget \"fading out\" over 1 second, that is, its opacity changes from 1 to 0 over that period of time, we should decrease the opacity by a specified amount each frame. Tying the amount to the frame rate of our window is ill-advised, many things can influence the frame rate and fluctuations would cause fluctuations in the speed of the fade-out. \n\nTo address this, Mousetrap offers [`Animation`](@ref), which acts as a *stable clock*, an object that outputs a value over a specified amount of time in a way that is independent of the frame rate.\n\nContinuing with our fade-out example, we first need to instance the widget we want to fade out, a `Button`. We then create an instance of `Animation`, which takes for its constructor the widget we want to animate, along with the target duration of the animation:\n\n```julia\nto_animate = Button(Label(\"Fade Out\"))\nanimation = Animation(to_animate, seconds(1))\n```\n\nBy tying the `Animation` to the widget we will target, Mousetrap will automatically preserve the animation while that widget is visible, as well as tie the animation clock to the render cycle of that specific widget, meaning the `Animation` will not play if the widget is not visible.\n\nTo start the animation, we call [`play!`](@ref). Of course, we have not yet implemented the behavior of the widgets' opacity decreasing. To do this, we register a callback using `Animation`'s [`on_tick!`](@ref), which requires a function with the signature:\n\n```julia\n(::Animation, value::Float64) -> Nothing\n```\n\nWhere `value` is the animations output value. By default, this will be in `[0, 1]`, though we can freely choose the upper and lower bound using [`set_lower!`](@ref) and [`set_upper!`](@ref). Once the animation is finished, the callback registered using [`on_done!`](@ref) is invoked.\n\nSince a widget's opacity is already in `[0, 1]`, we can use the animations value directly:\n\n```julia\nto_animate = Button(Label(\"Fade Out\"))\nanimation = Animation(to_animate, seconds(1))\non_tick!(animation, button) do self::Animation, value::Float64, target::Button\n    set_opacity!(target, 1 - value)\nend\n```\n\nWhere we used `1 - value` to invert the range, such that the widget starts fully opaque and decreases in opacity.\n\nWe can then start the animation using `play!`, for example, by clicking the button:\n\n![](../assets/animation_fade_out.webm)\n\n!!! details \"How to generate this Image\"\n    ```julia\n    using Mousetrap\n    main() do app::Application\n        window = Window(app)\n        set_title!(window, \"Mousetrap.jl\")\n\n        button = Button(Label(\"Fade Out\"))\n        aspect_frame = AspectFrame(1.0, button)\n        set_margin!(aspect_frame, 10)\n\n        animation = Animation(button, seconds(1))\n        on_tick!(animation, button) do self::Animation, value::Float64, target::Button\n            set_opacity!(target, 1 - value)\n        end\n        on_done!(animation, button) do self::Animation, target::Button\n            set_is_visible!(target, false)\n        end\n\n        connect_signal_clicked!(button, animation) do self::Button, animation::Animation\n            play!(animation)\n        end\n\n        set_child!(window, aspect_frame)\n        present!(window);\n    end\n    ```\n\nFor cyclical animations, we can use [`set_repeat_count!`](@ref) to specify the number of times the animation should loop, or `0` to loop infinitely. We can easily reverse an animation by setting [`set_is_reversed!`](@ref) to `true`.\n\nAttentive readers may remember that pre-made animations for `Stack` and `Revealer` also include widgets spinning or moving around the screen. So far, we have no good way of implementing motion like this. This is about to change.\n\n## TransformBin\n\nIn the chapter on rendering, we learned that we can apply a `GLTransform` to a `Shape`, a non-widget, in order to move that shape **without** changing its vertex data. [`TransformBin`](@ref) offers similar functionality to this, except it only applies to widgets.\n\n`TransformBin` is a widget that does not add any visual elements to its singular child. It furthermore always assumes the same size as its child. Instead, it offers a number of functions that allow us to apply a spatial transformation:\n\n| Function | Argument(s) | Effect |\n|----------------------|-----------|--------|\n| [`translate!`](@ref) | `Vector2f` | Move widget by number of pixels |\n| [`rotate!`](@ref) | `Angle` | Rotate around widgets' centroid |\n| [`scale!`](@ref) | `Number, Number` | Scale height and width by given factor |\n| [`skew!`](@ref) | `Number, Number` | [Skew](https://en.wikipedia.org/wiki/Shear_mapping) widget along x- and y-axis |\n| [`reset!`](@ref) | `(none)` | Reset transform to identity |\n\n!!! tip \"Rotate around a Point\"\n    To rotate a widget around a fixed point `p`, we can `translate!` such \n    that the widget's new center is at `p`, `rotate!`, then `translate!` back to the widget's initial position. \n\nThese functions are called on the `TransformBin` instance directly, we do not use a separate transform object. The arguments for these functions operate in absolute widget space, with `(0, 0)` being the top left corner of the `TransformBin`s size allocation, in pixels.\n\nFor example, to make a button spin one time when it is clicked, we can use `TransformBin` and `Animation` as follows:\n\n```julia\n# animation target\nto_animate = Button(Label(\"Spin\"))\n\n# transform bin\ntransform_bin = TransformBin()\nset_child!(transform_bin, to_animate)\n\n# animation\nanimation = Animation(to_animate, seconds(1))\non_tick!(animation, transform_bin) do self::Animation, value::Float64, transform_bin::TransformBin\n    # set the transform angle to value in [0, 360°]\n    reset!(transform_bin)\n    rotate!(transform_bin, degrees(value * 360))\nend\n\n# trigger animation when button is clicked\nconnect_signal_clicked!(to_animate, animation) do self::Button, animation::Animation\n    play!(animation)\nend\n```\n\n![](../assets/animation_spin.webm)\n\nNote that applying a transform using `TransformBin` does not change the size allocation of the widget, it only applies the effect visually, similarly to how a `GLTransform` is only applied to the rendered image, not the `Shape` itself.\n\nBy default, the function used to map the elapsed duration of the `Animation` to the output value of `on_tick!` is linear in shape (`f(x) = x`). Mousetrap offers additional functions with different shapes, allowing users to more easily implement animations that appear to speed up or slow down at certain points. Using [`set_timing_function!`](@ref), which takes a value of the enum [`AnimationTimingFunction`](@ref), we can choose from multiple presets. See its documentation for more information.\n\n---\n\n# Widget Themes & Style Classes\n\nMousetrap uses [Cascading Style Sheets (CSS)](https://developer.mozilla.org/en-US/docs/Web/CSS) to define the exact look of a widget. A UI theme is nothing more than a huge CSS file from which all widgets take information about how they should be rendered. \n\n!!! Warning \"CSS\"\n    The rest of this chapter will assume that readers are familiar with the basics of CSS. Readers are encouraged to consult [this documentation](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference) for CSS-related questions.\n\n## Applying CSS Properties to a Widget\n\nWe can define a CSS modifier class as a string, then compile that string using [`add_css!`](@ref), which makes that modifier class globally available:\n\n```julia\n# define modifier class `sharp-corners`\nadd_css!(\"\"\"\n.sharp-corners {\n   border-radius: 0%;\n}\n\"\"\")\n```\n\nWe can then apply this class to any widget using [`add_css_class!`](@ref), at which point the widgets' appearance will change accordingly. To remove the modifier, we call [`remove_css_class!`](@ref). A widget can have more than one modifier class. To list all applied CSS classes, we use [`get_css_classes`](@ref).\n\nFor a list of CSS properties supported by Mousetrap, see [here](https://docs.gtk.org/gtk4/css-properties.html).\n\nFor example, the following defines a `ToggleButton` that, when toggled, applies the following CSS class to both the `Window` (which is a Widget), and its `HeaderBar`:\n\n```julia\nusing Mousetrap\nadd_css!(\"\"\"\n.custom {\n    background-color: hotpink;\n    font-family: monospace;\n    border-radius: 0%;\n}\n\"\"\")\n\nmain() do app::Application\n\n    window = Window(app)\n    set_title!(window, \"Mousetrap.jl\")\n    \n    button = ToggleButton()\n    connect_signal_toggled!(button, window) do self::ToggleButton, window::Window\n        if get_is_active(self) \n            add_css_class!(window, \"custom\")\n            add_css_class!(get_header_bar(window), \"custom\")\n        else\n            remove_css_class!(window, \"custom\")\n            remove_css_class!(get_header_bar(window), \"custom\")\n        end\n    end\n    set_child!(window, button)\n    present!(window)\nend\n```\n\n![](../assets/css_style_pink_window.png)\n\nWhile a pink window is usually not recommended, making small adjustments to interactive widgets, or changing which font a widget uses, can be very useful when polishing our application's look and feel.\n\nFor a more practical example, after defining the following CSS modifier class:\n\n```css\n.monospaced {\n    font-family: monospace;\n}\n```\n\nWe can make an `Entry` or `TextView` use monospaced text by calling `add_css_class!(widget, \"monospaced\")` on each widget instance.\n\n---\n\n## Changing a Widgets Color\n\nThe following implements `set_accent_color!`, which is not part of Mousetrap. `set_accent_color!` takes a widget, one of the below constants, as well as a boolean indicating whether the window should be opaque, as its arguments. When applied to a widget, this function changes that widgets color to one of the 5 pre-defined UI colors, such that their look fits well with the default UI theme:\n\n```julia\nusing Mousetrap\n\n# define widget colors\nconst WidgetColor = String\nconst WIDGET_COLOR_DEFAULT = \"default\"\nconst WIDGET_COLOR_ACCENT = \"accent\"\nconst WIDGET_COLOR_SUCCESS = \"success\"\nconst WIDGET_COLOR_WARNING = \"warning\"\nconst WIDGET_COLOR_ERROR = \"error\"\n\n# create CSS classes for all of the widget colors\nfor name in [WIDGET_COLOR_DEFAULT, WIDGET_COLOR_ACCENT, WIDGET_COLOR_SUCCESS, WIDGET_COLOR_WARNING, WIDGET_COLOR_ERROR]\n    # compile CSS and append it to the global CSS style provider state\n    add_css!(\"\"\"\n    $name:not(.opaque) {\n        background-color: @$(name)_fg_color;\n    }\n    .$name.opaque {\n        background-color: @$(name)_bg_color;\n        color: @$(name)_fg_color;\n    }\n    \"\"\")\nend\n\n# function to set the accent color of a widget\nfunction set_accent_color!(widget::Widget, color, opaque = true)\n    if !(color in [WIDGET_COLOR_DEFAULT, WIDGET_COLOR_ACCENT, WIDGET_COLOR_SUCCESS, WIDGET_COLOR_WARNING, WIDGET_COLOR_ERROR])\n        log_critical(\"In set_color!: Color ID `\" * color * \"` is not supported\")\n    end    \n    add_css_class!(widget, color)\n    if opaque\n        add_css_class!(widget, \"opaque\")\n    end\nend\n```\n\nUsers are encouraged to just copy the above code into their own project, for `set_accent_color!` to become available.\n\nWe can use this function like so:\n\n```julia\n# widget factory\ncreate_widget() = Button(Label(\"TEST\"))\n\n# create column view\ncolumn_view = ColumnView()\n\n# column 1: whether `opaque` was set to true\ncolumn = push_back_column!(column_view, \" \")\nset_widget_at!(column_view, column, 1, Label(\"<small>!opaque</small>\"))\nset_widget_at!(column_view, column, 2, Label(\"<small>opaque</small>\"))\n\nfor color in [\n    WIDGET_COLOR_DEFAULT, # column 2: default look of a widget\n    WIDGET_COLOR_ACCENT,  # column 3: accented, usually blue\n    WIDGET_COLOR_SUCCESS, # column 4: marked successful, usually green\n    WIDGET_COLOR_WARNING, # column 5: marked as warning, usually yellow\n    WIDGET_COLOR_ERROR]   # column 6: marked as destructive action, usually red\n    column = push_back_column!(column_view, color)\n    \n    # row 1: accented widget, not opaque\n    widget = create_widget()\n    set_accent_color!(widget, color, false)\n    set_widget_at!(column_view, column, 1, widget)\n\n    # row 2: accented widget, opaque\n    widget = create_widget()\n    set_accent_color!(widget, color, true)\n    set_widget_at!(column_view, column, 2, widget)\nend\n```\n\nHere, we created a column view that shows all permutations of the arguments of `set_accent_color!`. We defined `create_widget() = Button(Label(\"TEST\"))`, therefore each cell will have a button with a label:\n\n![](../assets/css_style_column_view_buttons.png)\n\n`set_accent_color!` allows us to change the color of each button. It can be applied to any widget, however:\n\n![](../assets/css_style_column_view_headerbar.png)\n\nWhere we use a `HeaderBar` instead of `Button`. CSS modifiers can be applied to any widget, even windows.\n\n## Changing the Themes Palette\n\nTo change a color used by the global theme, we need to redefine one of the themes [color constants](https://docs.gtk.org/gtk4/css-properties.html#colors) using CSS.\n\nFor example, the global accent color, blue by default, can be redefined to any other color using the following function, which is also not part of Mousetrap:\n\n```julia\nset_accent_color!(color::RGBA) = add_css!(\"@define-color accent_bg_color $(serialize(color));\")\n```\n\nFor example, calling `set_accent_color!(RGBA(1, 0, 1, 1))` changes the accent color to magenta, which is applied to all widgets globally:\n\n![](../assets/css_style_magenta_accent.png)\n\nFor names of palette colors other than `accent_bg_color`, see [here](https://gitlab.gnome.org/GNOME/libadwaita/-/blob/main/src/stylesheet/_colors.scss?ref_type=heads).\n\n## CSS Animations\n\n`Animation` offers an in-engine way to do animations, which is usually preferred. However, [CSS animations](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_animations/Using_CSS_animations) are supported as well, and are defined as they would be in pure CSS:\n\n```julia\n# define CSS animation and modifier\nadd_css!(\"\"\"\n@keyframes spin-animation {\n    0%   { transform: rotate(0turn)   scale(1); }\n    50%  { transform: rotate(0.5turn) scale(2); }\n    100% { transform: rotate(1turn)   scale(1); }\n}\n\n.spinning {\n    animation: spin-animation;\n    animation-duration: 1s;\n    animation-iteration-count: infinite;\n    animation-timing-function: ease-in-out;\n}\n\"\"\")\n\nmain() do app::Application\n    window = Window(app)\n    button = Button()\n\n    # apply modifier, this sets the animation-related properties\n    add_css_class!(button, \"spinning\")\n\n    set_child!(window, AspectFrame(1.0, button))\n    present!(window)\nend\n```\n\n![](../assets/css_style_animation_spin.webm)\n\nWhile we could create this animation using `Mousetrap.Animation` and `TransformBin`, using CSS means that we do not have to instance these two objects for every widget we want to animate, we can instead just apply the CSS modifier class to any instance. In return, the CSS-based animation cannot depend on any external variables and is thus only suited for animations that remain the same each time they are played. \n"
  },
  {
    "path": "docs/src/01_manual/11_app_distribution.md",
    "content": "# Chapter 11: App Distribution\n\nIn this chapter, we will learn:\n+ How to load and store assets\n+ How to bundle our app for distribution\n+ How to install our app on a user's machine\n\n---\n\n!!! compat\n    These features are not yet implemented, this section is incomplete. As of version 0.3.0, there is no unified way to bundle and distribute a Mousetrap app. See [here](https://github.com/users/Clemapfel/projects/2?pane=issue&itemId=33978204#) for more information.\n"
  },
  {
    "path": "docs/src/01_manual/12_opengl_integration.md",
    "content": "# Chapter 12: OpenGL Integration & Makie Support\n\nIn this chapter, we will learn:\n+ How to use `GLArea` to allow foreign OpenGL libraries to render to a Mousetrap widget\n+ How to display a `GLMakie` plot in a Mousetrap window\n\n!!! compat \n    Features from this chapter are only available in Mousetrap `v0.3.0` or newer.\n\n---\n\n# Introduction: Use Case\n\nIf we want to render shapes or images using `OpenGL`, Mousetraps `RenderArea` offers a convenient interface for this. In some applications, we may want to use [OpenGL](https://github.com/JuliaGL/ModernGL.jl) itself. One common use case for this is the integration of another, separate library that is unrelated to Mousetrap and shares no interface with it, except for the fact that both use OpenGL for rendering. \n\nFor situations like this, Mousetrap offers a low-level, generic widget, [`GLArea`](@ref), which provides an OpenGL context and render surface, thus allowing OpenGL-based graphics to be displayed inside a Mousetrap application.\n\n# GLArea\n\n`GLarea` is a very simple widget, it acts exactly as `RenderArea` in terms of appearance and behavior, though there is no in-Mousetrap way to display graphics.\n\nWe create it like so:\n\n```julia\narea = GLArea()\n```\n\nAfter which it can be used just like any other widget, meaning it has a size request, opacity, adheres to CSS, etc.\n\n`GLArea` holds its own OpenGL context, which is **initialized after `realize` has been emitted**. This is important to understand, we cannot call any OpenGL code before the widget is fully realized and displayed on screen. To delay execution, we should connect to one of `GLArea`s signals.\n\n!!! danger \"OpenGL Context is only available after Realization\"\n    To reiterate, Mousetrap will only be able to provide an OpenGL context after `GLArea` is realized, which usually happens when the window it is contained within is shown for the first time. If we try to interact with the context before this point, a critical log message will be printed and the OpenGL operation will fail.\n\n## Signals\n\n`GLArea` has two unique signals, `render` and `resize`. Alongside these, in most situations, we will also want to connect to the `realize` signal, which all widgets share.\n\nWe recognize `resize` from `RenderArea`. Just as then, it requires a signal handler with the signature\n\n```julia\n(::GLArea, width::Integer, height::Integer, [::Data_t]) -> Nothing\n```\n\n`resize` is emitted anytime `GLArea` changes size, according to its widget properties. Crucially, this also means its [default framebuffer](https://www.khronos.org/opengl/wiki/Default_Framebuffer) and [viewport](https://www.khronos.org/opengl/wiki/GLAPI/glViewport) are resized accordingly. **We cannot change either size ourselves**, Mousetrap's back-end handles this for us. \n\nFor each `resize` invocation, we can assume that the default frame buffer has a size of `width * height` pixels.\n\nSignal `render` is usually emitted once per frame, whenever the widget is drawn on screen. This signal requires a callback with the signature\n\n```julia\n(::GLArea, gdk_gl_context::Ptr{Cvoid}, [::Data_t]) -> Bool\n```\n\nWhere `gdk_gl_context` is a C-pointer to the internally held [OpenGL context](https://docs.gtk.org/gdk4/class.GLContext.html). We usually do not have to interact with this context, though any `render` signal handler still requires including this argument to conform to the above signature.\n\nWe note that signal `render` requires its callback to return a boolean. This return value notifies Mousetrap whether the `GLArea`s framebuffer was updated during the draw step. If we have modified the image we want to appear on screen, we should return `true`, if no drawing has taken place and the `GLArea` does not need to be updated, `false` should be returned.\n\nIn the signal handler of `render`, we should make sure to bind the current `GLArea`s OpenGL context as the active one using [`make_current`](@ref) (see below). This is to make sure that we are rendering to the buffer associated with the specific `GLArea` emitting the signal, not another instance of the same widget type, or a completely separate OpenGL context from another library.\n\n!!! warning \"GLAreas do not share a Context\"\n    Unlike `RenderArea`, which all share a singular global OpenGL context, each `GLArea` instance has its own OpenGL context, meaning objects cannot be transmitted between `GLArea`s, and, if one is unrealized, all objects associated with that context will be inaccessible (but not freed).\n\nFor performance optimization reasons, `GLArea` will only be drawn when necessary, as is the case for all objects subtyping `Widget`. We can manually request `GLArea` to update the frame after this one, by calling [`queue_render`](@ref).\n\nGeneral usage of `GLarea` with another OpenGL-based library will have the following structure:\n\n```julia\nglarea = GLArea()\n\n# realize callback\nconnect_signal_realize!(glarea) do self::GLArea\n    make_current(self)\n\n    # do initialization here, only after this callback was invoked is the \n    # internal OpenGL context fully initialized and ready to be used\n\n    queue_render(self)\n    return nothing\nend\n\n# render callback\nconnect_signal_render!(glarea) do self::GLArea, _::Ptr{Cvoid}\n    make_current(self)\n\n    # do opengl renderloop here\n\n    return true\nend\n\n# resize callback\nconnect_signal_resize!(glarea) do self::GLArea, width, height\n    make_current(self)\n\n    # handle new size here\n\n    queue_render(self)\n    return nothing\nend\n\n# destroy callback\nconnect_signal_destroy!(glarea) do self::GLArea\n    make_current(self)\n\n    # do shutdown here\n\n    return nothing\nend\n```\n\nWe see that we should make sure to bind the context using `make_current` before doing any OpenGL-related work and to manually request a redraw after the area was initialized or resized.\n\n# Example: GLMakie \n\n[`GLMakie`](https://docs.makie.org/stable/explanations/backends/glmakie/index.html) is one backend for the hugely popular [`Makie`](https://github.com/MakieOrg/Makie.jl) plotting library. As its name suggests, `GLMakie` uses OpenGL for rendering, which means it is possible to allow Makie to render to a Mousetrap `GLArea`, allowing us to integrate plots and graphics into our Mousetrap application.\n\nGiven here will be a minimum working example that displays a scatter plot inside a `Mousetrap.Window` by creating a `GLArea`-based widget that can be used to create a `GLMakie.Screen`.\n\nNote that this example is incomplete and does not support all of Makies features. One conflict that Mousetrap users will have to resolve for themselves is how to handle input events. In the following, all of Makies input-related behavior was suppressed, making it so users will have to handle input events and window behavior using only the Mousetrap event model.\n\n!!! details \"Note from the Author: Makie Interface\"\n    The example here most likely does not implement enough of Makies interface to be fully ready for usage. Most of the code was based on [`Gtk4GLMakie`](https://github.com/JuliaGtk/Gtk4Makie.jl), which itself is still rough. I'm not that familiar with Makie in general usage, and fully implementing an interface requires knowledge of Makies internals on top of that. \n\n    If you or your project is very familiar with Makie and would like to improve this code, feel free to [open a PR](https://github.com/Clemapfel/Mousetrap.jl/pulls) that modifies [`test/makie_test.jl`](https://github.com/Clemapfel/Mousetrap.jl/blob/main/test/makie_test.jl), which ideally will become its own Julia package in the future, similar to `Gtk4GLMakie`. Any contributor will be credited as an author. Thank you for your consideration.\n\n    C.\n\n!!! details \"MousetrapMakie: Click to expand\"\n    ```julia\n    \"\"\"\n    Minimum working example showing how to display a GLMakie plot using Mousetrap `GLArea`\n    \"\"\"\n    module MousetrapMakie\n\n        export GLMakieArea, create_glmakie_screen\n\n        using Mousetrap\n        using ModernGL, GLMakie, Colors, GeometryBasics, ShaderAbstractions\n        using GLMakie: empty_postprocessor, fxaa_postprocessor, OIT_postprocessor, to_screen_postprocessor\n        using GLMakie.GLAbstraction\n        using GLMakie.Makie\n\n        \"\"\"\n        ## GLMakieArea <: Widget\n        `GLArea` wrapper that automatically connects all necessary callbacks to be used as a GLMakie render target. \n\n        Use `create_glmakie_screen` to initialize a screen you can render to using Makie from this widget. Note that `create_glmakie_screen` needs to be \n        called **after** `GLMakieArea` has been realized, as only then will the internal OpenGL context be available. See the example below.\n\n        ## Constructors\n        `GLMakieArea()`\n\n        ## Signals\n        (no unique signals)\n\n        ## Fields\n        (no public fields)\n\n        ## Example\n\n            using Mousetrap, MousetrapMakie\n            main() do app::Application\n                window = Window(app)\n                canvas = GLMakieArea()\n                set_size_request!(canvas, Vector2f(200, 200))\n                set_child!(window, canvas)\n            \n                # use optional ref to delay screen allocation after `realize`\n                screen = Ref{Union{Nothing, GLMakie.Screen{GLMakieArea}}}(nothing)\n                connect_signal_realize!(canvas) do self\n                    screen[] = create_glmakie_screen(canvas)\n                    display(screen[], scatter(1:4))\n                    return nothing\n                end\n                present!(window)\n            end\n        \"\"\"\n        mutable struct GLMakieArea <: Widget\n\n            glarea::GLArea              # wrapped native widget\n            framebuffer_id::Ref{Int}    # set by render callback, used in MousetrapMakie.create_glmakie_screen\n            framebuffer_size::Vector2i  # set by resize callback, used in GLMakie.framebuffer_size\n\n            function GLMakieArea()\n                glarea = GLArea()\n                set_auto_render!(glarea, false) # should `render` be emitted everytime the widget is drawn\n                connect_signal_render!(on_makie_area_render, glarea)\n                connect_signal_resize!(on_makie_area_resize, glarea)\n                return new(glarea, Ref{Int}(0), Vector2i(0, 0))\n            end\n        end\n        Mousetrap.get_top_level_widget(x::GLMakieArea) = x.glarea\n\n        # maps hash(GLMakieArea) to GLMakie.Screen\n        const screens = Dict{UInt64, GLMakie.Screen}()\n\n        # maps hash(GLMakieArea) to Scene, used in `on_makie_area_resize`\n        const scenes = Dict{UInt64, GLMakie.Scene}()\n\n        # render callback: if screen is open, render frame to `GLMakieArea`s OpenGL context\n        function on_makie_area_render(self, context)\n            key = Base.hash(self)\n            if haskey(screens, key)\n                screen = screens[key]\n                if !isopen(screen) return false end\n                screen.render_tick[] = nothing\n                glarea = screen.glscreen\n                glarea.framebuffer_id[] = glGetIntegerv(GL_FRAMEBUFFER_BINDING)\n                GLMakie.render_frame(screen) \n            end\n            return true\n        end\n\n        # resize callback: update framebuffer size, necessary for `GLMakie.framebuffer_size`\n        function on_makie_area_resize(self, w, h)\n            key = Base.hash(self)\n            if haskey(screens, key)\n                screen = screens[key]\n                glarea = screen.glscreen\n                glarea.framebuffer_size.x = w\n                glarea.framebuffer_size.y = h\n                queue_render(glarea.glarea)\n            end\n\n            if haskey(scenes, key)\n                scene = scenes[key]\n                scene.events.window_area[] = Recti(0, 0, glarea.framebuffer_size.x, glarea.framebuffer_size.y)\n                scene.events.window_dpi[] = Mousetrap.calculate_monitor_dpi(glarea)\n            end\n            return nothing\n        end\n\n        # resolution of `GLMakieArea` OpenGL framebuffer\n        GLMakie.framebuffer_size(self::GLMakieArea) = (self.framebuffer_size.x, self.framebuffer_size.y)\n\n        # forward retina scale factor from GTK4 back-end\n        GLMakie.retina_scaling_factor(w::GLMakieArea) = Mousetrap.get_scale_factor(w)\n\n        # resolution of `GLMakieArea` widget itself`\n        function GLMakie.window_size(w::GLMakieArea)\n            size = get_natural_size(w)\n            size.x = size.x * GLMakie.retina_scaling_factor(w)\n            size.y = size.y * GLMakie.retina_scaling_factor(w)\n            return (size.x, size.y)\n        end\n\n        # calculate screen size and dpi\n        function Makie.window_area(scene::Scene, screen::GLMakie.Screen{GLMakieArea})\n            glarea = screen.glscreen\n            scenes[hash(glarea)] = scene\n        end\n\n        # resize request by makie will be ignored\n        function GLMakie.resize_native!(native::GLMakieArea, resolution...)\n            # noop\n        end\n\n        # bind `GLMakieArea` OpenGL context\n        ShaderAbstractions.native_switch_context!(a::GLMakieArea) = make_current(a.glarea)\n\n        # check if `GLMakieArea` OpenGL context is still valid, it is while `GLMakieArea` widget stays realized\n        ShaderAbstractions.native_context_alive(x::GLMakieArea) = get_is_realized(x)\n\n        # destruction callback ignored, lifetime is managed by Mousetrap instead\n        function GLMakie.destroy!(w::GLMakieArea)\n            # noop\n        end\n\n        # check if canvas is still realized\n        GLMakie.was_destroyed(window::GLMakieArea) = !get_is_realized(window)\n\n        # check if canvas should signal it is open\n        Base.isopen(w::GLMakieArea) = !GLMakie.was_destroyed(w)\n\n        # react to makie screen visibility request\n        GLMakie.set_screen_visibility!(screen::GLMakieArea, bool) = bool ? show(screen.glarea) : hide!(screen.glarea)\n\n        # apply glmakie config\n        function GLMakie.apply_config!(screen::GLMakie.Screen{GLMakieArea}, config::GLMakie.ScreenConfig; start_renderloop=true) \n            @warn \"In MousetrapMakie: GLMakie.apply_config!: This feature is not yet implemented, ignoring config\"\n            # cf https://github.com/JuliaGtk/Gtk4Makie.jl/blob/main/src/screen.jl#L111\n            return screen\n        end\n\n        # screenshot framebuffer\n        function Makie.colorbuffer(screen::GLMakie.Screen{GLMakieArea}, format::Makie.ImageStorageFormat = Makie.JuliaNative)\n            @warn \"In MousetrapMakie: GLMakie.colorbuffer: This feature is not yet implemented, returning framecache\"\n            # cf https://github.com/JuliaGtk/Gtk4Makie.jl/blob/main/src/screen.jl#L147\n            return screen.framecache\n        end\n\n        # ignore makie event model, use Mousetrap event controllers instead\n        Makie.window_open(::Scene, ::GLMakieArea) = nothing\n        Makie.disconnect!(::GLMakieArea, f) = nothing\n        GLMakie.pollevents(::GLMakie.Screen{GLMakieArea}) = nothing\n        Makie.mouse_buttons(::Scene, ::GLMakieArea) = nothing\n        Makie.keyboard_buttons(::Scene, ::GLMakieArea) = nothing\n        Makie.dropped_files(::Scene, ::GLMakieArea) = nothing\n        Makie.unicode_input(::Scene, ::GLMakieArea) = nothing\n        Makie.mouse_position(::Scene, ::GLMakie.Screen{GLMakieArea}) = nothing\n        Makie.scroll(::Scene, ::GLMakieArea) = nothing\n        Makie.hasfocus(::Scene, ::GLMakieArea) = nothing\n        Makie.entered_window(::Scene, ::GLMakieArea) = nothing\n\n        \"\"\"\n        \n            create_gl_makie_screen(::GLMakieArea; screen_config...) -> GLMakie.Screen{GLMakieArea}\n        \n        For a `GLMakieArea`, create a `GLMakie.Screen` that can be used to display makie graphics\n        \"\"\"\n        function create_glmakie_screen(area::GLMakieArea; screen_config...)\n\n            if !get_is_realized(area) \n                log_critical(\"MousetrapMakie\", \"In MousetrapMakie.create_glmakie_screen: GLMakieArea is not yet realized, it's internal OpenGL context cannot yet be accessed\")\n            end\n\n            config = Makie.merge_screen_config(GLMakie.ScreenConfig, screen_config)\n\n            set_is_visible!(area, config.visible)\n            set_expand!(area, true)\n\n            # quote from https://github.com/JuliaGtk/Gtk4Makie.jl/blob/main/src/screen.jl#L342\n            shader_cache = GLAbstraction.ShaderCache(area)\n            ShaderAbstractions.switch_context!(area)\n            fb = GLMakie.GLFramebuffer((1, 1)) # resized on GLMakieArea realization later\n\n            postprocessors = [\n                config.ssao ? ssao_postprocessor(fb, shader_cache) : empty_postprocessor(),\n                OIT_postprocessor(fb, shader_cache),\n                config.fxaa ? fxaa_postprocessor(fb, shader_cache) : empty_postprocessor(),\n                to_screen_postprocessor(fb, shader_cache, area.framebuffer_id)\n            ]\n\n            screen = GLMakie.Screen(\n                area, shader_cache, fb,\n                config, false,\n                nothing,\n                Dict{WeakRef, GLMakie.ScreenID}(),\n                GLMakie.ScreenArea[],\n                Tuple{GLMakie.ZIndex, GLMakie.ScreenID, GLMakie.RenderObject}[],\n                postprocessors,\n                Dict{UInt64, GLMakie.RenderObject}(),\n                Dict{UInt32, Makie.AbstractPlot}(),\n                false,\n            )\n            # end quote\n\n            hash = Base.hash(area.glarea)\n            screens[hash] = screen\n            \n            set_tick_callback!(area.glarea) do clock::FrameClock\n                if GLMakie.requires_update(screen)\n                    queue_render(area.glarea)\n                end\n\n                if GLMakie.was_destroyed(area)\n                    return TICK_CALLBACK_RESULT_DISCONTINUE\n                else\n                    return TICK_CALLBACK_RESULT_CONTINUE\n                end\n            end\n            return screen\n        end\n    end\n    ```\n\nWe can test the above using:\n\n```julia\nusing Mousetrap, .MousetrapMakie, GLMakie\nmain() do app::Application\n    window = Window(app)\n    set_title!(window, \"Mousetrap x Makie\")\n\n    canvas = GLMakieArea()\n    set_size_request!(canvas, Vector2f(200, 200))\n    set_child!(window, canvas)\n\n    # use optional ref to delay screen allocation after `realize`\n    screen = Ref{Union{Nothing, GLMakie.Screen{GLMakieArea}}}(nothing)\n    connect_signal_realize!(canvas) do self\n\n        # initialize GLMakie.Screen\n        screen[] = create_glmakie_screen(canvas)\n\n        # use screen to display plot\n        display(screen[], scatter(rand(123)))\n        return nothing\n    end\n\n    present!(window)\nend\n```\n\n![](../assets/makie_scatter.png)\n\nWhere we delayed the call to `create_gl_makie_screen` to *after* `realize` was emitted for reasons discussed [earlier in this chapter](#glarea). Since we still need to reference the created screen outside the `realize` signal handler, we used the **optional pattern**:\n\n```julia\noptional = Ref{Union{Nothing, T}}(nothing)\n```\n\nWhich initializes a reference with `nothing`, such that the reference value can later be assigned with a value of `T`, `GLMakie:Screen{GLMakieArea}` in our example above. After `realize` was emitted, we can access the screen using `screen[]`.\n"
  },
  {
    "path": "docs/src/index.md",
    "content": "# Mousetrap\n\nWelcome to the documentation of Mousetrap.jl, a GUI engine for Julia. \nMousetrap was created and designed by [C. Cords](https://clemens-cords.com).\n\nThis page contains a [manual and tutorial](#manual), as well as an [index](#index) of all [functions](./02_library/functions.md), [classes](./02_library/classes.md), and [enums](./02_library/enums.md).\n\nTo download and install mousetrap, follow the instructions on the [official GitHub page](https://github.com/Clemapfel/mousetrap.jl#installation).\n\nMousetrap.jl, the non-Julia components of mousetrap, this documentation, and all its original assets, are licensed under [lGPL3.0](https://www.gnu.org/licenses/lgpl-3.0.en.html#license-text), meaning they can be used in both free, open-source, as well as for-profit, proprietary projects.\n\nFor frequently asked questions, see [here](#FAQ).\n\nUse the navigation area on the left side of this page or the table of contents below to navigate to the appropriate chapter of this manual:\n\n## Manual\n\n```@contents\nPages = [\n    \"01_manual/01_installation.md\"\n    \"01_manual/02_signals.md\"\n    \"01_manual/03_actions.md\"\n    \"01_manual/04_widgets.md\"\n    \"01_manual/05_event_handling.md\"\n    \"01_manual/06_image.md\"\n    \"01_manual/07_os_interface.md\"\n    \"01_manual/08_menus.md\"\n    \"01_manual/09_native_rendering.md\"\n    \"01_manual/10_theme_customization.md\"\n    \"01_manual/11_app_distribution.md\"\n    \"01_manual/12_opengl_integration.md\"\n]\n\nDepth = 5\n```\n\n---\n\n## Index\n\n+ [index of classes](./02_library/classes.md)\n+ [index of functions](./02_library/functions.md)\n+ [index of enums](./02_library/enums.md)\n\n---\n\n## FAQ\n\n### Why is there a C++ Component at all?\n\nThe C++ version of mousetrap was originally created for an unrelated commercial, closed-source project in 2022. When the project failed due to funding issues in 2023, I decided that, instead of throwing away all my C++ work, I would instead create a Julia wrapper around it, so it can at least contribute to the Julia ecosysem. Even though Julia-Mousetrap is the face of the project now, C++-Mousetrap existed and was finished *before* Julia-Mousetrap was ever conceived.\n\nA less valid reason is that mousetrap, in order to extend the GObject type system, [makes extensive use of C-macros](https://github.com/Clemapfel/mousetrap/blob/main/include/mousetrap/signal_component.hpp#L24), which are quite hard to emulate in Julia, as there is no way to `ccall` a macro, to my knowledge.\n\n### What is the difference between mousetrap and GTK4.jl?\n\nMousetrap is unaffiliated with [GTK.jl](https://github.com/JuliaGraphics/Gtk.jl) and [GTK4.jl](https://github.com/JuliaGtk/Gtk4.jl). The only connection these projects share is that both use the GTK4 C library as their basis. Mousetrap is not endorsed by [GNOME](https://gnome.org) and has no connection to that organization or any of its contributors.\n\n### What advantages does mousetrap have over pure GTK4?\n\nWhile based on GTK4 and usually calling native GTK4 under the hood, mousetraps interface, that is, the actual architecture of the library, including syntax and names, was reworked from the ground up. Heavy editorializing has taken place, renaming or completely removing certain parts of GTK4 in an effort to make mousetrap more friendly to newcomers and people with no GUI experience, easier to use, and less susceptible to developer error.\n\nFurthermore, mousetrap contains an all-new OpenGL-based rendering engine, which aims to replace the cairo component of native GTK4 to allow for faster, more easily integrated native rendering.\n\n### What advantages does pure GTK4 have over mousetrap?\n\nSpeaking about the GTK4 C library specifically, not GTK4.jl, GTK4 is much bigger and has many features that went unused in mousetrap, or, if used, were made opaque such that a user of mousetrap cannot interact with these features:\n\n+ Removed Modules: [GDK](https://docs.gtk.org/gdk4/), [ATK](https://gitlab.gnome.org/GNOME/atk), [gobject](https://docs.gtk.org/gobject/), [GLib](https://docs.gtk.org/glib/), [adwaita](https://gnome.pages.gitlab.gnome.org/libadwaita/doc/1.3/)\n+ Widgets with no mousetrap equivalent: `GtkListBox`, `PasswordEntry`, `GtkTextView`, `GtkSourceView`, `AdwAboutWindow`\n+ Removed Features: Mnemonics, Stateful Actions, [GtkBuilder Interface](https://docs.gtk.org/gtk4/class.Builder.html), `GTK_DEBUG`, `GtkInspector`\n\nFurthermore, any classes marked as deprecated in GKT4.10, such as `GtkTreeView`, where removed completely.\n\nWhen possible, the adwaita version of a widget was preferred over the native GTK4 version, for example, using `AdwWindow` instead of `GtkWindow`, or `AdwMessageDialog` over `GtkAlertDialog`.\n\nJulia does not use a [build system such as meson](https://mesonbuild.com/) and is thus incompatible with distribution through [flatpak](https://flatpak.org/) or similar services.\n\n### How do I ship my app?\n\nJuliaComputing and many more contributors are currently working on [`PackageCompiler`](https://github.com/JuliaLang/PackageCompiler.jl), which is supposed to compile a Julia package into a stand-alone binary. Compatibility of mousetrap with `PackageCompiler` remains untested and may be impossible until further work has been put into either `PackageCompiler`, or mousetrap itself. [There is evidence](https://www.reddit.com/r/Julia/comments/14kfyx7/comment/jpuofyg/) that static compilation is one of Julias next big goals. Ideally, when static compilation is working 100% of the time, mousetrap will be polished enough to be considered fully stable and easily distributable, therefore making it usable in production.\n\nUntil then, the only way to ship a stand-alone Julia app is to bundle the entire Julia runtime along with the app-specific Julia code and resources, which will usually be a folder with a size of 2 GB or more. Alternatively, developers have to force end users to [install Julia on their machine globally](https://github.com/JuliaLang/juliaup), after which launching the app is as simple as calling (`julia main.jl`) from a shell script.\n"
  },
  {
    "path": "jll/build_tarballs.jl",
    "content": "# Note that this script can accept some limited command-line arguments, run\n# `julia build_tarballs.jl --help` to see a usage message.\nusing BinaryBuilder, Pkg\n\nname = \"libmousetrap\"\nversion = v\"0.3.0\"\n\n# Collection of sources required to complete build\nsources = [\n    GitSource(\"https://github.com/Clemapfel/mousetrap.git\", \"ffa28d0bd569118320a6bb063286edfb35fc0429\"),\n    GitSource(\"https://github.com/Clemapfel/mousetrap_julia_binding.git\", \"6b838ea238e118d694ffc1b3a1e0225441c8cfb3\")\n]\n\n# Bash recipe for building across all platforms\nscript = raw\"\"\"\ncd $WORKSPACE/srcdir\necho -e \"[binaries]\\ncmake='/usr/bin/cmake'\" >> cmake_toolchain_patch.ini\ncd mousetrap\nmkdir ${prefix}/share/licenses/mousetrap\ncp LICENSE ${prefix}/share/licenses/mousetrap/LICENSE\nmeson setup build --cross-file=$MESON_TARGET_TOOLCHAIN --cross-file=../cmake_toolchain_patch.ini\nmeson install -C build\ncd ../mousetrap_julia_binding\nmeson setup build --cross-file=$MESON_TARGET_TOOLCHAIN --cross-file=../cmake_toolchain_patch.ini -DJulia_INCLUDE_DIRS=$prefix/include/julia\nmeson install -C build\ncd ..\nrm cmake_toolchain_patch.ini\n\"\"\"\n\n# These are the platforms we will build for by default, unless further\n# platforms are passed in on the command line\nplatforms = filter(p -> nbits(p) == 64, supported_platforms())\n\n# The products that we will ensure are always built\nproducts = [\n    LibraryProduct(\"libmousetrap\", :mousetrap),\n    LibraryProduct(\"libmousetrap_julia_binding\", :mousetrap_julia_binding),\n    FileProduct(\"\")\n]\n\nx11_platforms = filter(p -> Sys.islinux(p) || Sys.isfreebsd(p), platforms)\n# Dependencies that must be installed before this package can be built\ndependencies = [\n    Dependency(\"GLEW_jll\")\n    Dependency(\"GLU_jll\"; platforms = x11_platforms)\n    Dependency(\"GTK4_jll\")\n    Dependency(\"libadwaita_jll\")\n    Dependency(\"OpenGLMathematics_jll\")\n    Dependency(\"libcxxwrap_julia_jll\")\n    BuildDependency(\"libjulia_jll\")\n    BuildDependency(\"Xorg_xorgproto_jll\"; platforms = x11_platforms)\n]\n\n# Build the tarballs, and possibly a `build.jl` as well.\nbuild_tarballs(ARGS, name, version, sources, script, platforms, products, dependencies; julia_compat=\"1.6\", preferred_gcc_version = v\"12.1.0\")\n"
  },
  {
    "path": "jll/build_tarballs.jl.in",
    "content": "# Note that this script can accept some limited command-line arguments, run\n# `julia build_tarballs.jl --help` to see a usage message.\nusing BinaryBuilder, Pkg\n\nname = \"libmousetrap\"\nversion = v\"@VERSION@\"\n\n# Collection of sources required to complete build\nsources = [\n    GitSource(\"https://github.com/Clemapfel/mousetrap.git\", \"@MOUSETRAP_COMMIT@\"),\n    GitSource(\"https://github.com/Clemapfel/mousetrap_julia_binding.git\", \"@MOUSETRAP_JULIA_BINDING_COMMIT@\")\n]\n\n# Bash recipe for building across all platforms\nscript = raw\"\"\"\ncd $WORKSPACE/srcdir\necho -e \"[binaries]\\ncmake='/usr/bin/cmake'\" >> cmake_toolchain_patch.ini\ncd mousetrap\nmkdir ${prefix}/share/licenses/mousetrap\ncp LICENSE ${prefix}/share/licenses/mousetrap/LICENSE\nmeson setup build --cross-file=$MESON_TARGET_TOOLCHAIN --cross-file=../cmake_toolchain_patch.ini\nmeson install -C build\ncd ../mousetrap_julia_binding\nmeson setup build --cross-file=$MESON_TARGET_TOOLCHAIN --cross-file=../cmake_toolchain_patch.ini -DJulia_INCLUDE_DIRS=$prefix/include/julia\nmeson install -C build\ncd ..\nrm cmake_toolchain_patch.ini\n\"\"\"\n\n# These are the platforms we will build for by default, unless further\n# platforms are passed in on the command line\nplatforms = filter(p -> nbits(p) == 64, supported_platforms())\n\n# The products that we will ensure are always built\nproducts = [\n    LibraryProduct(\"libmousetrap\", :mousetrap),\n    LibraryProduct(\"libmousetrap_julia_binding\", :mousetrap_julia_binding)\n]\n\nx11_platforms = filter(p -> Sys.islinux(p) || Sys.isfreebsd(p), platforms)\n# Dependencies that must be installed before this package can be built\ndependencies = [\n    Dependency(\"GLEW_jll\")\n    Dependency(\"GLU_jll\"; platforms = x11_platforms)\n    Dependency(\"GTK4_jll\")\n    Dependency(\"libadwaita_jll\")\n    Dependency(\"OpenGLMathematics_jll\")\n    Dependency(\"libcxxwrap_julia_jll\")\n    BuildDependency(\"libjulia_jll\")\n    BuildDependency(\"Xorg_xorgproto_jll\"; platforms = x11_platforms)\n]\n\n# Build the tarballs, and possibly a `build.jl` as well.\nbuild_tarballs(ARGS, name, version, sources, script, platforms, products, dependencies; julia_compat=\"1.6\", preferred_gcc_version = v\"12.1.0\")\n"
  },
  {
    "path": "jll/deploy.jl",
    "content": "#throw(AssertionError(\"In Mousetrap/jll/deploy.jl: This script is meant for internal use only and should not be tampered with by the general public. Do not run this file.\"))\n\nconst VERSION = \"0.3.0\"\n\nfunction get_most_recent_commit(folder::String)\n    current = pwd()\n    cd(folder)\n    io = IOBuffer()\n    run(pipeline(`git rev-parse HEAD`; stdout=io, stderr=devnull))\n    cd(current)\n    return replace(String(take!(io)), \"\\n\" => \"\")\nend\n\nconst mousetrap_commit = get_most_recent_commit(\"../../mousetrap\")\nconst mousetrap_julia_binding_commit = get_most_recent_commit(\"../../mousetrap_julia_binding\")\n\n# if local, files will be written to ~/.julia/dev/mousetrap_jll\nconst deploy_local = false\nconst skip_build = true\n\nif deploy_local\n    @info \"Deployment: local\"\n    repo = \"local\"\nelse\n    @info \"Deployment: github\"\n    repo = \"Clemapfel/mousetrap_jll\"\nend\n\n## Configure\n\nfunction configure_file(path_in::String, path_out::String)\n    file_in = open(path_in, \"r\")\n    file_out = open(path_out, \"w+\")\n\n    for line in eachline(file_in)\n        write(file_out, replace(line,\n            \"@MOUSETRAP_COMMIT@\" => mousetrap_commit,\n            \"@MOUSETRAP_JULIA_BINDING_COMMIT@\" => mousetrap_julia_binding_commit,\n            \"@VERSION@\" => VERSION\n        ) * \"\\n\")\n    end\n\n    close(file_in)\n    close(file_out)\nend\n\n@info \"Configuring `build_tarballs.jl.in`\"\nconfigure_file(\"./build_tarballs.jl.in\", \"./build_tarballs.jl\")\n\npath = \"/home/clem/.julia/dev/mousetrap_jll\"\nif isfile(path)\n    run(`rm -r $path`)\nend\n\n\nrun(`julia -t 8 build_tarballs.jl --debug --verbose --skip-build --deploy=$repo`)\n"
  },
  {
    "path": "src/Mousetrap.jl",
    "content": "#\n# Author: C. Cords (mail@clemens-cords.com)\n# GitHub: https://github.com/clemapfel/mousetrap.jl\n# Documentation: https://clemens-cords.com/mousetrap\n#\n# Copyright © 2023, Licensed under lGPL-3.0\n#\n\n\"\"\"\n# Mousetrap GUI Engine ($(Mousetrap.VERSION))\n\nGitHub: https://github.com/clemapfel/mousetrap.jl\nDocumentation: http://clemens-cords.com/mousetrap/\n\nCopyright © 2023 C.Cords, Licensed under lGPL-3.0\n\"\"\"\nmodule Mousetrap\n\n    const VERSION = v\"0.3.1\"\n\n####### detail.jl\n\n    module detail\n        using CxxWrap\n        import GTK4_jll, Glib_jll\n        function try_update_gsettings_schema_dir()\n            # request to use GTK4_jll-supplied settings schema if none are available on the machine\n            if !Sys.islinux() && (!haskey(ENV, \"GSETTINGS_SCHEMA_DIR\") || isempty(ENV[\"GSETTINGS_SCHEMA_DIR\"]))\n                ENV[\"GSETTINGS_SCHEMA_DIR\"] = normpath(joinpath(GTK4_jll.libgtk4, \"../../share/glib-2.0/schemas\"))\n            end\n        end\n\n        function __init__()\n            try_update_gsettings_schema_dir()   # executed on `using Mousetrap`, env needs to be set each time before `adw_init` is called\n            @initcxx()\n        end\n\n        using mousetrap_jll\n        function get_mousetrap_julia_binding()\n            return mousetrap_jll.mousetrap_julia_binding\n        end\n\n        try_update_gsettings_schema_dir()       # executed on `precompile Mousetrap`, but not on using, silences warning during installation\n        @wrapmodule(get_mousetrap_julia_binding)\n    end\n\n    const MOUSETRAP_ENABLE_OPENGL_COMPONENT = convert(Bool, detail.MOUSETRAP_ENABLE_OPENGL_COMPONENT)\n\n####### typed_function.jl\n\n    mutable struct TypedFunction\n\n        _apply::Function\n        _return_t::Type\n        _arg_ts::Tuple\n\n        function TypedFunction(f::Function, return_t::Type, arg_ts::Tuple)\n\n            precompile(f, arg_ts)\n\n            arg_ts_string = \"(\"\n            for i in 1:length(arg_ts)\n                arg_ts_string = arg_ts_string * string(arg_ts[i]) * ((i < length(arg_ts)) ? \", \" : \")\")\n            end\n            signature = arg_ts_string  * \" -> $return_t\"\n\n            actual_return_ts = Base.return_types(f, arg_ts)\n\n            if isempty(actual_return_ts)\n                throw(AssertionError(\"Object `$f` is not invokable as function with signature `$signature`, because it does not have a method with argument type(s) `$arg_ts_string`\"))\n            end\n\n            match_found = false\n            for type in actual_return_ts\n                if type <: return_t #|| return_t == Nothing\n                    match_found = true\n                    break\n                end\n            end\n\n            if !match_found\n                throw(AssertionError(\"Object `$f` is not invokable as function with signature `$signature`, because its return type is not `$return_t`\"))\n            end\n\n            return new(f, return_t, arg_ts)\n        end\n    end\n    export TypedFunction\n\n    function (instance::TypedFunction)(args...)\n        return Base.convert(instance._return_t, instance._apply([Base.convert(instance._arg_ts[i], args[i]) for i in 1:length(args)]...))\n    end\n\n####### types.jl\n\n    abstract type SignalEmitter end\n    export SignalEmitter\n\n    abstract type Widget <: SignalEmitter end\n    export Widget\n\n####### log.jl\n\n    const LogDomain = String;\n    export LogDomain\n\n    const MOUSETRAP_DOMAIN = detail.MOUSETRAP_DOMAIN * \".jl\"\n    # no export\n\n    \"\"\"\n    See [`log_debug`](@ref).\n    \"\"\"\n    macro log_debug(domain, message)\n        return :(Mousetrap.detail.log_debug($message, $domain))\n    end\n    log_debug(domain::LogDomain, message::String) = detail.log_debug(message, domain)\n    export @log_debug, log_debug\n\n    \"\"\"\n    See [`log_info`](@ref).\n    \"\"\"\n    macro log_info(domain, message)\n        return :(Mousetrap.detail.log_info($message, $domain))\n    end\n    log_info(domain::LogDomain, message::String) = detail.log_info(message, domain)\n    export @log_info, log_info\n\n    \"\"\"\n    See [`log_warning`](@ref).\n    \"\"\"\n    macro log_warning(domain, message)\n        return :(Mousetrap.detail.log_warning($message, $domain))\n    end\n    log_warning(domain::LogDomain, message::String) = detail.log_warning(message, domain)\n    export @log_warning, log_warning\n\n    \"\"\"\n    See [`log_critical`](@ref).\n    \"\"\"\n    macro log_critical(domain, message)\n        return :(Mousetrap.detail.log_critical($message, $domain))\n    end\n    log_critical(domain::LogDomain, message::String) = detail.log_critical(message, domain)\n    export @log_critical, log_critical\n\n    \"\"\"\n    See [`log_fatal`](@ref).\n    \"\"\"\n    macro log_fatal(domain, message)\n        return :(Mousetrap.detail.log_fatal($message, $domain))\n    end\n    log_fatal(domain::LogDomain, message::String) = detail.log_fatal(message, domain)\n    export @log_fatal, log_fatal\n\n    set_surpress_debug!(domain::LogDomain, b::Bool) = detail.log_set_surpress_debug(domain, b)\n    export set_surpress_debug!\n\n    set_surpress_info!(domain::LogDomain, b::Bool) = detail.log_set_surpress_info(domain, b)\n    export set_surpress_info!\n\n    get_surpress_debug(domain::LogDomain) ::Bool = detail.log_get_surpress_debug(domain)\n    export get_surpress_debug\n\n    get_surpress_info(domain::LogDomain) ::Bool = detail.log_get_surpress_info(domain)\n    export get_surpress_info\n\n    set_log_file!(path::String) ::Bool = detail.log_set_file(path)\n    export set_log_file!\n\n####### common.jl\n\n    macro do_not_compile(args...) return :() end\n\n    import Base: *\n    *(x::Symbol, y::Symbol) = Symbol(string(x) * string(y))\n\n    function from_julia_index(x::Integer) ::UInt64\n        if x <= 0\n            throw(AssertionError(\"Index $x < 1; Indices in Julia are 1-based.\"))\n        end\n        return x - 1\n    end\n\n    function to_julia_index(x::Integer) ::Int64\n        return x + 1\n    end\n\n    function safe_call(scope::String, f, args...)\n        try\n            return f(args...)\n        catch exception\n            printstyled(stderr, \"[ERROR] \"; bold = true, color = :red)\n            printstyled(stderr, \"In \" * scope * \": \"; bold = true)\n            Base.showerror(stderr, exception, catch_backtrace())\n            print(stderr, \"\\n\")\n            throw(exception) # this causes jl_call to return nullptr C-side, as opposed to jl_nothing\n        end\n    end\n\n    macro export_function(type, name, return_t)\n\n        return_t = esc(return_t)\n\n        Mousetrap.eval(:(export $name))\n        return :($name(x::$type) = Base.convert($return_t, detail.$name(x._internal)))\n    end\n\n    macro export_function(type, name, return_t, arg1_type, arg1_name)\n\n        return_t = esc(return_t)\n\n        if arg1_type isa Expr\n            arg1_origin_type = arg1_type.args[2]\n            arg1_destination_type = arg1_type.args[3]\n        else\n            arg1_origin_type = arg1_type\n            arg1_destination_type = arg1_type\n        end\n        arg1_name = esc(arg1_name)\n\n        Mousetrap.eval(:(export $name))\n        return :($name(\n                x::$type,\n                $arg1_name::$arg1_origin_type\n            ) = Base.convert($return_t, detail.$name(x._internal,\n                convert($arg1_destination_type, $arg1_name)\n            )))\n    end\n\n    macro export_function(type, name, return_t, arg1_type, arg1_name, arg2_type, arg2_name)\n\n        return_t = esc(return_t)\n\n        if arg1_type isa Expr\n            arg1_origin_type = arg1_type.args[2]\n            arg1_destination_type = arg1_type.args[3]\n        else\n            arg1_origin_type = arg1_type\n            arg1_destination_type = arg1_type\n        end\n        arg1_name = esc(arg1_name)\n\n        if arg2_type isa Expr\n            arg2_origin_type = arg2_type.args[2]\n            arg2_destination_type = arg2_type.args[3]\n        elseif arg2_type isa Symbol\n            arg2_origin_type = arg2_type\n            arg2_destination_type = arg2_type\n        end\n        arg2_name = esc(arg2_name)\n\n        Mousetrap.eval(:(export $name))\n        return :($name(\n                x::$type,\n                $arg1_name::$arg1_origin_type,\n                $arg2_name::$arg2_origin_type\n            ) = Base.convert($return_t, detail.$name(x._internal,\n                convert($arg1_destination_type, $arg1_name),\n                convert($arg2_destination_type, $arg2_name)\n            )))\n    end\n\n    macro export_function(type, name, return_t, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name)\n\n        return_t = esc(return_t)\n\n        if arg1_type isa Expr\n            arg1_origin_type = arg1_type.args[2]\n            arg1_destination_type = arg1_type.args[3]\n        else\n            arg1_origin_type = arg1_type\n            arg1_destination_type = arg1_type\n        end\n        arg1_name = esc(arg1_name)\n\n        if arg2_type isa Expr\n            arg2_origin_type = arg2_type.args[2]\n            arg2_destination_type = arg2_type.args[3]\n        elseif arg2_type isa Symbol\n            arg2_origin_type = arg2_type\n            arg2_destination_type = arg2_type\n        end\n        arg2_name = esc(arg2_name)\n\n        if arg3_type isa Expr\n            arg3_origin_type = arg3_type.args[2]\n            arg3_destination_type = arg3_type.args[3]\n        else\n            arg3_origin_type = arg3_type\n            arg3_destination_type = arg3_type\n        end\n        arg3_name = esc(arg3_name)\n\n        Mousetrap.eval(:(export $name))\n        return :($name(\n                x::$type,\n                $arg1_name::$arg1_origin_type,\n                $arg2_name::$arg2_origin_type,\n                $arg3_name::$arg3_origin_type\n            ) = Base.convert($return_t, detail.$name(x._internal,\n                convert($arg1_destination_type, $arg1_name),\n                convert($arg2_destination_type, $arg2_name),\n                convert($arg3_destination_type, $arg3_name),\n            )))\n    end\n\n    macro export_function(type, name, return_t, arg1_type, arg1_name, arg2_type, arg2_name, arg3_type, arg3_name, arg4_type, arg4_name)\n\n        return_t = esc(return_t)\n\n        if arg1_type isa Expr\n            arg1_origin_type = arg1_type.args[2]\n            arg1_destination_type = arg1_type.args[3]\n        else\n            arg1_origin_type = arg1_type\n            arg1_destination_type = arg1_type\n        end\n        arg1_name = esc(arg1_name)\n\n        if arg2_type isa Expr\n            arg2_origin_type = arg2_type.args[2]\n            arg2_destination_type = arg2_type.args[3]\n        elseif arg2_type isa Symbol\n            arg2_origin_type = arg2_type\n            arg2_destination_type = arg2_type\n        end\n        arg2_name = esc(arg2_name)\n\n        if arg3_type isa Expr\n            arg3_origin_type = arg3_type.args[2]\n            arg3_destination_type = arg3_type.args[3]\n        else\n            arg3_origin_type = arg3_type\n            arg3_destination_type = arg3_type\n        end\n        arg3_name = esc(arg3_name)\n\n        if arg4_type isa Expr\n            arg4_origin_type = arg4_type.args[2]\n            arg4_destination_type = arg4_type.args[3]\n        else\n            arg4_origin_type = arg4_type\n            arg4_destination_type = arg4_type\n        end\n        arg4_name = esc(arg4_name)\n\n        Mousetrap.eval(:(export $name))\n        return :($name(\n                x::$type,\n                $arg1_name::$arg1_origin_type,\n                $arg2_name::$arg2_origin_type,\n                $arg3_name::$arg3_origin_type,\n                $arg4_name::$arg4_origin_type\n            ) = Base.convert($return_t, detail.$name(x._internal,\n                convert($arg1_destination_type, $arg1_name),\n                convert($arg2_destination_type, $arg2_name),\n                convert($arg3_destination_type, $arg3_name),\n                convert($arg4_destination_type, $arg4_name)\n            )))\n    end\n\n    @generated function get_top_level_widget(x) ::Widget\n        return :(\n            throw(AssertionError(\"Object of type $(typeof(x)) does not fulfill the widget interface. In order for it to be able to be treated as a widget, you need to subtype `Mousetrap.Widget` **and** add a method with signature `(::$(typeof(x))) -> Widget` to `Mousetrap.get_top_level_widget`, which should map an instance of $(typeof(x)) to its top-level widget component.\"))\n        )\n    end\n    export get_top_level_widget\n\n    @generated function is_native_widget(x::Any)\n        :(return false)\n    end\n    # no export\n\n    macro declare_native_widget(Type)\n        out = Expr(:block)\n        push!(out.args, esc(\n            :(is_native_widget(::$Type) = return true)\n        ))\n        #=\n        push!(out.args, esc(\n            :(Mousetrap.get_top_level_widget(x::$Type) = return x)\n        ))\n        =#\n        return out\n    end\n\n    macro export_type(name, super)\n\n        super = esc(super)\n        internal_name = Symbol(\"_\" * \"$name\")\n\n        if !isdefined(detail, :($internal_name))\n            throw(AssertionError(\"In Mousetrap.@export_type: detail.$internal_name undefined\"))\n        end\n\n        out = Expr(:toplevel)\n        Mousetrap.eval(:(export $name))\n        push!(out.args, :(\n            mutable struct $name <: $super\n                _internal::detail.$internal_name\n            end\n        ))\n        return out\n    end\n\n    macro export_type(name)\n\n        internal_name = Symbol(\"_\" * \"$name\")\n\n        if !isdefined(detail, :($internal_name))\n            throw(AssertionError(\"In Mousetrap.@export_type: detail.$internal_name undefined\"))\n        end\n\n        out = Expr(:toplevel)\n        Mousetrap.eval(:(export $name))\n        push!(out.args, :(\n            struct $name\n                _internal::detail.$internal_name\n            end\n        ))\n        return out\n    end\n\n    macro export_enum(enum, block)\n\n        @assert block isa Expr\n\n        out = Expr(:toplevel)\n\n        push!(out.args, (:(export $enum)))\n\n        detail_enum_name = :_ * enum\n        @assert isdefined(detail, detail_enum_name)\n\n        names = Symbol[]\n        push!(out.args, :(const $(esc(enum)) = Mousetrap.detail.$detail_enum_name))\n        for name in block.args\n            if !(name isa Symbol)\n                continue\n            end\n\n            push!(out.args, :(const $(esc(name)) = detail.$name))\n            push!(out.args, :(export $name))\n\n            push!(names, name)\n        end\n\n        enum_str = string(enum)\n        enum_sym = QuoteNode(enum)\n        to_int_name = Symbol(enum) * :_to_int\n\n        push!(out.args, :(Base.string(x::$enum) = string(Mousetrap.detail.$to_int_name(x))))\n        push!(out.args, :(Base.convert(::Type{Integer}, x::$enum) = Integer(Mousetrap.detail.to_int_name(x))))\n        push!(out.args, :(Base.instances(x::Type{$enum}) = [$(names...)]))\n        push!(out.args, :(Base.show(io::IO, x::Type{$enum}) = print(io, (isdefined(Main, $enum_sym) ? \"\" : \"Mousetrap.\") * $enum_str)))\n        push!(out.args, :(Base.show(io::IO, x::$enum) = print(io, string($enum) * \"(\" * string(convert(Int64, x)) * \")\")))\n        return out\n    end\n\n    function show_aux(io::IO, x::T, fields::Symbol...) where T\n\n        out = string(typeof(x)) * \"(\"\n        for i in 1:length(fields)\n\n            field = fields[i]\n\n            get_field = :get_ * field\n            value = eval(:($get_field($x)))\n\n            if typeof(value) == String\n                out *= \"$field = \\\"$value\\\"\"\n            else\n                out *= \"$field = $value\"\n            end\n\n            if i != length(fields)\n                out *= \", \"\n            end\n        end\n\n        out *= \")\"\n        print(io, out)\n    end\n\n###### vector.jl\n\n    mutable struct Vector2{T <: Number}\n        x::T\n        y::T\n\n        Vector2{T}(all::Number) where T = new{T}(convert(T, all), convert(T, all))\n        Vector2{T}(x::Number, y::Number) where T = new{T}(convert(T, x), convert(T, y))\n    end\n    export Vector2\n\n    Base.:(+)(a::Vector2{T}, b::Vector2{T}) where T = Vector2{T}(a.x + b.x, a.y + b.y)\n    Base.:(-)(a::Vector2{T}, b::Vector2{T}) where T = Vector2{T}(a.x - b.x, a.y - b.y)\n    Base.:(*)(a::Vector2{T}, b::Vector2{T}) where T = Vector2{T}(a.x * b.x, a.y * b.y)\n    Base.:(/)(a::Vector2{T}, b::Vector2{T}) where T = Vector2{T}(a.x / b.x, a.y / b.y)\n    Base.:(==)(a::Vector2{T}, b::Vector2{T}) where T = a.x == b.x && a.y == b.y\n    Base.:(!=)(a::Vector2{T}, b::Vector2{T}) where T = !(a == b)\n\n    const Vector2f = Vector2{Cfloat}\n    const Vector2i = Vector2{Cint}\n    const Vector2ui = Vector2{Csize_t}\n\n    export Vector2f, Vector2i, Vector2ui\n\n    mutable struct Vector3{T <: Number}\n        x::T\n        y::T\n        z::T\n\n        Vector3{T}(all::Number) where T = new{T}(convert(T, all), convert(T, all), convert(T, all))\n        Vector3{T}(x::Number, y::Number, z::Number) where T = new{T}(convert(T, x), convert(T, y), convert(T, z))\n    end\n    export Vector3\n\n    Base.:(+)(a::Vector3{T}, b::Vector3{T}) where T = Vector3{T}(a.x + b.x, a.y + b.y, a.z + b.z)\n    Base.:(-)(a::Vector3{T}, b::Vector3{T}) where T = Vector3{T}(a.x - b.x, a.y - b.y, a.z - b.z)\n    Base.:(*)(a::Vector3{T}, b::Vector3{T}) where T = Vector3{T}(a.x * b.x, a.y * b.y, a.z * b.z)\n    Base.:(/)(a::Vector3{T}, b::Vector3{T}) where T = Vector3{T}(a.x / b.x, a.y / b.y, a.z / b.z)\n    Base.:(==)(a::Vector3{T}, b::Vector3{T}) where T = a.x == b.x && a.y == b.y && a.z == b.z\n    Base.:(!=)(a::Vector3{T}, b::Vector3{T}) where T = !(a == b)\n\n    const Vector3f = Vector3{Cfloat}\n    const Vector3i = Vector3{Cint}\n    const Vector3ui = Vector3{Csize_t}\n\n    export Vector3f, Vector3i, Vector3ui\n\n    mutable struct Vector4{T <: Number}\n        x::T\n        y::T\n        z::T\n        w::T\n\n        Vector4{T}(all::Number) where T = new{T}(convert(T, all), convert(T, all), convert(T, all), convert(T, all))\n        Vector4{T}(x::Number, y::Number, z::Number, w::Number) where T = new{T}(convert(T, x), convert(T, y), convert(T, z), convert(T, w))\n    end\n    export Vector4\n\n    Base.:(+)(a::Vector4{T}, b::Vector4{T}) where T = Vector4{T}(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w)\n    Base.:(-)(a::Vector4{T}, b::Vector4{T}) where T = Vector4{T}(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w)\n    Base.:(*)(a::Vector4{T}, b::Vector4{T}) where T = Vector4{T}(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w)\n    Base.:(/)(a::Vector4{T}, b::Vector4{T}) where T = Vector4{T}(a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w)\n    Base.:(==)(a::Vector4{T}, b::Vector4{T}) where T = a.x == b.x && a.y == b.y && a.z == b.z && a.w == b.w\n    Base.:(!=)(a::Vector4{T}, b::Vector4{T}) where T = !(a == b)\n\n    const Vector4f = Vector4{Cfloat}\n    const Vector4i = Vector4{Cint}\n    const Vector4ui = Vector4{Csize_t}\n\n    export Vector4f, Vector4i, Vector4ui\n\n    Base.show(io::IO, x::Vector2{T}) where T = print(io, \"Vector2{\" * string(T) * \"}(\" * string(x.x) * \", \" * string(x.y) * \")\")\n    Base.show(io::IO, x::Vector3{T}) where T = print(io, \"Vector3{\" * string(T) * \"}(\" * string(x.x) * \", \" * string(x.y) * \", \" * string(x.z) * \")\")\n    Base.show(io::IO, x::Vector4{T}) where T = print(io, \"Vector4{\" * string(T) * \"}(\" * string(x.x) * \", \" * string(x.y) * \", \" * string(x.z) * \", \" * string(x.w) * \")\")\n\n####### time.jl\n\n    struct Time\n        _ns::Int64\n    end\n    export Time\n\n    as_minutes(time::Time) ::Cdouble = detail.ns_to_minutes(time._ns)\n    export as_minutes\n\n    as_seconds(time::Time) ::Cdouble = detail.ns_to_seconds(time._ns)\n    export as_seconds\n\n    as_milliseconds(time::Time) ::Cdouble = detail.ns_to_milliseconds(time._ns)\n    export as_milliseconds\n\n    as_microseconds(time::Time) ::Cdouble = detail.ns_to_microseconds(time._ns)\n    export as_microseconds\n\n    as_nanoseconds(time::Time) ::Int64 = time._ns\n    export as_nanoseconds\n\n    minutes(n::Number) = Time(detail.minutes_to_ns(convert(Cdouble, n)))\n    export minutes\n\n    seconds(n::Number) = Time(detail.seconds_to_ns(convert(Cdouble, n)))\n    export seconds\n\n    milliseconds(n::Number) = Time(detail.milliseconds_to_ns(convert(Cdouble, n)))\n    export milliseconds\n\n    microseconds(n::Number) = Time(detail.microseconds_to_ns(convert(Cdouble, n)))\n    export microseconds\n\n    nanoseconds(n::Integer) = Time(n)\n    export nanoseconds\n\n    Base.:(+)(a::Time, b::Time) = Time(a._ns + b._ns)\n    Base.:(-)(a::Time, b::Time) = Time(a._ns - b._ns)\n    Base.:(==)(a::Time, b::Time) = a._ns == b._ns\n    Base.:(!=)(a::Time, b::Time) = !(a == b)\n    Base.:(<)(a::Time, b::Time) = a._ns < b._ns\n    Base.:(>)(a::Time, b::Time) = a._ns > b._ns\n\n    Base.show(io::IO, x::Time) = print(io, \"Time($(as_seconds(x))s)\")\n\n    @export_type Clock SignalEmitter\n    Clock() = Clock(detail._Clock())\n\n    restart!(clock::Clock) ::Time = microseconds(detail.restart!(clock._internal))\n    export restart!\n\n    elapsed(clock::Clock) ::Time = microseconds(detail.elapsed(clock._internal))\n    export elapsed\n\n    Base.show(io::IO, x::Clock) = print(io, \"Clock($(elapsed(x))s)\")\n\n####### angle.jl\n\n    struct Angle\n        _rads::Cfloat\n    end\n    export Angle\n\n    degrees(x::Number) = Angle(convert(Cfloat, deg2rad(x)))\n    export degrees\n\n    radians(x::Number) = Angle(convert(Cfloat, x))\n    export radians\n\n    as_degrees(angle::Angle) ::Cdouble = rad2deg(angle._rads)\n    export as_degrees\n\n    as_radians(angle::Angle) ::Cdouble = angle._rads\n    export as_radians\n\n    Base.:(+)(a::Angle, b::Angle) = Angle(a._rads + b._rads)\n    Base.:(-)(a::Angle, b::Angle) = Angle(a._rads - b._rads)\n    Base.:(*)(a::Angle, b::Angle) = Angle(a._rads * b._rads)\n    Base.:(/)(a::Angle, b::Angle) = Angle(a._rads / b._rads)\n    Base.:(==)(a::Angle, b::Angle) = a._rads == b._rads\n    Base.:(!=)(a::Angle, b::Angle) = !(a._rads == b._rads)\n\n    Base.show(io::IO, angle::Angle) = print(io, \"Angle($(as_degrees(angle))°)\")\n\n####### signal_components.jl\n\n    macro add_signal(T, snake_case, Return_t)\n\n        out = Expr(:block)\n\n        connect_signal_name = :connect_signal_ * snake_case * :!\n\n        push!(out.args, esc(:(\n            function $connect_signal_name(f, x::$T)\n                typed_f = TypedFunction(f, $Return_t, ($T,))\n                detail.$connect_signal_name(x._internal, function(x)\n                    typed_f($T(x[1][]))\n                end)\n            end\n        )))\n\n        push!(out.args, esc(:(\n            function $connect_signal_name(f, x::$T, data::Data_t) where Data_t\n                typed_f = TypedFunction(f, $Return_t, ($T, Data_t))\n                detail.$connect_signal_name(x._internal, function(x)\n                    typed_f($T(x[1][]), data)\n                end)\n            end\n        )))\n\n        emit_signal_name = :emit_signal_ * snake_case\n\n        push!(out.args, esc(:(\n            function $emit_signal_name(x::$T) ::$Return_t\n                return convert($Return_t, detail.$emit_signal_name(x._internal))\n            end\n        )))\n\n        disconnect_signal_name = :disconnect_signal_ * snake_case * :!\n\n        push!(out.args, esc(:(\n            function $disconnect_signal_name(x::$T)\n                detail.$disconnect_signal_name(x._internal)\n            end\n        )))\n\n        set_signal_blocked_name = :set_signal_ * snake_case * :_blocked * :!\n\n        push!(out.args, esc(:(\n            function $set_signal_blocked_name(x::$T, b)\n                detail.$set_signal_blocked_name(x._internal, b)\n            end\n        )))\n\n        get_signal_blocked_name = :get_signal_ * snake_case * :_blocked\n\n        push!(out.args, esc(:(\n            function $get_signal_blocked_name(x::$T)\n                return detail.$get_signal_blocked_name(x._internal)\n            end\n        )))\n\n        push!(out.args, esc(:(export $connect_signal_name)))\n        push!(out.args, esc(:(export $disconnect_signal_name)))\n        push!(out.args, esc(:(export $set_signal_blocked_name)))\n        push!(out.args, esc(:(export $get_signal_blocked_name)))\n        push!(out.args, esc(:(export $emit_signal_name)))\n\n        return out\n    end\n\n    macro add_signal(T, snake_case, Return_t, Arg1_t, arg1_name)\n\n        out = Expr(:block)\n\n        connect_signal_name = :connect_signal_ * snake_case * :!\n\n        Arg1_t = esc(Arg1_t)\n\n        arg1_name = esc(arg1_name)\n\n        push!(out.args, esc(:(\n            function $connect_signal_name(f, x::$T)\n                typed_f = TypedFunction(f, $Return_t, ($T, $Arg1_t))\n                detail.$connect_signal_name(x._internal, function(x)\n                    typed_f($T(x[1][]), x[2])\n                end)\n            end\n        )))\n\n        push!(out.args, esc(:(\n            function $connect_signal_name(f, x::$T, data::Data_t) where Data_t\n                typed_f = TypedFunction(f, $Return_t, ($T, $Arg1_t, Data_t))\n                detail.$connect_signal_name(x._internal, function(x)\n                    typed_f($T(x[1][]), x[2], data)\n                end)\n            end\n        )))\n\n        emit_signal_name = :emit_signal_ * snake_case\n\n        push!(out.args, esc(:(\n            function $emit_signal_name(x::$T, $arg1_name::$Arg1_t) ::$Return_t\n                return convert($Return_t, detail.$emit_signal_name(x._internal, $arg1_name))\n            end\n        )))\n\n        disconnect_signal_name = :disconnect_signal_ * snake_case * :!\n\n        push!(out.args, esc(:(\n            function $disconnect_signal_name(x::$T)\n                detail.$disconnect_signal_name(x._internal)\n            end\n        )))\n\n        set_signal_blocked_name = :set_signal_ * snake_case * :_blocked * :!\n\n        push!(out.args, esc(:(\n            function $set_signal_blocked_name(x::$T, b)\n                detail.$set_signal_blocked_name(x._internal, b)\n            end\n        )))\n\n        get_signal_blocked_name = :get_signal_ * snake_case * :_blocked\n\n        push!(out.args, esc(:(\n            function $get_signal_blocked_name(x::$T)\n                return detail.$get_signal_blocked_name(x._internal)\n            end\n        )))\n\n        push!(out.args, esc(:(export $connect_signal_name)))\n        push!(out.args, esc(:(export $disconnect_signal_name)))\n        push!(out.args, esc(:(export $set_signal_blocked_name)))\n        push!(out.args, esc(:(export $get_signal_blocked_name)))\n        push!(out.args, esc(:(export $emit_signal_name)))\n\n        return out\n    end\n\n    macro add_signal(T, snake_case, Return_t, Arg1_t, arg1_name, Arg2_t, arg2_name)\n\n        out = Expr(:block)\n\n        connect_signal_name = :connect_signal_ * snake_case * :!\n\n        Arg1_t = esc(Arg1_t)\n        Arg2_t = esc(Arg2_t)\n\n        arg1_name = esc(arg1_name)\n        arg2_name = esc(arg2_name)\n\n        push!(out.args, esc(:(\n            function $connect_signal_name(f, x::$T)\n                typed_f = TypedFunction(f, $Return_t, ($T, $Arg1_t, $Arg2_t))\n                detail.$connect_signal_name(x._internal, function(x)\n                    typed_f($T(x[1][]), x[2], x[3])\n                end)\n            end\n        )))\n\n        push!(out.args, esc(:(\n            function $connect_signal_name(f, x::$T, data::Data_t) where Data_t\n                typed_f = TypedFunction(f, $Return_t, ($T, $Arg1_t, $Arg2_t, Data_t))\n                detail.$connect_signal_name(x._internal, function(x)\n                    typed_f($T(x[1][]), x[2], x[3], data)\n                end)\n            end\n        )))\n\n        emit_signal_name = :emit_signal_ * snake_case\n\n        push!(out.args, esc(:(\n            function $emit_signal_name(x::$T, $arg1_name::$Arg1_t, $arg2_name::$Arg2_t) ::$Return_t\n                return convert($Return_t, detail.$emit_signal_name(x._internal, $arg1_name, $arg2_name))\n            end\n        )))\n\n        disconnect_signal_name = :disconnect_signal_ * snake_case * :!\n\n        push!(out.args, esc(:(\n            function $disconnect_signal_name(x::$T)\n                detail.$disconnect_signal_name(x._internal)\n            end\n        )))\n\n        set_signal_blocked_name = :set_signal_ * snake_case * :_blocked * :!\n\n        push!(out.args, esc(:(\n            function $set_signal_blocked_name(x::$T, b)\n                detail.$set_signal_blocked_name(x._internal, b)\n            end\n        )))\n\n        get_signal_blocked_name = :get_signal_ * snake_case * :_blocked\n\n        push!(out.args, esc(:(\n            function $get_signal_blocked_name(x::$T)\n                return detail.$get_signal_blocked_name(x._internal)\n            end\n        )))\n\n        push!(out.args, esc(:(export $connect_signal_name)))\n        push!(out.args, esc(:(export $disconnect_signal_name)))\n        push!(out.args, esc(:(export $set_signal_blocked_name)))\n        push!(out.args, esc(:(export $get_signal_blocked_name)))\n        push!(out.args, esc(:(export $emit_signal_name)))\n\n        return out\n    end\n\n    macro add_signal(T, snake_case, Return_t, Arg1_t, arg1_name, Arg2_t, arg2_name, Arg3_t, arg3_name)\n\n        out = Expr(:block)\n\n        connect_signal_name = :connect_signal_ * snake_case * :!\n\n        Arg1_t = esc(Arg1_t)\n        Arg2_t = esc(Arg2_t)\n        Arg3_t = esc(Arg3_t)\n\n        arg1_name = esc(arg1_name)\n        arg2_name = esc(arg2_name)\n        arg3_name = esc(arg3_name)\n\n        push!(out.args, esc(:(\n            function $connect_signal_name(f, x::$T)\n                typed_f = TypedFunction(f, $Return_t, ($T, $Arg1_t, $Arg2_t, $Arg3_t))\n                detail.$connect_signal_name(x._internal, function(x)\n                    typed_f($T(x[1][]), x[2], x[3], x[4])\n                end)\n            end\n        )))\n\n        push!(out.args, esc(:(\n            function $connect_signal_name(f, x::$T, data::Data_t) where Data_t\n                typed_f = TypedFunction(f, $Return_t, ($T, $Arg1_t, $Arg2_t, $Arg3_t, Data_t))\n                detail.$connect_signal_name(x._internal, function(x)\n                    typed_f($T(x[1][]), x[2], x[3], x[4], data)\n                end)\n            end\n        )))\n\n        emit_signal_name = :emit_signal_ * snake_case\n\n        push!(out.args, esc(:(\n            function $emit_signal_name(x::$T, $arg1_name::$Arg1_t, $arg2_name::$Arg2_t, $arg3_name::$Arg3_t) ::$Return_t\n                return convert($Return_t, detail.$emit_signal_name(x._internal, $arg1_name, $arg2_name, $arg3_name))\n            end\n        )))\n\n        disconnect_signal_name = :disconnect_signal_ * snake_case * :!\n\n        push!(out.args, esc(:(\n            function $disconnect_signal_name(x::$T)\n                detail.$disconnect_signal_name(x._internal)\n            end\n        )))\n\n        set_signal_blocked_name = :set_signal_ * snake_case * :_blocked * :!\n\n        push!(out.args, esc(:(\n            function $set_signal_blocked_name(x::$T, b)\n                detail.$set_signal_blocked_name(x._internal, b)\n            end\n        )))\n\n        get_signal_blocked_name = :get_signal_ * snake_case * :_blocked\n\n        push!(out.args, esc(:(\n            function $get_signal_blocked_name(x::$T)\n                return detail.$get_signal_blocked_name(x._internal)\n            end\n        )))\n\n        push!(out.args, esc(:(export $connect_signal_name)))\n        push!(out.args, esc(:(export $disconnect_signal_name)))\n        push!(out.args, esc(:(export $set_signal_blocked_name)))\n        push!(out.args, esc(:(export $get_signal_blocked_name)))\n        push!(out.args, esc(:(export $emit_signal_name)))\n\n        return out\n    end\n\n    macro add_signal_realize(x) return :(@add_signal $x realize Cvoid) end\n    macro add_signal_unrealize(x) return :(@add_signal $x unrealize Cvoid) end\n    macro add_signal_destroy(x) return :(@add_signal $x destroy Cvoid) end\n    macro add_signal_hide(x) return :(@add_signal $x hide Cvoid) end\n    macro add_signal_show(x) return :(@add_signal $x show Cvoid) end\n    macro add_signal_map(x) return :(@add_signal $x map Cvoid) end\n    macro add_signal_unmap(x) return :(@add_signal $x unmap Cvoid) end\n\n    macro add_widget_signals(x)\n        return quote\n            @add_signal_realize($x)\n            @add_signal_unrealize($x)\n            @add_signal_destroy($x)\n            @add_signal_hide($x)\n            @add_signal_show($x)\n            @add_signal_map($x)\n            @add_signal_unmap($x)\n        end\n    end\n\n    macro add_signal_activate(x) return :(@add_signal $x activate Cvoid) end\n    macro add_signal_shutdown(x) return :(@add_signal $x shutdown Cvoid) end\n    macro add_signal_clicked(x) return :(@add_signal $x clicked Cvoid) end\n    macro add_signal_toggled(x) return :(@add_signal $x toggled Cvoid) end\n    macro add_signal_dismissed(x) return :(@add_signal $x dismissed Cvoid) end\n    macro add_signal_button_clicked(x) return :(@add_signal $x button_clicked Cvoid) end\n    macro add_signal_activate_default_widget(x) return :(@add_signal $x activate_default_widget Cvoid) end\n    macro add_signal_activate_focused_widget(x) return :(@add_signal $x activate_focused_widget Cvoid) end\n    macro add_signal_close_request(x) return :(@add_signal $x close_request WindowCloseRequestResult) end\n    macro add_signal_items_changed(x) return :(@add_signal $x items_changed Cvoid Integer position Integer n_removed Integer n_added) end\n    macro add_signal_closed(x) return :(@add_signal $x closed Cvoid) end\n    macro add_signal_text_changed(x) return :(@add_signal $x text_changed Cvoid) end\n\n    macro add_signal_drag_begin(x) return :(@add_signal $x drag_begin Cvoid AbstractFloat start_x AbstractFloat start_y) end\n    macro add_signal_drag(x) return :(@add_signal $x drag Cvoid AbstractFloat offset_x AbstractFloat offset_y) end\n    macro add_signal_drag_end(x) return :(@add_signal $x drag_end Cvoid AbstractFloat offset_x AbstractFloat offset_y) end\n\n    macro add_signal_click_pressed(x) return :(@add_signal $x click_pressed Cvoid Integer n_press AbstractFloat x AbstractFloat y) end\n    macro add_signal_click_released(x) return :(@add_signal $x click_released Cvoid Integer n_press AbstractFloat x AbstractFloat y) end\n    macro add_signal_click_stopped(x) return :(@add_signal $x click_stopped Cvoid) end\n\n    macro add_signal_focus_gained(x) return :(@add_signal $x focus_gained Cvoid) end\n    macro add_signal_focus_lost(x) return :(@add_signal $x focus_lost Cvoid) end\n\n    macro add_signal_pressed(x) return :(@add_signal $x pressed Cvoid AbstractFloat x AbstractFloat y) end\n    macro add_signal_press_cancelled(x) return :(@add_signal $x press_cancelled Cvoid) end\n\n    macro add_signal_motion_enter(x) return :(@add_signal $x motion_enter Cvoid AbstractFloat x AbstractFloat y) end\n    macro add_signal_motion(x) return :(@add_signal $x motion Cvoid AbstractFloat x AbstractFloat y) end\n    macro add_signal_motion_leave(x) return :(@add_signal $x motion_leave Cvoid) end\n\n    macro add_signal_scale_changed(x) return :(@add_signal $x scale_changed Cvoid AbstractFloat scale) end\n    macro add_signal_rotation_changed(x) return :(@add_signal $x rotation_changed Cvoid AbstractFloat angle_absolute_rads AbstractFloat angle_delta_rads) end\n\n    macro add_signal_kinetic_scroll_decelerate(x) return :(@add_signal $x kinetic_scroll_decelerate Cvoid AbstractFloat x_velocity AbstractFloat y_velocity) end\n    macro add_signal_scroll_begin(x) return :(@add_signal $x scroll_begin Cvoid) end\n    macro add_signal_scroll(x) return :(@add_signal $x scroll Cvoid AbstractFloat delta_x AbstractFloat delta_y) end # sic, jl_unbox_bool(jl_nothing) == true\n    macro add_signal_scroll_end(x) return :(@add_signal $x scroll_end Cvoid) end\n\n    macro add_signal_stylus_up(x) return :(@add_signal $x stylus_up Cvoid AbstractFloat x AbstractFloat y) end\n    macro add_signal_stylus_down(x) return :(@add_signal $x stylus_down Cvoid AbstractFloat x AbstractFloat y) end\n    macro add_signal_proximity(x) return :(@add_signal $x proximity Cvoid AbstractFloat x AbstractFloat y) end\n\n    macro add_signal_swipe(x) return :(@add_signal $x swipe Cvoid AbstractFloat x_velocity AbstractFloat y_velocity) end\n    macro add_signal_pan(x) return :(@add_signal $x pan Cvoid PanDirection direction AbstractFloat offset) end\n\n    macro add_signal_paint(x) return :(@add_signal $x paint Cvoid) end\n    macro add_signal_update(x) return :(@add_signal $x update Cvoid) end\n\n    macro add_signal_value_changed(x) return :(@add_signal $x value_changed Cvoid) end\n    macro add_signal_properties_changed(x) return :(@add_signal $x properties_changed Cvoid) end\n\n    macro add_signal_wrapped(x) return :(@add_signal $x wrapped Cvoid) end\n\n    macro add_signal_scroll_child(x) return :(@add_signal $x scroll_child Cvoid ScrollType type Bool is_horizontal) end\n    macro add_signal_resize(x) return :(@add_signal $x resize Cvoid Integer width Integer height) end\n    macro add_signal_render(x) return :(@add_signal $x render Bool Ptr{Cvoid} gdk_gl_context_ptr) end\n\n    macro add_signal_modifiers_changed(x) return :(@add_signal $x modifiers_changed Cvoid ModifierState state) end\n\n    macro add_signal_activate_item(T)\n\n        snake_case = :activate_item\n        Return_t = Cvoid\n        Arg1_t = Integer\n        arg1_name = :index\n\n        out = Expr(:block)\n        connect_signal_name = :connect_signal_ * snake_case * :!\n\n        push!(out.args, esc(:(\n            function $connect_signal_name(f, x::$T)\n                typed_f = TypedFunction(f, $Return_t, ($T, $Arg1_t))\n                detail.$connect_signal_name(x._internal, function(x)\n                    typed_f($T(x[1][]), to_julia_index(x[2]))\n                end)\n            end\n        )))\n\n        push!(out.args, esc(:(\n            function $connect_signal_name(f, x::$T, data::Data_t) where Data_t\n                typed_f = TypedFunction(f, $Return_t, ($T, $Arg1_t, Data_t))\n                detail.$connect_signal_name(x._internal, function(x)\n                    typed_f($T(x[1][]), to_julia_index(x[2]), data)\n                end)\n            end\n        )))\n\n        emit_signal_name = :emit_signal_ * snake_case\n\n        push!(out.args, esc(:(\n            function $emit_signal_name(x::$T, $arg1_name::$Arg1_t) ::$Return_t\n                return convert($Return_t, detail.$emit_signal_name(x._internal, from_julia_index($arg1_name)))\n            end\n        )))\n\n        disconnect_signal_name = :disconnect_signal_ * snake_case * :!\n\n        push!(out.args, esc(:(\n            function $disconnect_signal_name(x::$T)\n                detail.$disconnect_signal_name(x._internal)\n            end\n        )))\n\n        set_signal_blocked_name = :set_signal_ * snake_case * :_blocked * :!\n\n        push!(out.args, esc(:(\n            function $set_signal_blocked_name(x::$T, b)\n                detail.$set_signal_blocked_name(x._internal, b)\n            end\n        )))\n\n        get_signal_blocked_name = :get_signal_ * snake_case * :_blocked\n\n        push!(out.args, esc(:(\n            function $get_signal_blocked_name(x::$T)\n                return detail.$get_signal_blocked_name(x._internal)\n            end\n        )))\n\n        push!(out.args, esc(:(export $connect_signal_name)))\n        push!(out.args, esc(:(export $disconnect_signal_name)))\n        push!(out.args, esc(:(export $set_signal_blocked_name)))\n        push!(out.args, esc(:(export $get_signal_blocked_name)))\n        push!(out.args, esc(:(export $emit_signal_name)))\n\n        return out\n    end\n\n    macro add_signal_activated(T)\n\n        out = Expr(:block)\n        snake_case = :activated\n        Return_t = Cvoid\n\n        connect_signal_name = :connect_signal_ * snake_case * :!\n\n        push!(out.args, esc(:(\n            function $connect_signal_name(f, x::$T)\n                typed_f = TypedFunction(f, $Return_t, ($T,))\n                detail.$connect_signal_name(x._internal, function(x)\n                    typed_f($T(x[1][]))\n                end)\n            end\n        )))\n\n        push!(out.args, esc(:(\n            function $connect_signal_name(f, x::$T, data::Data_t) where Data_t\n                typed_f = TypedFunction(f, $Return_t, ($T, Data_t))\n                detail.$connect_signal_name(x._internal, function(x)\n                    typed_f($T(x[1][]), data)\n                end)\n            end\n        )))\n\n        emit_signal_name = :emit_signal_ * snake_case\n\n        push!(out.args, esc(:(\n            function $emit_signal_name(x::$T) ::$Return_t\n                return convert($Return_t, detail.$emit_signal_name(x._internal, nothing))\n            end\n        )))\n\n        disconnect_signal_name = :disconnect_signal_ * snake_case * :!\n\n        push!(out.args, esc(:(\n            function $disconnect_signal_name(x::$T)\n                detail.$disconnect_signal_name(x._internal)\n            end\n        )))\n\n        set_signal_blocked_name = :set_signal_ * snake_case * :_blocked * :!\n\n        push!(out.args, esc(:(\n            function $set_signal_blocked_name(x::$T, b)\n                detail.$set_signal_blocked_name(x._internal, b)\n            end\n        )))\n\n        get_signal_blocked_name = :get_signal_ * snake_case * :_blocked\n\n        push!(out.args, esc(:(\n            function $get_signal_blocked_name(x::$T)\n                return detail.$get_signal_blocked_name(x._internal)\n            end\n        )))\n\n        push!(out.args, esc(:(export $connect_signal_name)))\n        push!(out.args, esc(:(export $disconnect_signal_name)))\n        push!(out.args, esc(:(export $set_signal_blocked_name)))\n        push!(out.args, esc(:(export $get_signal_blocked_name)))\n        push!(out.args, esc(:(export $emit_signal_name)))\n\n        return out\n    end\n\n    macro add_signal_revealed(T)\n\n        out = Expr(:block)\n        snake_case = :revealed\n        Return_t = Cvoid\n\n        connect_signal_name = :connect_signal_ * snake_case * :!\n\n        push!(out.args, esc(:(\n            function $connect_signal_name(f, x::$T)\n                typed_f = TypedFunction(f, $Return_t, ($T,))\n                detail.$connect_signal_name(x._internal, function(x)\n                    typed_f($T(x[1][]))\n                end)\n            end\n        )))\n\n        push!(out.args, esc(:(\n            function $connect_signal_name(f, x::$T, data::Data_t) where Data_t\n                typed_f = TypedFunction(f, $Return_t, ($T, Data_t))\n                detail.$connect_signal_name(x._internal, function(x)\n                    typed_f($T(x[1][]), data)\n                end)\n            end\n        )))\n\n        emit_signal_name = :emit_signal_ * snake_case\n\n        push!(out.args, esc(:(\n            function $emit_signal_name(x::$T) ::$Return_t\n                return convert($Return_t, detail.$emit_signal_name(x._internal, nothing))\n            end\n        )))\n\n        disconnect_signal_name = :disconnect_signal_ * snake_case * :!\n\n        push!(out.args, esc(:(\n            function $disconnect_signal_name(x::$T)\n                detail.$disconnect_signal_name(x._internal)\n            end\n        )))\n\n        set_signal_blocked_name = :set_signal_ * snake_case * :_blocked * :!\n\n        push!(out.args, esc(:(\n            function $set_signal_blocked_name(x::$T, b)\n                detail.$set_signal_blocked_name(x._internal, b)\n            end\n        )))\n\n        get_signal_blocked_name = :get_signal_ * snake_case * :_blocked\n\n        push!(out.args, esc(:(\n            function $get_signal_blocked_name(x::$T)\n                return detail.$get_signal_blocked_name(x._internal)\n            end\n        )))\n\n        push!(out.args, esc(:(export $connect_signal_name)))\n        push!(out.args, esc(:(export $disconnect_signal_name)))\n        push!(out.args, esc(:(export $set_signal_blocked_name)))\n        push!(out.args, esc(:(export $get_signal_blocked_name)))\n        push!(out.args, esc(:(export $emit_signal_name)))\n\n        return out\n    end\n\n    macro add_signal_switched(T)\n\n        out = Expr(:block)\n        snake_case = :switched\n        Return_t = Cvoid\n\n        connect_signal_name = :connect_signal_ * snake_case * :!\n\n        push!(out.args, esc(:(\n            function $connect_signal_name(f, x::$T)\n                typed_f = TypedFunction(f, $Return_t, ($T,))\n                detail.$connect_signal_name(x._internal, function(x)\n                    typed_f($T(x[1][]))\n                end)\n            end\n        )))\n\n        push!(out.args, esc(:(\n            function $connect_signal_name(f, x::$T, data::Data_t) where Data_t\n                typed_f = TypedFunction(f, $Return_t, ($T, Data_t))\n                detail.$connect_signal_name(x._internal, function(x)\n                    typed_f($T(x[1][]), data)\n                end)\n            end\n        )))\n\n        emit_signal_name = :emit_signal_ * snake_case\n\n        push!(out.args, esc(:(\n            function $emit_signal_name(x::$T) ::$Return_t\n                return convert($Return_t, detail.$emit_signal_name(x._internal, nothing))\n            end\n        )))\n\n        disconnect_signal_name = :disconnect_signal_ * snake_case * :!\n\n        push!(out.args, esc(:(\n            function $disconnect_signal_name(x::$T)\n                detail.$disconnect_signal_name(x._internal)\n            end\n        )))\n\n        set_signal_blocked_name = :set_signal_ * snake_case * :_blocked * :!\n\n        push!(out.args, esc(:(\n            function $set_signal_blocked_name(x::$T, b)\n                detail.$set_signal_blocked_name(x._internal, b)\n            end\n        )))\n\n        get_signal_blocked_name = :get_signal_ * snake_case * :_blocked\n\n        push!(out.args, esc(:(\n            function $get_signal_blocked_name(x::$T)\n                return detail.$get_signal_blocked_name(x._internal)\n            end\n        )))\n\n        push!(out.args, esc(:(export $connect_signal_name)))\n        push!(out.args, esc(:(export $disconnect_signal_name)))\n        push!(out.args, esc(:(export $set_signal_blocked_name)))\n        push!(out.args, esc(:(export $get_signal_blocked_name)))\n        push!(out.args, esc(:(export $emit_signal_name)))\n\n        return out\n    end\n\n    macro add_signal_selection_changed(T)\n\n        snake_case = :selection_changed\n\n        Arg1_t = Int64\n        arg1_name = :position\n\n        Arg2_t = Int64\n        arg2_name = :n_items\n\n        Return_t = Cvoid\n\n        out = Expr(:block)\n\n        connect_signal_name = :connect_signal_ * snake_case * :!\n\n        push!(out.args, esc(:(\n            function $connect_signal_name(f, x::$T)\n                typed_f = TypedFunction(f, $Return_t, ($T, $Arg1_t, $Arg2_t))\n                detail.$connect_signal_name(x._internal, function(x)\n                    typed_f($T(x[1][]), to_julia_index(x[2]), x[3])\n                end)\n            end\n        )))\n\n        push!(out.args, esc(:(\n            function $connect_signal_name(f, x::$T, data::Data_t) where Data_t\n                typed_f = TypedFunction(f, $Return_t, ($T, $Arg1_t, $Arg2_t, Data_t))\n                detail.$connect_signal_name(x._internal, function(x)\n                    typed_f($T(x[1][]), to_julia_index(x[2]), x[3], data)\n                end)\n            end\n        )))\n\n        emit_signal_name = :emit_signal_ * snake_case\n\n        push!(out.args, esc(:(\n            function $emit_signal_name(x::$T, $arg1_name::$Arg1_t, $arg2_name::$Arg2_t) ::$Return_t\n                return convert($Return_t, detail.$emit_signal_name(x._internal, from_julia_index($arg1_name), $arg2_name))\n            end\n        )))\n\n        disconnect_signal_name = :disconnect_signal_ * snake_case * :!\n\n        push!(out.args, esc(:(\n            function $disconnect_signal_name(x::$T)\n                detail.$disconnect_signal_name(x._internal)\n            end\n        )))\n\n        set_signal_blocked_name = :set_signal_ * snake_case * :_blocked * :!\n\n        push!(out.args, esc(:(\n            function $set_signal_blocked_name(x::$T, b)\n                detail.$set_signal_blocked_name(x._internal, b)\n            end\n        )))\n\n        get_signal_blocked_name = :get_signal_ * snake_case * :_blocked\n\n        push!(out.args, esc(:(\n            function $get_signal_blocked_name(x::$T)\n                return detail.$get_signal_blocked_name(x._internal)\n            end\n        )))\n\n        push!(out.args, esc(:(export $connect_signal_name)))\n        push!(out.args, esc(:(export $disconnect_signal_name)))\n        push!(out.args, esc(:(export $set_signal_blocked_name)))\n        push!(out.args, esc(:(export $get_signal_blocked_name)))\n        push!(out.args, esc(:(export $emit_signal_name)))\n\n        return out\n    end\n\n    macro add_key_event_controller_signal(T, name, Return_t)\n\n        out = Expr(:block)\n        snake_case = name\n\n        connect_signal_name = :connect_signal_ * snake_case * :!\n\n        Arg1_t = Cuint\n        Arg2_t = Cuint\n        Arg3_t = detail._ModifierState\n\n        arg1_name = :key_code\n        arg2_name = :key_value\n        arg3_name = :modifiers\n\n        push!(out.args, esc(:(\n            function $connect_signal_name(f, x::$T)\n                typed_f = TypedFunction(f, $Return_t, ($T, $Arg1_t, $Arg3_t))\n                detail.$connect_signal_name(x._internal, function(x)\n                    typed_f($T(x[1][]), x[2], x[4])\n                    return false\n                end)\n            end\n        )))\n\n        push!(out.args, esc(:(\n            function $connect_signal_name(f, x::$T, data::Data_t) where Data_t\n                typed_f = TypedFunction(f, $Return_t, ($T, $Arg1_t, $Arg3_t, Data_t))\n                detail.$connect_signal_name(x._internal, function(x)\n                    typed_f($T(x[1][]), x[2], x[4], data)\n                    return false\n                end)\n            end\n        )))\n\n        emit_signal_name = :emit_signal_ * snake_case\n\n        push!(out.args, esc(:(\n            function $emit_signal_name(x::$T, $arg1_name::$Arg1_t, $arg3_name::$Arg3_t) ::$Return_t\n                return convert($Return_t, detail.$emit_signal_name(x._internal, $arg1_name, $arg3_name))\n            end\n        )))\n\n        disconnect_signal_name = :disconnect_signal_ * snake_case * :!\n\n        push!(out.args, esc(:(\n            function $disconnect_signal_name(x::$T)\n                detail.$disconnect_signal_name(x._internal)\n            end\n        )))\n\n        set_signal_blocked_name = :set_signal_ * snake_case * :_blocked * :!\n\n        push!(out.args, esc(:(\n            function $set_signal_blocked_name(x::$T, b)\n                detail.$set_signal_blocked_name(x._internal, b)\n            end\n        )))\n\n        get_signal_blocked_name = :get_signal_ * snake_case * :_blocked\n\n        push!(out.args, esc(:(\n            function $get_signal_blocked_name(x::$T)\n                return detail.$get_signal_blocked_name(x._internal)\n            end\n        )))\n\n        push!(out.args, esc(:(export $connect_signal_name)))\n        push!(out.args, esc(:(export $disconnect_signal_name)))\n        push!(out.args, esc(:(export $set_signal_blocked_name)))\n        push!(out.args, esc(:(export $get_signal_blocked_name)))\n        push!(out.args, esc(:(export $emit_signal_name)))\n\n        return out\n    end\n\n    macro add_notebook_signal(T, snake_case)\n\n        out = Expr(:block)\n\n        Return_t = Cvoid\n        connect_signal_name = :connect_signal_ * snake_case * :!\n\n        push!(out.args, esc(:(\n            function $connect_signal_name(f, x::$T)\n                typed_f = TypedFunction(f, $Return_t, ($T, Integer))\n                detail.$connect_signal_name(x._internal, function(x)\n                    typed_f($T(x[1][]), to_julia_index(x[3]))\n                end)\n            end\n        )))\n\n        push!(out.args, esc(:(\n            function $connect_signal_name(f, x::$T, data::Data_t) where Data_t\n                typed_f = TypedFunction(f, $Return_t, ($T, Integer, Data_t))\n                detail.$connect_signal_name(x._internal, function(x)\n                    typed_f($T(x[1][]), to_julia_index(x[3]), data)\n                end)\n            end\n        )))\n\n        emit_signal_name = :emit_signal_ * snake_case\n\n        push!(out.args, esc(:(\n            function $emit_signal_name(x::$T, page_index::Integer) ::$Return_t\n                return convert($Return_t, detail.$emit_signal_name(x._internal, from_julia_index(page_index)))\n            end\n        )))\n\n        disconnect_signal_name = :disconnect_signal_ * snake_case * :!\n\n        push!(out.args, esc(:(\n            function $disconnect_signal_name(x::$T)\n                detail.$disconnect_signal_name(x._internal)\n            end\n        )))\n\n        set_signal_blocked_name = :set_signal_ * snake_case * :_blocked * :!\n\n        push!(out.args, esc(:(\n            function $set_signal_blocked_name(x::$T, b)\n                detail.$set_signal_blocked_name(x._internal, b)\n            end\n        )))\n\n        get_signal_blocked_name = :get_signal_ * snake_case * :_blocked\n\n        push!(out.args, esc(:(\n            function $get_signal_blocked_name(x::$T)\n                return detail.$get_signal_blocked_name(x._internal)\n            end\n        )))\n\n        push!(out.args, esc(:(export $connect_signal_name)))\n        push!(out.args, esc(:(export $disconnect_signal_name)))\n        push!(out.args, esc(:(export $set_signal_blocked_name)))\n        push!(out.args, esc(:(export $get_signal_blocked_name)))\n        push!(out.args, esc(:(export $emit_signal_name)))\n\n        return out\n    end\n\n####### theme.jl\n\n    @export_enum Theme begin\n        THEME_DEFAULT_LIGHT\n        THEME_DEFAULT_DARK\n        THEME_HIGH_CONTRAST_LIGHT\n        THEME_HIGH_CONTRAST_DARK\n    end\n\n####### application.jl\n\n    @export_type Application SignalEmitter\n    @export_type Action SignalEmitter\n\n    const ApplicationID = String;\n    export ApplicationID\n\n    Application(id::String; allow_multiple_instances = false) = Application(detail._Application(id, allow_multiple_instances))\n\n    run!(app::Application) ::Cint = Mousetrap.detail.run!(app._internal)\n    export run!\n\n    @export_function Application quit! Cvoid\n    @export_function Application hold! Cvoid\n    @export_function Application release! Cvoid\n    @export_function Application get_is_holding Bool\n    @export_function Application mark_as_busy! Cvoid\n    @export_function Application unmark_as_busy! Cvoid\n    @export_function Application get_is_marked_as_busy Bool\n    @export_function Application get_id String\n    @export_function Application get_current_theme Theme\n    @export_function Application set_current_theme! Cvoid Theme theme\n\n    add_action!(app::Application, action::Action) = detail.add_action!(app._internal, action._internal)\n    export add_action!\n\n    get_action(app::Application, id::String) ::Action = Action(detail.get_action(app._internal, id))\n    export get_action\n\n    @export_function Application remove_action! Cvoid String id\n    @export_function Application has_action Bool String id\n\n    @add_signal_activate Application\n    @add_signal_shutdown Application\n\n    function main(f, application_id::String = \"com.julia.mousetrap\")\n        app = Application(application_id)\n        typed_f = TypedFunction(f, Any, (Application,))\n        connect_signal_activate!(app)  do app::Application\n            try\n                typed_f(app)\n            catch(exception)\n                printstyled(stderr, \"[ERROR] \"; bold = true, color = :red)\n                printstyled(stderr, \"In Mousetrap.main: \"; bold = true)\n                Base.showerror(stderr, exception, catch_backtrace())\n                print(stderr, \"\\n\")\n                quit!(app)\n            end\n            return nothing\n        end\n        return run!(app)\n    end\n    export main\n\n    Base.show(io::IO, x::Application) = show_aux(io, x, :is_holding, :is_marked_as_busy)\n\n####### window.jl\n\n    @export_enum WindowCloseRequestResult begin\n        WINDOW_CLOSE_REQUEST_RESULT_ALLOW_CLOSE\n        WINDOW_CLOSE_REQUEST_RESULT_PREVENT_CLOSE\n    end\n\n    @export_type Window Widget\n    @declare_native_widget Window\n\n    Window(app::Application) = Window(detail._Window(app._internal))\n\n    function set_application!(window::Window, app::Application)\n        detail.set_application!(window._internal, app._internal)\n    end\n    export set_application!\n\n    @export_function Window set_maximized! Cvoid Bool b\n    @export_function Window set_fullscreen! Cvoid Bool b\n    @export_function Window set_minimized! Cvoid Bool b\n    @export_function Window present! Cvoid\n    @export_function Window set_hide_on_close! Cvoid Bool b\n    @export_function Window get_hide_on_close Bool\n    @export_function Window close! Cvoid\n    @export_function Window destroy! Cvoid\n    @export_function Window get_is_closed Bool\n\n    function set_child!(window::Window, child::Widget)\n        detail.set_child!(window._internal, as_widget_pointer(child))\n    end\n    export set_child!\n\n    @export_function Window remove_child! Cvoid\n    @export_function Window set_destroy_with_parent! Cvoid Bool n\n    @export_function Window get_destroy_with_parent Bool\n    @export_function Window set_title! Cvoid String title\n    @export_function Window get_title String\n\n    function get_header_bar(window::Window) ::HeaderBar\n        return HeaderBar(detail.get_header_bar(window._internal))\n    end\n    export get_header_bar\n\n    @export_function Window set_is_modal! Cvoid Bool b\n    @export_function Window get_is_modal Bool\n\n    function set_transient_for!(self::Window, other::Window)\n        detail.set_transient_for!(self._internal, other._internal)\n    end\n    export set_transient_for!\n\n    @export_function Window set_is_decorated! Cvoid Bool b\n    @export_function Window get_is_decorated Bool\n    @export_function Window set_has_close_button! Cvoid Bool b\n    @export_function Window get_has_close_button Bool\n    @export_function Window set_startup_notification_identifier! Cvoid String id\n    @export_function Window set_focus_visible! Cvoid Bool b\n    @export_function Window get_focus_visible Bool\n\n    function set_default_widget!(window::Window, widget::Widget)\n        detail.set_default_widget!(window._internal, as_widget_pointer(widget))\n    end\n    export set_default_widget!\n\n    @add_widget_signals Window\n    @add_signal_close_request Window\n    @add_signal_activate_default_widget Window\n    @add_signal_activate_focused_widget Window\n\n    Base.show(io::IO, x::Window) = show_aux(io, x, :title)\n\n####### action.jl\n\n    const ShortcutTrigger = String\n    export ShortcutTrigger\n\n    Action(id::String, app::Application) = Action(detail._Action(id, app._internal.cpp_object))\n    function Action(f, id::String, app::Application)\n        out = Action(id, app)\n        set_function!(f, out)\n        return out\n    end\n\n    @export_function Action get_id String\n    @export_function Action activate! Cvoid\n    @export_function Action add_shortcut! Cvoid ShortcutTrigger shortcut\n\n    get_shortcuts(action::Action) ::Vector{String} = detail.get_shortcuts(action._internal)[]\n    export get_shortcuts\n\n    @export_function Action clear_shortcuts! Cvoid\n    @export_function Action set_enabled! Cvoid Bool b\n    @export_function Action get_enabled Bool\n\n    function set_function!(f, action::Action, data::Data_t) where Data_t\n        typed_f = TypedFunction(f, Cvoid, (Action, Data_t))\n        detail.set_function!(action._internal, function (internal_ref)\n            typed_f(Action(internal_ref[]), data)\n        end)\n    end\n\n    function set_function!(f, action::Action)\n        typed_f = TypedFunction(f, Cvoid, (Action,))\n        detail.set_function!(action._internal, function (internal_ref)\n            typed_f(Action(internal_ref[]))\n        end)\n    end\n    export set_function!\n\n    @add_signal_activated Action\n\n    Base.show(io::IO, x::Action) = show_aux(io, x, :id, :enabled)\n\n####### adjustment.jl\n\n    @export_type Adjustment SignalEmitter\n\n    function Adjustment(value::Number, lower::Number, upper::Number, increment::Number)\n        return Adjustment(detail._Adjustment(\n            convert(Cfloat, value),\n            convert(Cfloat, lower),\n            convert(Cfloat, upper),\n            convert(Cfloat, increment)\n        ));\n    end\n\n    Adjustment(internal::Ptr{Cvoid}) = Adjustment(detail._Adjustment(internal))\n\n    @export_function Adjustment get_lower Cfloat\n    @export_function Adjustment get_upper Cfloat\n    @export_function Adjustment get_value Cfloat\n    @export_function Adjustment get_step_increment Cfloat\n\n    @export_function Adjustment set_lower! Cvoid Number => Cfloat value\n    @export_function Adjustment set_upper! Cvoid Number => Cfloat value\n    @export_function Adjustment set_value! Cvoid Number => Cfloat value\n    @export_function Adjustment set_step_increment! Cvoid Number => Cfloat value\n\n    @add_signal_value_changed Adjustment\n    @add_signal_properties_changed Adjustment\n\n    Base.show(io::IO, x::Adjustment) = show_aux(io, x, :value, :lower, :upper, :step_increment)\n\n####### alignment.jl\n\n    @export_enum Alignment begin\n        ALIGNMENT_START\n        ALIGNMENT_CENTER\n        ALIGNMENT_END\n    end\n\n####### orientation.jl\n\n    @export_enum Orientation begin\n        ORIENTATION_HORIZONTAL\n        ORIENTATION_VERTICAL\n    end\n\n####### cursor_type.jl\n\n    @export_enum CursorType begin\n        CURSOR_TYPE_NONE\n        CURSOR_TYPE_DEFAULT\n        CURSOR_TYPE_HELP\n        CURSOR_TYPE_POINTER\n        CURSOR_TYPE_CONTEXT_MENU\n        CURSOR_TYPE_PROGRESS\n        CURSOR_TYPE_WAIT\n        CURSOR_TYPE_CELL\n        CURSOR_TYPE_CROSSHAIR\n        CURSOR_TYPE_TEXT\n        CURSOR_TYPE_MOVE\n        CURSOR_TYPE_NOT_ALLOWED\n        CURSOR_TYPE_GRAB\n        CURSOR_TYPE_GRABBING\n        CURSOR_TYPE_ALL_SCROLL\n        CURSOR_TYPE_ZOOM_IN\n        CURSOR_TYPE_ZOOM_OUT\n        CURSOR_TYPE_COLUMN_RESIZE\n        CURSOR_TYPE_ROW_RESIZE\n        CURSOR_TYPE_NORTH_RESIZE\n        CURSOR_TYPE_NORTH_EAST_RESIZE\n        CURSOR_TYPE_EAST_RESIZE\n        CURSOR_TYPE_SOUTH_EAST_RESIZE\n        CURSOR_TYPE_SOUTH_RESIZE\n        CURSOR_TYPE_SOUTH_WEST_RESIZE\n        CURSOR_TYPE_WEST_RESIZE\n        CURSOR_TYPE_NORTH_WEST_RESIZE\n    end\n\n    const CURSOR_TYPE_HORIZONTAL_RESIZE = CURSOR_TYPE_ROW_RESIZE\n    const CURSOR_TYPE_VERTICAL_RESIZE = CURSOR_TYPE_COLUMN_RESIZE\n\n####### color.jl\n\n    abstract type Color end\n\n    mutable struct RGBA <: Color\n        r::Cfloat\n        g::Cfloat\n        b::Cfloat\n        a::Cfloat\n    end\n    export RGBA\n\n    function RGBA(r::AbstractFloat, g::AbstractFloat, b::AbstractFloat, a::AbstractFloat)\n        return RBGA(\n            convert(Cfloat, r),\n            convert(Cfloat, g),\n            convert(Cfloat, b),\n            convert(Cfloat, a)\n        )\n    end\n\n    mutable struct HSVA <: Color\n        h::Cfloat\n        s::Cfloat\n        v::Cfloat\n        a::Cfloat\n    end\n    export HSVA\n\n    function HSVA(h::AbstractFloat, s::AbstractFloat, v::AbstractFloat, a::AbstractFloat)\n        return HSVA(\n            convert(Cfloat, h),\n            convert(Cfloat, s),\n            convert(Cfloat, v),\n            convert(Cfloat, a)\n        )\n    end\n\n    import Base.==\n    ==(x::RGBA, y::RGBA) = x.r == y.r && x.g == y.g && x.b == y.b && x.a == y.a\n    function ==(x::HSVA, y::HSVA)\n        hue_equal = x.h == y.h || abs(x.h - y.h) == 1\n        return hue_equal && x.s == y.s && x.v == y.v && x.a == y.a\n    end\n\n    import Base.!=\n    !=(x::RGBA, y::RGBA) = !(x == y)\n    !=(x::HSVA, y::HSVA) = !(x == y)\n\n    rgba_to_hsva(rgba::RGBA) ::HSVA = detail.rgba_to_hsva(rgba)\n    export rgba_to_hsva\n\n    hsva_to_rgba(hsva::HSVA) ::RGBA = detail.hsva_to_rgba(hsva)\n    export hsva_to_rgba\n\n    Base.convert(::Type{HSVA}, rgba::RGBA) = rgba_to_hsva(rbga)\n    Base.convert(::Type{RGBA}, hsva::HSVA) = hsva_to_rgba(hsva)\n\n    rgba_to_html_code(rgba::RGBA) = convert(String, detail.rgba_to_html_code(rgba))\n    export rgba_to_html_code\n\n    html_code_to_rgba(code::String) ::RGBA = detail.html_code_to_rgba(code)\n    export html_code_to_rgba\n\n    is_valid_html_code(code::String) ::Bool = detail.is_valid_html_code(code)\n    export is_valid_html_code\n\n    Base.show(io::IO, x::RGBA) = print(io, \"RGBA($(x.r), $(x.g), $(x.b), $(x.a))\")\n    Base.show(io::IO, x::HSVA) = print(io, \"HSVA($(x.h), $(x.s), $(x.v), $(x.a))\")\n\n####### icon.jl\n\n    @export_type IconTheme\n    IconTheme(window::Window) = IconTheme(detail._IconTheme(window._internal))\n\n    const IconID = String\n    export IconID\n\n    @export_type Icon\n    Icon() = Icon(detail._Icon())\n\n    function Icon(path::String)\n        out = Icon()\n        create_from_file!(out, path)\n        return out\n    end\n\n    function Icon(theme::IconTheme, id::IconID, square_resolution::Integer)\n        out = Icon()\n        create_from_theme!(out, theme, id, square_resolution)\n        return out\n    end\n\n    # Icon\n\n    @export_function Icon create_from_file! Bool String path\n\n    function create_from_theme!(icon::Icon, theme::IconTheme, id::IconID, square_resolution::Integer, scale::Integer = 1) ::Bool\n        detail.create_from_theme!(icon._internal, theme._internal.cpp_object, id, UInt64(square_resolution), UInt64(scale))\n    end\n    export create_from_theme!\n\n    @export_function Icon get_name IconID\n\n    import Base.==\n    ==(a::Icon, b::Icon) = detail.compare_icons(a._internal, b_internal)\n\n    import Base.!=\n    !=(a::Icon, b::Icon) = !(a == b)\n\n    @export_function Icon get_size Vector2i\n\n    Base.show(io::IO, x::Icon) = show_aux(io, x, :name)\n\n    # IconTheme\n\n    function get_icon_names(theme::IconTheme) ::Vector{String}\n        return detail.get_icon_names(theme._internal)\n    end\n    export get_icon_names\n\n    has_icon(theme::IconTheme, icon::Icon) = detail.has_icon_icon(theme._internal, icon._internal)\n    has_icon(theme::IconTheme, id::IconID) = detail.has_icon_id(theme._internal, id)\n    export has_icon\n\n    @export_function IconTheme add_resource_path! Cvoid String path\n    @export_function IconTheme set_resource_path! Cvoid String path\n\n    Base.show(io::IO, x::IconTheme) = show_aux(io, x)\n\n####### image.jl\n\n    @export_enum InterpolationType begin\n        INTERPOLATION_TYPE_NEAREST\n        INTERPOLATION_TYPE_TILES\n        INTERPOLATION_TYPE_BILINEAR\n        INTERPOLATION_TYPE_HYPERBOLIC\n    end\n\n    @export_type Image\n    Image() = Image(detail._Image())\n    Image(path::String) = Image(detail._Image(path))\n    Image(width::Integer, height::Integer, color::RGBA = RGBA(0, 0, 0, 1)) = Image(detail._Image(UInt64(width), UInt64(height), color))\n\n    function create!(image::Image, width::Integer, height::Integer, color::RGBA = RGBA(0, 0, 0, 1))\n        detail.create!(image._internal, UInt64(width), UInt64(height), color)\n    end\n    export create!\n\n    @export_function Image create_from_file! Bool String path\n    @export_function Image save_to_file Bool String path\n    @export_function Image get_n_pixels Int64\n    @export_function Image get_size Vector2i\n\n    function as_scaled(image::Image, size_x::Integer, size_y::Integer, interpolation::InterpolationType)\n        return Image(detail.as_scaled(image._internal, UInt64(size_x), UInt64(size_y), interpolation))\n    end\n    export as_scaled\n\n    function as_cropped(image::Image, offset_x::Signed, offset_y::Signed, new_width::Integer, new_height::Integer)\n        return Image(detail.as_cropped(image._internal, offset_x, offset_y, UInt64(new_width), UInt64(new_height)))\n    end\n    export as_cropped\n\n    function as_flipped(image::Image, flip_horizontally::Bool, flip_vertically::Bool)\n        return Image(detail.as_flipped(image._internal, flip_horizontally, flip_vertically))\n    end\n    export as_flipped\n\n    function set_pixel!(image::Image, x::Integer, y::Integer, color::RGBA)\n        detail.set_pixel_rgba!(image._internal, from_julia_index(x), from_julia_index(y), color)\n    end\n    function set_pixel!(image::Image, x::Integer, y::Integer, color::HSVA)\n        detail.set_pixel_hsva!(image._internal, from_julia_index(x), from_julia_index(y), color)\n    end\n    export set_pixel!\n\n    function get_pixel(image::Image, x::Integer, y::Integer) ::RGBA\n        return detail.get_pixel(image._internal, from_julia_index(x), from_julia_index(y))\n    end\n    export get_pixel\n\n    Base.show(io::IO, x::Image) = show_aux(io, x, :size)\n\n####### key_file.jl\n\n    @export_type KeyFile SignalEmitter\n    KeyFile() = KeyFile(detail._KeyFile())\n    KeyFile(path::String) = KeyFile(detail._KeyFile(path))\n\n    const GroupID = String\n    export GroupID\n\n    const KeyID = String\n    export KeyID\n\n    @export_function KeyFile as_string String\n    @export_function KeyFile create_from_file! Bool String path\n    @export_function KeyFile create_from_string! Bool String file\n    @export_function KeyFile save_to_file Bool String path\n    @export_function KeyFile get_groups Vector{GroupID}\n    @export_function KeyFile get_keys Vector{KeyID} GroupID group\n    @export_function KeyFile has_key Bool GroupID group KeyID key\n    @export_function KeyFile has_group Bool GroupID group\n\n    set_comment_above!(file::KeyFile, group::GroupID, key::KeyID, comment::String) = detail.set_comment_above_key!(file._internal, group, key, comment)\n    set_comment_above!(file::KeyFile, group::GroupID, comment::String) = detail.set_comment_above_group!(file._internal, group, comment)\n    export set_comment_above!\n\n    get_comment_above(file::KeyFile, group::GroupID) ::String = detail.get_comment_above_group(file._internal, group)\n    get_comment_above(file::KeyFile, group::GroupID, key::KeyID) ::String = detail.get_comment_above_key(file._internal, group, key)\n    export get_comment_above\n\n    export set_value!\n    export get_value\n\n    function set_value!(file::KeyFile, group::GroupID, key::KeyID, value::Bool)\n        detail.set_value_as_bool!(file._internal, group, key, value)\n    end\n\n    function set_value!(file::KeyFile, group::GroupID, key::KeyID, value::AbstractFloat)\n        detail.set_value_as_double!(file._internal, group, key, convert(Cdouble, value))\n    end\n\n    function set_value!(file::KeyFile, group::GroupID, key::KeyID, value::Signed)\n        detail.set_value_as_int!(file._internal, group, key, convert(Cint, value))\n    end\n\n    function set_value!(file::KeyFile, group::GroupID, key::KeyID, value::Unsigned)\n        detail.set_value_as_uint!(file._internal, group, key, convert(Cuint, value))\n    end\n\n    function set_value!(file::KeyFile, group::GroupID, key::KeyID, value::String)\n        detail.set_value_as_string!(file._internal, group, key, value)\n    end\n\n    function set_value!(file::KeyFile, group::GroupID, key::KeyID, value::RGBA)\n        detail.set_value_as_rgba!(file._internal, group, key, value)\n    end\n\n    function set_value!(file::KeyFile, group::GroupID, key::KeyID, value::HSVA)\n        detail.set_value_as_hsva!(file._internal, group, key, value)\n    end\n\n    function set_value!(file::KeyFile, group::GroupID, key::KeyID, value::Image)\n       detail.set_value_as_image!(file._internal, group, key, value._internal)\n    end\n\n    function set_value!(file::KeyFile, group::GroupID, key::KeyID, value::Vector{Bool})\n        detail.set_value_as_bool_list!(file._internal, group, key, value)\n    end\n\n    function set_value!(file::KeyFile, group::GroupID, key::KeyID, value::Vector{<: AbstractFloat})\n        vec::Vector{Cdouble} = []\n        for x in value\n            push!(vec, convert(Cdouble, x))\n        end\n        detail.set_value_as_double_list!(file._internal, group, key, vec)\n    end\n\n    function set_value!(file::KeyFile, group::GroupID, key::KeyID, value::Vector{<: Signed})\n        vec::Vector{Cint} = []\n        for x in value\n            push!(vec, convert(Cint, x))\n        end\n        detail.set_value_as_int_list!(file._internal, group, key, vec)\n    end\n\n    function set_value!(file::KeyFile, group::GroupID, key::KeyID, value::Vector{<: Unsigned})\n        vec::Vector{Cuint} = []\n        for x in value\n            push!(vec, convert(Cuint, x))\n        end\n        detail.set_value_as_uint_list!(file._internal, group, key, vec)\n    end\n\n    function set_value!(file::KeyFile, group::GroupID, key::KeyID, value::Vector{String})\n        detail.set_value_as_string_list!(file._internal, group, key, value)\n    end\n\n    ##\n\n    function get_value(file::KeyFile, group::GroupID, key::KeyID, ::Type{Bool})\n        return detail.get_value_as_bool(file._internal, group, key)\n    end\n\n    function get_value(file::KeyFile, group::GroupID, key::KeyID, ::Type{<: AbstractFloat})\n        return detail.get_value_as_double(file._internal, group, key)\n    end\n\n    function get_value(file::KeyFile, group::GroupID, key::KeyID, ::Type{<: Signed})\n        return detail.get_value_as_int(file._internal, group, key)\n    end\n\n    function get_value(file::KeyFile, group::GroupID, key::KeyID, ::Type{<: Unsigned})\n        return detail.get_value_as_uint(file._internal, group, key)\n    end\n\n    function get_value(file::KeyFile, group::GroupID, key::KeyID, ::Type{String})\n        return detail.get_value_as_string(file._internal, group, key)\n    end\n\n    function get_value(file::KeyFile, group::GroupID, key::KeyID, ::Type{RGBA})\n        return detail.get_value_as_rgba(file._internal, group, key)\n    end\n\n    function get_value(file::KeyFile, group::GroupID, key::KeyID, ::Type{HSVA})\n        return detail.get_value_as_hsva(file._internal, group, key)\n    end\n\n    function get_value(file::KeyFile, group::GroupID, key::KeyID, ::Type{Image})\n        return Image(detail.get_value_as_image(file._internal, group, key))\n    end\n\n    function get_value(file::KeyFile, group::GroupID, key::KeyID, ::Type{Vector{Bool}})\n        return convert(Vector{Bool}, detail.get_value_as_bool_list(file._internal, group, key))\n    end\n\n    function get_value(file::KeyFile, group::GroupID, key::KeyID, ::Type{Vector{T}}) where T <: AbstractFloat\n        return convert(Vector{T}, detail.get_value_as_double_list(file._internal, group, key))\n    end\n\n    function get_value(file::KeyFile, group::GroupID, key::KeyID, ::Type{Vector{T}}) where T <: Signed\n        return convert(Vector{T}, detail.get_value_as_int_list(file._internal, group, key))\n    end\n\n    function get_value(file::KeyFile, group::GroupID, key::KeyID, ::Type{Vector{T}}) where T <: Unsigned\n        return convert(Vector{T}, detail.get_value_as_uint_list(file._internal, group, key))\n    end\n\n    function get_value(file::KeyFile, group::GroupID, key::KeyID, ::Type{Vector{String}})\n        return convert(Vector{String}, detail.get_value_as_string_list(file._internal, group, key))\n    end\n\n    Base.show(io::IO, x::KeyFile) = show_aux(io, x, :groups)\n\n####### file_descriptor.jl\n\n    @export_type FileMonitor SignalEmitter\n    @export_type FileDescriptor SignalEmitter\n\n    # Monitor\n\n    @export_enum FileMonitorEvent begin\n        FILE_MONITOR_EVENT_CHANGED\n        FILE_MONITOR_EVENT_CHANGES_DONE_HINT\n        FILE_MONITOR_EVENT_DELETED\n        FILE_MONITOR_EVENT_CREATED\n        FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED\n        FILE_MONITOR_EVENT_RENAMED\n        FILE_MONITOR_EVENT_MOVED_IN\n        FILE_MONITOR_EVENT_MOVED_OUT\n    end\n\n    @export_function FileMonitor cancel! Cvoid\n    @export_function FileMonitor is_cancelled Bool\n\n    function on_file_changed!(f, monitor::FileMonitor, data::Data_t) where Data_t\n        typed_f = TypedFunction(f, Cvoid, (FileMonitor, FileMonitorEvent, FileDescriptor, FileDescriptor, Data_t))\n        detail.on_file_changed!(monitor._internal, function(monitor_ref, event, self_descriptor_internal::Ptr{Cvoid}, other_descriptor_internal::Ptr{Cvoid})\n            typed_f(FileMonitor(monitor_ref[]), event, FileDescriptor(self_descriptor_internal), FileDescriptor(other_descriptor_internal), data)\n        end)\n    end\n    function on_file_changed!(f, monitor::FileMonitor)\n        typed_f = TypedFunction(f, Cvoid, (FileMonitor, FileMonitorEvent, FileDescriptor, FileDescriptor))\n        detail.on_file_changed!(monitor._internal, function(monitor_ref, event, self_descriptor_internal::Ptr{Cvoid}, other_descriptor_internal::Ptr{Cvoid})\n            typed_f(FileMonitor(monitor_ref[]), event, FileDescriptor(self_descriptor_internal), FileDescriptor(other_descriptor_internal))\n        end)\n    end\n    export on_file_changed!\n\n    Base.show(io::IO, x::FileMonitor) = print(io, \"FileMonitor(cancelled = $(is_cancelled(x)))\")\n\n    # Descriptor\n\n    FileDescriptor(internal::Ptr{Cvoid}) = FileDescriptor(detail._FileDescriptor(internal))\n    FileDescriptor(path::String) = FileDescriptor(detail._FileDescriptor(path))\n\n    import Base.==\n    ==(a::FileDescriptor, b::FileDescriptor) = detail.file_descriptor_equal(a._internal, b._internal)\n\n    import Base.!=\n    !=(a::FileDescriptor, b::FileDescriptor) = !(a == b)\n\n    @export_function FileDescriptor create_from_path! Bool String path\n    @export_function FileDescriptor create_from_uri! Bool String uri\n    @export_function FileDescriptor get_name String\n    @export_function FileDescriptor get_path String\n    @export_function FileDescriptor get_uri String\n\n    get_path_relative_to(self::FileDescriptor, other::FileDescriptor) = detail.get_path_relative_to(self._internal, other._internal)\n    export get_path_relative_to\n\n    get_parent(self::FileDescriptor) = FileDescriptor(detail.get_parent(self._internal))\n    export get_parent\n\n    @export_function FileDescriptor get_file_extension String\n    @export_function FileDescriptor exists Bool\n    @export_function FileDescriptor is_folder Bool\n    @export_function FileDescriptor is_file Bool\n    @export_function FileDescriptor is_symlink Bool\n\n    read_symlink(self::FileDescriptor) = FileDescriptor(detail.read_symlink(self._internal))\n    export read_symlink\n\n    @export_function FileDescriptor is_executable Bool\n    @export_function FileDescriptor get_content_type String\n    @export_function FileDescriptor query_info String String info_id\n\n    create_monitor(descriptor::FileDescriptor) ::FileMonitor = FileMonitor(detail._FileMonitor(detail.create_monitor(descriptor._internal)))\n    export create_monitor\n\n    function get_children(descriptor::FileDescriptor; recursive::Bool = false) ::Vector{FileDescriptor}\n        children::Vector{Ptr{Cvoid}} = detail.get_children(descriptor._internal, recursive)\n        return FileDescriptor[FileDescriptor(ptr) for ptr in children]\n    end\n    export get_children\n\n    # File System\n\n    create_file_at!(destination::FileDescriptor; replace::Bool = false) ::Bool = detail.create_file_at!(destination._internal, replace)\n    export create_file_at!\n\n    create_directory_at!(destination::FileDescriptor) ::Bool = detail.create_directory_at!(destination._internal)\n    export create_directory_at!\n\n    delete_at!(file::FileDescriptor) ::Bool = detail.delete_at!(file._internal)\n    export delete_at!\n\n    function copy!(from::FileDescriptor, to::FileDescriptor, allow_overwrite::Bool; make_backup::Bool = false, follow_symlink::Bool = false) ::Bool\n        detail.copy!(from._internal, to._internal, allow_overwrite, make_backup, follow_symlink)\n    end\n    export copy!\n\n    function move!(from::FileDescriptor, to::FileDescriptor, allow_overwrite::Bool; make_backup::Bool = false, follow_symlink::Bool = false) ::Bool\n        detail.move!(from._internal, to._internal, allow_overwrite, make_backup, follow_symlink)\n    end\n    export move!\n\n    move_to_trash!(file::FileDescriptor) ::Bool = detail.move_to_trash!(file._internal)\n    export move_to_trash!\n\n    open_file(file::FileDescriptor) ::Cvoid = detail.open_file(file._internal)\n    export open_file\n\n    show_in_file_explorer(file::FileDescriptor) ::Cvoid = detail.show_in_file_explorer(file._internal)\n    export show_in_file_explorer\n\n    open_url(file::FileDescriptor) ::Cvoid = detail.open_url(file._internal)\n    export open_url\n\n    Base.show(io::IO, x::FileDescriptor) = show_aux(io, x, :path)\n\n####### file_chooser.jl\n\n    @export_type FileFilter SignalEmitter\n    FileFilter(name::String) = FileFilter(detail._FileFilter(name))\n\n    get_name(filter::FileFilter) ::String = detail.get_name(filter._internal)\n    export get_name\n\n    @export_function FileFilter add_allowed_pattern! Cvoid String pattern\n    @export_function FileFilter add_allow_all_supported_image_formats! Cvoid\n    @export_function FileFilter add_allowed_suffix! Cvoid String suffix\n    @export_function FileFilter add_allowed_mime_type! Cvoid String mime_type_id\n\n    @export_enum FileChooserAction begin\n        FILE_CHOOSER_ACTION_OPEN_FILE\n        FILE_CHOOSER_ACTION_OPEN_MULTIPLE_FILES\n        FILE_CHOOSER_ACTION_SAVE\n        FILE_CHOOSER_ACTION_SELECT_FOLDER\n        FILE_CHOOSER_ACTION_SELECT_MULTIPLE_FOLDERS\n    end\n\n    @export_type FileChooser SignalEmitter\n    FileChooser(action::FileChooserAction = FILE_CHOOSER_ACTION_OPEN_FILE, title::String = \"\") = FileChooser(detail._FileChooser(action, title))\n\n    @export_function FileChooser set_accept_label! Cvoid String label\n    @export_function FileChooser get_accept_label String\n    @export_function FileChooser present! Cvoid\n    @export_function FileChooser cancel! Cvoid\n    @export_function FileChooser set_is_modal! Cvoid Bool modal\n    @export_function FileChooser get_is_modal Bool\n    @export_function FileChooser set_title! Cvoid String title\n    @export_function FileChooser get_title String\n\n    function on_accept!(f, chooser::FileChooser, data::Data_t) where Data_t\n        typed_f = TypedFunction(f, Cvoid, (FileChooser, Vector{FileDescriptor}, Data_t))\n        detail.on_accept!(chooser._internal, function(file_chooser_ref, descriptor_ptrs::Vector{Ptr{Cvoid}})\n            descriptors = FileDescriptor[FileDescriptor(ptr) for ptr in descriptor_ptrs]\n            typed_f(FileChooser(file_chooser_ref[]), descriptors, data)\n        end)\n    end\n    function on_accept!(f, chooser::FileChooser)\n        typed_f = TypedFunction(f, Cvoid, (FileChooser, Vector{FileDescriptor}))\n        detail.on_accept!(chooser._internal, function(file_chooser_ref, descriptor_ptrs::Vector{Ptr{Cvoid}})\n            descriptors = FileDescriptor[FileDescriptor(ptr) for ptr in descriptor_ptrs]\n            typed_f(FileChooser(file_chooser_ref[]), descriptors)\n        end)\n    end\n    export on_accept!\n\n    function on_cancel!(f, chooser::FileChooser, data::Data_t) where Data_t\n        typed_f = TypedFunction(f, Cvoid, (FileChooser, Data_t))\n        detail.on_cancel!(chooser._internal, function(file_chooser_ref)\n            typed_f(FileChooser(file_chooser_ref[]), data)\n        end)\n    end\n    function on_cancel!(f, chooser::FileChooser)\n        typed_f = TypedFunction(f, Cvoid, (FileChooser,))\n        detail.on_cancel!(chooser._internal, function(file_chooser_ref)\n            typed_f(FileChooser(file_chooser_ref[]))\n        end)\n    end\n    export on_cancel!\n\n    @export_function FileChooser set_file_chooser_action! Cvoid FileChooserAction action\n    @export_function FileChooser get_file_chooser_action FileChooserAction\n\n    add_filter!(chooser::FileChooser, filter::FileFilter) = detail.add_filter!(chooser._internal, filter._internal)\n    export add_filter!\n\n    clear_filters!(chooser::FileChooser) = detail.clear_filters!(chooser._internal)\n    export clear_filters!\n\n    set_initial_filter!(chooser::FileChooser, filter::FileFilter) = detail.set_initial_filter!(chooser._internal, filter._internal)\n    export set_initial_filter!\n\n    set_initial_file!(chooser::FileChooser, file::FileDescriptor) = detail.set_initial_file!(chooser._internal, file._internal)\n    export set_initial_file!\n\n    set_initial_folder!(chooser::FileChooser, folder::FileDescriptor) = detail.set_initial_file!(chooser._internal, folder._internal)\n    export set_initial_folder!\n\n    set_initial_name!(chooser::FileChooser, name::String) = detail.set_initial_name!(chooser._internal, name)\n    export set_initial_name!\n\n    Base.show(io::IO, x::FileChooser) = show_aux(io, x)\n\n####### alert_dialog.jl\n\n    @export_type AlertDialog SignalEmitter\n    AlertDialog(message::String, detailed_message::String = \"\") = AlertDialog(detail._AlertDialog(message, detailed_message))\n\n    function add_button!(alert_dialog::AlertDialog, label::String) ::Integer\n        return to_julia_index(detail.add_button!(alert_dialog._internal, label))\n    end\n    export add_button!\n\n    function set_default_button!(alert_dialog::AlertDialog, id::Integer)\n        detail.set_default_button!(alert_dialog.AlertDialog, from_julia_index(id))\n    end\n    export set_default_button!\n\n    function set_button_label!(alert_dialog::AlertDialog, id::Integer, label::String)\n        detail.set_button_label!(alert_dialog._internal, from_julia_index(id), label)\n    end\n    export set_button_label!\n\n    function get_button_label(alert_dialog::AlertDialog, id::Integer) ::String\n        return detail.get_button_label(alert_dialog._internal, from_julia_index(id))\n    end\n    export get_button_label\n\n    function set_extra_widget!(alert_dialog::AlertDialog, widget::Widget)\n        detail.set_extra_widget!(alert_dialog._internal, as_widget_pointer(widget))\n    end\n    export set_extra_widget!\n\n    @export_function AlertDialog remove_extra_widget! Cvoid\n    @export_function AlertDialog get_n_buttons Integer\n    @export_function AlertDialog get_message String\n    @export_function AlertDialog set_message! Cvoid String message\n    @export_function AlertDialog get_detailed_description String\n    @export_function AlertDialog set_detailed_description! Cvoid String message\n    @export_function AlertDialog set_is_modal! Cvoid Bool b\n    @export_function AlertDialog get_is_modal Bool\n    @export_function AlertDialog present! Cvoid\n    @export_function AlertDialog close! Cvoid\n\n    function on_selection!(f, dialog::AlertDialog, data::Data_t) where Data_t\n        typed_f = TypedFunction(f, Cvoid, (AlertDialog, Integer, Data_t))\n        detail.on_selection!(dialog._internal, function(dialog_ref, index)\n        typed_f(AlertDialog(dialog_ref[]), convert(UInt32, to_julia_index(index)), data)\n        end)\n    end\n    function on_selection!(f, dialog::AlertDialog)\n        typed_f = TypedFunction(f, Cvoid, (AlertDialog, Signed))\n        detail.on_selection!(dialog._internal, function(dialog_ref, index)\n            typed_f(AlertDialog(dialog_ref[]), convert(UInt32, to_julia_index(index)))\n        end)\n    end\n    export on_selection!\n\n    Base.show(io::IO, x::AlertDialog) = show_aux(io, x)\n\n####### popup_message.jl\n\n    @export_type PopupMessage SignalEmitter\n    PopupMessage(title::String) = PopupMessage(detail._PopupMessage(title, \"\"))\n    PopupMessage(title::String, button_label::String) = PopupMessage(detail._PopupMessage(title, button_label))\n\n    @export_function PopupMessage set_title! Cvoid String title\n    @export_function PopupMessage get_title String\n    @export_function PopupMessage set_button_label! Cvoid String title\n    @export_function PopupMessage get_button_label String\n\n    set_button_action!(popup_message::PopupMessage, action::Action) = detail.set_button_action!(popup_message._internal, action._internal)\n    export set_button_action!\n\n    @export_function PopupMessage get_button_action_id String\n    @export_function PopupMessage set_is_high_priority! Cvoid Bool b\n    @export_function PopupMessage get_is_high_priority Bool\n\n    set_timeout!(popup_message::PopupMessage, duration::Time) = detail.set_timeout!(popup_message._internal, convert(Cfloat, as_microseconds(duration)))\n    export set_timeout!\n\n    get_timeout(popup_message::PopupMessage) ::Time = microseconds(detail.get_timeout(popup_message._internal))\n    export get_timeout\n\n    @add_signal_dismissed PopupMessage\n    @add_signal_button_clicked PopupMessage\n\n    Base.show(io::IO, x::PopupMessage) = show_aux(io, x, :title, :button_label, :is_high_priority, :timeout)\n\n####### popup_message_overlay.jl\n\n    @export_type PopupMessageOverlay Widget\n    @declare_native_widget PopupMessageOverlay\n\n    PopupMessageOverlay() = PopupMessageOverlay(detail._PopupMessageOverlay())\n\n    function set_child!(overlay::PopupMessageOverlay, child::Widget)\n        detail.set_child!(overlay._internal, as_widget_pointer(child))\n    end\n    export set_child!\n\n    @export_function PopupMessageOverlay remove_child! Cvoid\n\n    function show_message!(overlay::PopupMessageOverlay, popup_message::PopupMessage)\n        detail.show_message!(overlay._internal, popup_message._internal)\n    end\n    export show_message!\n\n    @add_widget_signals PopupMessageOverlay\n\n    Base.show(io::IO, x::PopupMessageOverlay) = show_aux(io, x)\n\n####### color_chooser.jl\n\n    if detail.GTK_MINOR_VERSION >= 10\n\n    @export_type ColorChooser SignalEmitter\n    ColorChooser(title::String = \"\") = ColorChooser(detail._ColorChooser(title))\n\n    get_color(color_chooser::ColorChooser) ::RGBA = detail.get_color(color_chooser._internal)\n    export get_color\n\n    present!(color_chooser::ColorChooser) = detail.present!(color_chooser._internal)\n    export present!\n\n    @export_function ColorChooser set_is_modal! Cvoid Bool b\n    @export_function ColorChooser get_is_modal Bool\n    @export_function ColorChooser set_title! Cvoid String title\n    @export_function ColorChooser get_title String\n\n    function on_accept!(f, chooser::ColorChooser, data::Data_t) where Data_t\n        typed_f = TypedFunction(f, Cvoid, (ColorChooser, RGBA, Data_t))\n        detail.on_accept!(chooser._internal, function(color_chooser_ref, rgba::RGBA)\n            typed_f(ColorChooser(color_chooser_ref[]), rgba, data)\n        end)\n    end\n    function on_accept!(f, chooser::ColorChooser)\n        typed_f = TypedFunction(f, Cvoid, (ColorChooser, RGBA))\n        detail.on_accept!(chooser._internal, function(color_chooser_ref, rgba::RGBA)\n            typed_f(ColorChooser(color_chooser_ref[]), rgba)\n        end)\n    end\n    export on_accept!\n\n    function on_cancel!(f, chooser::ColorChooser, data::Data_t) where Data_t\n        typed_f = TypedFunction(f, Cvoid, (ColorChooser, Data_t))\n        detail.on_cancel!(chooser._internal, function(color_chooser_ref)\n            typed_f(ColorChooser(color_chooser_ref[]), data)\n        end)\n    end\n    function on_cancel!(f, chooser::ColorChooser)\n        typed_f = TypedFunction(f, Cvoid, (ColorChooser,))\n        detail.on_cancel!(chooser._internal, function(color_chooser_ref)\n            typed_f(ColorChooser(color_chooser_ref[]))\n        end)\n    end\n    export on_cancel!\n\n    Base.show(io::IO, x::ColorChooser) = show_aux(io, x, :color)\n\n    end # GTK_MINOR_VERSION >= 10\n\n####### image_display.jl\n\n    @export_type ImageDisplay Widget\n    @declare_native_widget ImageDisplay\n\n    ImageDisplay() = ImageDisplay(detail._ImageDisplay())\n    ImageDisplay(path::String) = ImageDisplay(detail._ImageDisplay(path))\n    ImageDisplay(image::Image) = ImageDisplay(detail._ImageDisplay(image._internal))\n    ImageDisplay(icon::Icon) = ImageDisplay(detail._ImageDisplay(icon._internal))\n\n    @export_function ImageDisplay create_from_file! Bool String path\n\n    create_from_image!(image_display::ImageDisplay, image::Image) = detail.create_from_image!(image_display._internal, image._internal)\n    export create_from_image!\n\n    create_from_icon!(image_display::ImageDisplay, icon::Icon) = detail.create_from_icon!(image_display._internal, icon._internal)\n    export create_from_icon!\n\n    create_as_file_preview!(image_display::ImageDisplay, file::FileDescriptor) ::Bool = detail.create_as_file_preview!(image_display._internal, file._internal)\n    export create_as_file_preview!\n\n    @export_function ImageDisplay clear! Cvoid\n    @export_function ImageDisplay set_scale! Cvoid Integer scale\n    @export_function ImageDisplay get_scale Int64\n    @export_function ImageDisplay get_size Vector2i\n\n    @add_widget_signals ImageDisplay\n\n    Base.show(io::IO, x::ImageDisplay) = show_aux(io, x, :size)\n\n####### aspect_frame.jl\n\n    @export_type AspectFrame Widget\n    @declare_native_widget AspectFrame\n\n    function AspectFrame(ratio::Number; child_x_alignment::AbstractFloat = 0.5, child_y_alignment::AbstractFloat = 0.5)\n        return AspectFrame(detail._AspectFrame(convert(Cfloat, ratio), convert(Cfloat, child_x_alignment), convert(Cfloat, child_y_alignment)))\n    end\n\n    function AspectFrame(ratio::Number, child::Widget)\n        out = AspectFrame(ratio)\n        set_child!(out, child)\n        return out\n    end\n\n    @export_function AspectFrame set_ratio! Cvoid AbstractFloat => Cfloat ratio\n    @export_function AspectFrame get_ratio Cfloat\n    @export_function AspectFrame set_child_x_alignment! Cvoid AbstractFloat => Cfloat alignment\n    @export_function AspectFrame set_child_y_alignment! Cvoid AbstractFloat => Cfloat alignment\n    @export_function AspectFrame get_child_x_alignment Cfloat\n    @export_function AspectFrame get_child_y_alignment Cfloat\n\n    @export_function AspectFrame remove_child! Cvoid\n\n    set_child!(aspect_frame::AspectFrame, child::Widget) = detail.set_child!(aspect_frame._internal, as_widget_pointer(child))\n    export set_child!\n\n    Base.show(io::IO, x::AspectFrame) = show_aux(io, x, :ratio)\n\n####### clamp_frame.jl\n\n    @export_type ClampFrame Widget\n    @declare_native_widget ClampFrame\n\n    function ClampFrame(size::Number, orientation::Orientation = ORIENTATION_HORIZONTAL)\n        out = ClampFrame(detail._ClampFrame(orientation))\n        set_maximum_size!(out, size)\n        return out\n    end\n\n    @export_function ClampFrame set_orientation! Cvoid Orientation orientation\n    @export_function ClampFrame get_orientation Orientation\n    @export_function ClampFrame set_maximum_size! Cvoid Number => Cfloat px\n    @export_function ClampFrame get_maximum_size Cfloat\n\n    @export_function ClampFrame remove_child! Cvoid\n\n    set_child!(clamp_frame::ClampFrame, child::Widget) = detail.set_child!(clamp_frame._internal, as_widget_pointer(child))\n    export set_child!\n\n    Base.show(io::IO, x::ClampFrame) = show_aux(io, x, :maximum_size)\n\n####### box.jl\n\n    @export_type Box Widget\n    @declare_native_widget Box\n\n    Box(orientation::Orientation) = Box(detail._Box(orientation))\n\n    function hbox(widgets::Widget...)\n        out = Box(ORIENTATION_HORIZONTAL)\n        for widget in widgets\n            push_back!(out, widget)\n        end\n        return out\n    end\n    export hbox\n\n    function vbox(widgets::Widget...)\n        out = Box(ORIENTATION_VERTICAL)\n        for widget in widgets\n            push_back!(out, widget)\n        end\n        return out\n    end\n    export vbox\n\n    function push_back!(box::Box, widget::Widget)\n        detail.push_back!(box._internal, as_widget_pointer(widget))\n    end\n    export push_back!\n\n    function push_front!(box::Box, widget::Widget)\n        detail.push_front!(box._internal, as_widget_pointer(widget))\n    end\n    export push_front!\n\n    function insert_after!(box::Box, to_append::Widget, after::Widget)\n        detail.insert_after!(box._internal, as_widget_pointer(to_append), as_widget_pointer(after))\n    end\n    export insert_after!\n\n    function remove!(box::Box, widget::Widget)\n        detail.remove!(box._internal, as_widget_pointer(widget))\n    end\n    export remove!\n\n    @export_function Box clear! Cvoid\n    @export_function Box set_homogeneous! Cvoid Bool b\n    @export_function Box get_homogeneous Bool\n\n    function set_spacing!(box::Box, spacing::Number)\n        detail.set_spacing!(box._internal, convert(Cfloat, spacing))\n    end\n    export set_spacing!\n\n    @export_function Box get_spacing Cfloat\n    @export_function Box get_n_items Cint\n    @export_function Box get_orientation Orientation\n    @export_function Box set_orientation! Cvoid Orientation orientation\n\n    @add_widget_signals Box\n\n    Base.show(io::IO, x::Box) = show_aux(io, x, :n_items)\n\n####### flow_box.jl\n\n    @export_type FlowBox Widget\n    @declare_native_widget FlowBox\n\n    FlowBox(orientation::Orientation) = FlowBox(detail._FlowBox(orientation))\n\n    function push_back!(box::FlowBox, widget::Widget)\n        detail.push_back!(box._internal, as_widget_pointer(widget))\n    end\n    export push_back!\n\n    function push_front!(box::FlowBox, widget::Widget)\n        detail.push_front!(box._internal, as_widget_pointer(widget))\n    end\n    export push_front!\n\n    insert_at!(box::FlowBox, index::Integer, widget::Widget) = detail.insert!(box._internal, from_julia_index(index), as_widget_pointer(widget))\n    export insert_at!\n\n    function remove!(box::FlowBox, widget::Widget)\n        detail.remove!(box._internal, as_widget_pointer(widget))\n    end\n    export remove!\n\n    @export_function FlowBox clear! Cvoid\n    @export_function FlowBox set_homogeneous! Cvoid Bool b\n    @export_function FlowBox get_homogeneous Bool\n\n    function set_row_spacing!(box::FlowBox, spacing::Number)\n        detail.set_row_spacing!(box._internal, convert(Cfloat, spacing))\n    end\n    export set_row_spacing!\n\n    @export_function FlowBox get_row_spacing Cfloat\n\n    function set_column_spacing!(box::FlowBox, spacing::Number)\n        detail.set_column_spacing!(box._internal, convert(Cfloat, spacing))\n    end\n    export set_column_spacing!\n\n    @export_function FlowBox get_column_spacing Cfloat\n\n    @export_function FlowBox get_n_items Cint\n    @export_function FlowBox get_orientation Orientation\n    @export_function FlowBox set_orientation! Cvoid Orientation orientation\n\n    @add_widget_signals FlowBox\n\n    Base.show(io::IO, x::FlowBox) = show_aux(io, x, :n_items)\n\n####### button.jl\n\n    @export_type Button Widget\n    @declare_native_widget Button\n\n    Button() = Button(detail._Button())\n\n    function Button(label::Widget)\n        out = Button()\n        set_child!(out, label)\n        return out\n    end\n\n    function Button(icon::Icon)\n        out = Button()\n        set_icon!(out, icon)\n        return out\n    end\n\n    @export_function Button set_has_frame! Cvoid Bool b\n    @export_function Button get_has_frame Bool\n    @export_function Button set_is_circular! Cvoid Bool b\n    @export_function Button get_is_circular Bool\n\n    function set_child!(button::Button, child::Widget)\n        detail.set_child!(button._internal, as_widget_pointer(child))\n    end\n    export set_child!\n\n    function set_icon!(button::Button, icon::Icon)\n        detail.set_icon!(button._internal, icon._internal)\n    end\n    export set_icon!\n\n    @export_function Button remove_child! Cvoid\n\n    function set_action!(button::Button, action::Action)\n        detail.set_action!(button._internal, action._internal)\n    end\n    export set_action!\n\n    @add_widget_signals Button\n    @add_signal_clicked Button\n\n    Base.show(io::IO, x::Button) = show_aux(io, x)\n\n####### center_box.jl\n\n    @export_type CenterBox Widget\n    @declare_native_widget CenterBox\n\n    CenterBox(orientation::Orientation) = CenterBox(detail._CenterBox(orientation))\n\n    function CenterBox(orientation::Orientation, left::Widget, center::Widget, right::Widget) ::CenterBox\n        out = CenterBox(orientation)\n        set_start_child!(out, left)\n        set_center_child!(out, center)\n        set_end_child!(out, right)\n        return out\n    end\n\n    function set_start_child!(center_box::CenterBox, child::Widget)\n        detail.set_start_child!(center_box._internal, as_widget_pointer(child))\n    end\n    export set_start_child!\n\n    function set_center_child!(center_box::CenterBox, child::Widget)\n        detail.set_center_child!(center_box._internal, as_widget_pointer(child))\n    end\n    export set_center_child!\n\n    function set_end_child!(center_box::CenterBox, child::Widget)\n        detail.set_end_child!(center_box._internal, as_widget_pointer(child))\n    end\n    export set_end_child!\n\n    @export_function CenterBox remove_start_child! Cvoid\n    @export_function CenterBox remove_center_child! Cvoid\n    @export_function CenterBox remove_end_child! Cvoid\n    @export_function CenterBox get_orientation Orientation\n    @export_function CenterBox set_orientation! Cvoid Orientation orientation\n\n    @add_widget_signals CenterBox\n\n    Base.show(io::IO, x::CenterBox) = show_aux(io, x)\n\n####### check_button.jl\n\n    @export_enum CheckButtonState begin\n        CHECK_BUTTON_STATE_ACTIVE\n        CHECK_BUTTON_STATE_INCONSISTENT\n        CHECK_BUTTON_STATE_INACTIVE\n    end\n\n    @export_type CheckButton Widget\n    @declare_native_widget CheckButton\n\n    CheckButton() = CheckButton(detail._CheckButton())\n\n    @export_function CheckButton set_state! Cvoid CheckButtonState state\n    @export_function CheckButton get_state CheckButtonState\n    @export_function CheckButton get_is_active Bool\n\n    set_is_active!(button::CheckButton, b::Bool) = set_state!(button, b ? CHECK_BUTTON_STATE_ACTIVE : CHECK_BUTTON_STATE_INACTIVE)\n    export set_is_active!\n\n    if detail.GTK_MINOR_VERSION >= 8\n        function set_child!(check_button::CheckButton, child::Widget)\n            detail.set_child!(check_button._internal, as_widget_pointer(child))\n        end\n        export set_child!\n\n        @export_function CheckButton remove_child! Cvoid\n    end\n\n    @add_widget_signals CheckButton\n    @add_signal_toggled CheckButton\n\n    Base.show(io::IO, x::CheckButton) = show_aux(io, x, :state)\n\n####### switch.jl\n\n    @export_type Switch Widget\n    @declare_native_widget Switch\n\n    Switch() = Switch(detail._Switch())\n\n    @export_function Switch get_is_active Bool\n    @export_function Switch set_is_active! Cvoid Bool b\n\n    @add_widget_signals Switch\n    @add_signal_switched Switch\n\n    Base.show(io::IO, x::Switch) = show_aux(io, x, :is_active)\n\n####### toggle_button.jl\n\n    @export_type ToggleButton Widget\n    @declare_native_widget ToggleButton\n\n    ToggleButton() = ToggleButton(detail._ToggleButton())\n    function ToggleButton(label::Widget)\n        out = ToggleButton()\n        set_child!(out, label)\n        return out\n    end\n\n    function ToggleButton(icon::Icon)\n        out = ToggleButton()\n        set_icon!(out, icon)\n        return out\n    end\n\n    @export_function ToggleButton set_is_active! Cvoid Bool b\n    @export_function ToggleButton get_is_active Bool\n    @export_function ToggleButton set_is_circular! Cvoid Bool b\n    @export_function ToggleButton get_is_circular Bool\n\n    function set_child!(toggle_button::ToggleButton, child::Widget)\n        detail.set_child!(toggle_button._internal, as_widget_pointer(child))\n    end\n    export set_child!\n\n    function set_icon!(toggle_button::ToggleButton, icon::Icon)\n        detail.set_icon!(toggle_button._internal, icon._internal)\n    end\n    export set_icon!\n\n    @export_function ToggleButton remove_child! Cvoid\n\n    @add_widget_signals ToggleButton\n    @add_signal_clicked ToggleButton\n    @add_signal_toggled ToggleButton\n\n    Base.show(io::IO, x::ToggleButton) = show_aux(io, x, :is_active)\n\n####### viewport.jl\n\n    @export_enum ScrollbarVisibilityPolicy begin\n        SCROLLBAR_VISIBILITY_POLICY_NEVER\n        SCROLLBAR_VISIBILITY_POLICY_ALWAYS\n        SCROLLBAR_VISIBILITY_POLICY_AUTOMATIC\n    end\n\n    @export_enum CornerPlacement begin\n        CORNER_PLACEMENT_TOP_LEFT\n        CORNER_PLACEMENT_TOP_RIGHT\n        CORNER_PLACEMENT_BOTTOM_LEFT\n        CORNER_PLACEMENT_BOTTOM_RIGHT\n    end\n\n    @export_type Viewport Widget\n    @declare_native_widget Viewport\n\n    Viewport() = Viewport(detail._Viewport())\n\n    function Viewport(child::Widget) ::Viewport\n        out = Viewport()\n        set_child!(out, child)\n        return out\n    end\n\n    @export_function Viewport set_propagate_natural_height! Cvoid Bool b\n    @export_function Viewport get_propagate_natural_height Bool\n    @export_function Viewport set_propagate_natural_width! Cvoid Bool b\n    @export_function Viewport get_propagate_natural_width Bool\n    @export_function Viewport set_horizontal_scrollbar_policy! Cvoid ScrollbarVisibilityPolicy policy\n    @export_function Viewport set_vertical_scrollbar_policy! Cvoid ScrollbarVisibilityPolicy policy\n    @export_function Viewport get_horizontal_scrollbar_policy ScrollbarVisibilityPolicy\n    @export_function Viewport get_vertical_scrollbar_policy ScrollbarVisibilityPolicy\n    @export_function Viewport set_scrollbar_placement! Cvoid CornerPlacement placement\n    @export_function Viewport get_scrollbar_placement CornerPlacement\n    @export_function Viewport set_has_frame! Cvoid Bool b\n    @export_function Viewport get_has_frame Bool\n    @export_function Viewport set_kinetic_scrolling_enabled! Cvoid Bool b\n    @export_function Viewport get_kinetic_scrolling_enabled Bool\n\n    get_horizontal_adjustment(viewport::Viewport) ::Adjustment = Adjustment(detail.get_horizontal_adjustment(viewport._internal))\n    export get_horizontal_adjustment\n\n    get_vertical_adjustment(viewport::Viewport) ::Adjustment = Adjustment(detail.get_vertical_adjustment(viewport._internal))\n    export get_vertical_adjustment\n\n    set_child!(viewport::Viewport, child::Widget) = detail.set_child!(viewport._internal, as_widget_pointer(child))\n    export set_child!\n\n    @export_function Viewport remove_child! Cvoid\n\n    @export_enum ScrollType begin\n        SCROLL_TYPE_NONE\n        SCROLL_TYPE_JUMP\n        SCROLL_TYPE_STEP_BACKWARD\n        SCROLL_TYPE_STEP_FORWARD\n        SCROLL_TYPE_STEP_UP\n        SCROLL_TYPE_STEP_DOWN\n        SCROLL_TYPE_STEP_LEFT\n        SCROLL_TYPE_STEP_RIGHT\n        SCROLL_TYPE_PAGE_BACKWARD\n        SCROLL_TYPE_PAGE_FORWARD\n        SCROLL_TYPE_PAGE_UP\n        SCROLL_TYPE_PAGE_DOWN\n        SCROLL_TYPE_PAGE_LEFT\n        SCROLL_TYPE_PAGE_RIGHT\n        SCROLL_TYPE_SCROLL_START\n        SCROLL_TYPE_SCROLL_END\n    end\n\n    @add_widget_signals Viewport\n    @add_signal_scroll_child Viewport\n\n    Base.show(io::IO, x::Viewport) = show_aux(io, x,\n        :propagate_natural_height,\n        :propagate_natural_width\n    )\n\n####### entry.jl\n\n    @export_type Entry Widget\n    @declare_native_widget Entry\n\n    Entry() = Entry(detail._Entry())\n\n    @export_function Entry get_text String\n    @export_function Entry set_text! Cvoid String text\n    @export_function Entry set_max_width_chars! Cvoid Integer n\n    @export_function Entry get_max_width_chars Signed\n    @export_function Entry set_has_frame! Cvoid Bool b\n    @export_function Entry get_has_frame Bool\n    @export_function Entry set_text_visible! Cvoid Bool b\n    @export_function Entry get_text_visible Bool\n\n    function set_primary_icon!(entry::Entry, icon::Icon)\n        detail.set_primary_icon!(entry._internal, icon._internal)\n    end\n    export set_primary_icon!\n\n    @export_function Entry remove_primary_icon! Cvoid\n\n    function set_secondary_icon!(entry::Entry, icon::Icon)\n        detail.set_secondary_icon!(entry._internal, icon._internal)\n    end\n    export set_secondary_icon!\n\n    @export_function Entry remove_secondary_icon! Cvoid\n\n    @add_widget_signals Entry\n    @add_signal_text_changed Entry\n    @add_signal_activate Entry\n\n    Base.show(io::IO, x::Entry) = show_aux(io, x, :text)\n\n####### expander.jl\n\n    @export_type Expander Widget\n    @declare_native_widget Expander\n\n    Expander() = Expander(detail._Expander())\n    function Expander(child::Widget, label::Widget) ::Expander\n        out = Expander()\n        set_child!(out, child)\n        set_label_widget!(out, label)\n        return out\n    end\n\n    function set_child!(expander::Expander, child::Widget)\n        detail.set_child!(expander._internal, as_widget_pointer(child))\n    end\n    export set_child!\n\n    @export_function Expander remove_child! Cvoid\n\n    function set_label_widget!(expander::Expander, child::Widget)\n        detail.set_label_widget!(expander._internal, as_widget_pointer(child))\n    end\n    export set_label_widget!\n\n    @export_function Expander remove_label_widget! Cvoid\n    @export_function Expander set_is_expanded! Cvoid Bool b\n    @export_function Expander get_is_expanded Bool\n\n    @add_widget_signals Expander\n    @add_signal_activate Expander\n\n    Base.show(io::IO, x::Expander) = show_aux(io, x, :is_expanded)\n\n####### fixed.jl\n\n    @export_type Fixed Widget\n    @declare_native_widget Fixed\n\n    Fixed() = Fixed(detail._Fixed())\n\n    add_child!(fixed::Fixed, child::Widget, position::Vector2f) = detail.add_child!(fixed._internal, as_widget_pointer(child), position)\n    export add_child!\n\n    remove_child!(fixed::Fixed, child::Widget) = detail.remove_child!(fixed._internal, as_widget_pointer(child))\n    export remove_child!\n\n    set_child_position!(fixed::Fixed, child::Widget, position::Vector2f) = detail.set_child_position!(fixed._internal, as_widget_pointer(child), position)\n    export set_child_position!\n\n    @add_widget_signals Fixed\n\n    Base.show(io::IO, x::Fixed) = show_aux(io, x)\n\n####### level_bar.jl\n\n    @export_enum LevelBarMode begin\n        LEVEL_BAR_MODE_CONTINUOUS\n        LEVEL_BAR_MODE_DISCRETE\n    end\n\n    @export_type LevelBar Widget\n    @declare_native_widget LevelBar\n\n    LevelBar(min::Number, max::Number) = LevelBar(detail._LevelBar(convert(Cfloat, min), convert(Cfloat, max)))\n\n    @export_function LevelBar add_marker! Cvoid String name Number => Cfloat value\n    @export_function LevelBar remove_marker! Cvoid String name\n    @export_function LevelBar set_inverted! Cvoid Bool b\n    @export_function LevelBar get_inverted Bool\n    @export_function LevelBar set_mode! Cvoid LevelBarMode mode\n    @export_function LevelBar get_mode LevelBarMode\n    @export_function LevelBar set_min_value! Cvoid Number => Cfloat value\n    @export_function LevelBar get_min_value Cfloat\n    @export_function LevelBar set_max_value! Cvoid Number => Cfloat value\n    @export_function LevelBar get_max_value Cfloat\n    @export_function LevelBar set_value! Cvoid Number => Cfloat value\n    @export_function LevelBar get_value Cfloat\n    @export_function LevelBar set_orientation! Cvoid Orientation orientation\n    @export_function LevelBar get_orientation Orientation\n\n    @add_widget_signals LevelBar\n\n    Base.show(io::IO, x::LevelBar) = show_aux(io, x, :orientation, :value, :min_value, :max_value)\n\n####### label.jl\n\n    @export_enum JustifyMode begin\n        JUSTIFY_MODE_LEFT\n        JUSTIFY_MODE_RIGHT\n        JUSTIFY_MODE_CENTER\n        JUSTIFY_MODE_FILL\n    end\n\n    @export_enum EllipsizeMode begin\n        ELLIPSIZE_MODE_NONE\n        ELLIPSIZE_MODE_START\n        ELLIPSIZE_MODE_MIDDLE\n        ELLIPSIZE_MODE_END\n    end\n\n    @export_enum LabelWrapMode begin\n        LABEL_WRAP_MODE_NONE\n        LABEL_WRAP_MODE_ONLY_ON_WORD\n        LABEL_WRAP_MODE_ONLY_ON_CHAR\n        LABEL_WRAP_MODE_WORD_OR_CHAR\n    end\n\n    @export_type Label Widget\n    @declare_native_widget Label\n\n    Label() = Label(detail._Label())\n    Label(formatted_string::String) = Label(detail._Label(formatted_string))\n\n    @export_function Label set_text! Cvoid String text\n    @export_function Label get_text String\n    @export_function Label set_use_markup! Cvoid Bool b\n    @export_function Label get_use_markup Bool\n    @export_function Label set_ellipsize_mode! Cvoid EllipsizeMode mode\n    @export_function Label get_ellipsize_mode EllipsizeMode\n    @export_function Label set_wrap_mode! Cvoid LabelWrapMode mode\n    @export_function Label get_wrap_mode LabelWrapMode\n    @export_function Label set_justify_mode! Cvoid JustifyMode mode\n    @export_function Label get_justify_mode JustifyMode\n    @export_function Label set_max_width_chars! Cvoid Integer n\n    @export_function Label get_max_width_chars Int64\n    @export_function Label set_x_alignment! Cvoid AbstractFloat => Cfloat x\n    @export_function Label get_x_alignment Cfloat\n    @export_function Label set_y_alignment! Cvoid AbstractFloat => Cfloat x\n    @export_function Label get_y_alignment Cfloat\n    @export_function Label set_is_selectable! Cvoid Bool b\n    @export_function Label get_is_selectable Bool\n\n    @add_widget_signals Label\n\n    Base.show(io::IO, x::Label) = show_aux(io, x,\n        :text,\n        :ellipsize_mode,\n        :wrap_mode,\n        :justify_mode\n    )\n\n####### text_view.jl\n\n    @export_type TextView Widget\n    @declare_native_widget TextView\n\n    TextView() = TextView(detail._TextView())\n\n    @export_function TextView get_text String\n    @export_function TextView set_text! Cvoid String text\n    @export_function TextView set_cursor_visible! Cvoid Bool b\n    @export_function TextView get_cursor_visible Bool\n    @export_function TextView undo! Cvoid\n    @export_function TextView redo! Cvoid\n    @export_function TextView set_was_modified! Cvoid Bool b\n    @export_function TextView get_was_modified Bool\n    @export_function TextView set_editable! Cvoid Bool b\n    @export_function TextView get_editable Bool\n    @export_function TextView set_justify_mode! Cvoid JustifyMode mode\n    @export_function TextView get_justify_mode JustifyMode\n    @export_function TextView set_left_margin! Cvoid Number => Cfloat margin\n    @export_function TextView get_left_margin Cfloat\n    @export_function TextView set_right_margin! Cvoid Number => Cfloat margin\n    @export_function TextView get_right_margin Cfloat\n    @export_function TextView set_top_margin! Cvoid Number => Cfloat margin\n    @export_function TextView get_top_margin Cfloat\n    @export_function TextView set_bottom_margin! Cvoid Number => Cfloat margin\n    @export_function TextView get_bottom_margin Cfloat\n\n    @add_widget_signals TextView\n    @add_signal_text_changed TextView\n\n    Base.show(io::IO, x::TextView) = show_aux(io, x, :text, :was_modified)\n\n####### frame.jl\n\n    @export_type Frame Widget\n    @declare_native_widget Frame\n\n    Frame() = Frame(detail._Frame())\n\n    function Frame(child::Widget) ::Frame\n        out = Frame()\n        set_child!(out, child)\n        return out\n    end\n\n    set_child!(frame::Frame, child::Widget) = detail.set_child!(frame._internal, as_widget_pointer(child))\n    export set_child!\n\n    set_label_widget!(frame::Frame, label::Widget) = detail.set_label_widget!(frame._internal, as_widget_pointer(label))\n    export set_label_widget!\n\n    @export_function Frame remove_child! Cvoid\n    @export_function Frame remove_label_widget! Cvoid\n    @export_function Frame set_label_x_alignment! Cvoid AbstractFloat => Cfloat x\n    @export_function Frame get_label_x_alignment Cfloat\n\n    @add_widget_signals Frame\n\n    Base.show(io::IO, x::Frame) = show_aux(io, x)\n\n####### overlay.jl\n\n    @export_type Overlay Widget\n    @declare_native_widget Overlay\n\n    Overlay() = Overlay(detail._Overlay())\n    function Overlay(base::Widget, overlays::Widget...) ::Overlay\n        out = Overlay()\n        set_child!(out, base)\n        for overlay in overlays\n            add_overlay!(out, overlay)\n        end\n        return out\n    end\n\n    set_child!(overlay::Overlay, child::Widget) = detail.set_child!(overlay._internal, as_widget_pointer(child))\n    export set_child!\n\n    remove_child!(overlay::Overlay) = detail.remove_child!(overlay._internal)\n    export remove_child!\n\n    function add_overlay!(overlay::Overlay, child::Widget; include_in_measurement::Bool = true, clip::Bool = false)\n        detail.add_overlay!(overlay._internal, as_widget_pointer(child), include_in_measurement, clip)\n    end\n    export add_overlay!\n\n    remove_overlay!(overlay::Overlay, child::Widget) = detail.remove_overlay!(overlay._internal, as_widget_pointer(child))\n    export remove_overlay!\n\n    @add_widget_signals Overlay\n\n    Base.show(io::IO, x::Overlay) = show_aux(io, x)\n\n####### relative_position.jl\n\n    @export_enum RelativePosition begin\n        RELATIVE_POSITION_ABOVE\n        RELATIVE_POSITION_BELOW\n        RELATIVE_POSITION_LEFT_OF\n        RELATIVE_POSITION_RIGHT_OF\n    end\n\n####### menu_model.jl\n\n    @export_type MenuModel SignalEmitter\n    MenuModel() = MenuModel(detail._MenuModel())\n\n    add_action!(model::MenuModel, label::String, action::Action) = detail.add_action!(model._internal, label, action._internal)\n    export add_action!\n\n    add_widget!(model::MenuModel, widget::Widget) = detail.add_widget!(model._internal, as_widget_pointer(widget))\n    export add_widget!\n\n    @export_enum SectionFormat begin\n        SECTION_FORMAT_NORMAL\n        SECTION_FORMAT_HORIZONTAL_BUTTONS\n        SECTION_FORMAT_HORIZONTAL_BUTTONS_LEFT_TO_RIGHT\n        SECTION_FORMAT_HORIZONTAL_BUTTONS_RIGHT_TO_LEFT\n        SECTION_FORMAT_CIRCULAR_BUTTONS\n        SECTION_FORMAT_INLINE_BUTTONS\n    end\n\n    function add_section!(model::MenuModel, title::String, to_add::MenuModel, section_format::SectionFormat = SECTION_FORMAT_NORMAL)\n        detail.add_section!(model._internal, title, to_add._internal, section_format)\n    end\n    export add_section!\n\n    add_submenu!(model::MenuModel, label::String, to_add::MenuModel) = detail.add_submenu!(model._internal, label, to_add._internal)\n    export add_submenu!\n\n    add_icon!(model::MenuModel, icon::Icon, action::Action) = detail.add_icon!(model._internal, icon._internal, action._internal)\n    export add_icon!\n\n    @add_signal_items_changed MenuModel\n\n    Base.show(io::IO, x::MenuModel) = show_aux(io, x)\n\n###### menubar.jl\n\n    @export_type MenuBar Widget\n    @declare_native_widget MenuBar\n\n    MenuBar(model::MenuModel) = MenuBar(detail._MenuBar(model._internal))\n\n    @add_widget_signals MenuBar\n\n    Base.show(io::IO, x::MenuBar) = show_aux(io, x)\n\n####### popover_menu.jl\n\n    @export_type PopoverMenu Widget\n    @declare_native_widget PopoverMenu\n\n    PopoverMenu(model::MenuModel) = PopoverMenu(detail._PopoverMenu(model._internal))\n\n    @add_widget_signals PopoverMenu\n    @add_signal_closed PopoverMenu\n\n    Base.show(io::IO, x::PopoverMenu) = show_aux(io, x)\n\n###### popover.jl\n\n    @export_type Popover Widget\n    @declare_native_widget Popover\n\n    Popover() = Popover(detail._Popover())\n\n    @export_function Popover popup! Cvoid\n    @export_function Popover popdown! Cvoid\n\n    function set_child!(popover::Popover, child::Widget)\n        detail.set_child!(popover._internal, as_widget_pointer(child))\n    end\n    export set_child!\n\n    @export_function Popover remove_child! Cvoid\n\n    @export_function Popover set_relative_position! Cvoid RelativePosition position\n    @export_function Popover get_relative_position RelativePosition\n    @export_function Popover set_autohide! Cvoid Bool b\n    @export_function Popover get_autohide Bool\n    @export_function Popover set_has_base_arrow! Cvoid Bool b\n    @export_function Popover get_has_base_arrow Bool\n\n    @add_widget_signals Popover\n    @add_signal_closed Popover\n\n    Base.show(io::IO, x::Popover) = show_aux(io, x)\n\n###### popover_button.jl\n\n    @export_type PopoverButton Widget\n    @declare_native_widget PopoverButton\n\n    PopoverButton(popover::Popover) = PopoverButton(detail._PopoverButton(popover._internal))\n    PopoverButton(popover_menu::PopoverMenu) = PopoverButton(detail._PopoverButton(popover_menu._internal))\n\n    set_child!(popover_button::PopoverButton, child::Widget) = detail.set_child!(popover_button._internal, as_widget_pointer(child))\n    export set_child!\n\n    set_icon!(popover_button::PopoverButton, icon::Icon) = detail.set_icon!(popover_button._internal, icon._internal)\n    export set_icon!\n\n    @export_function PopoverButton remove_child! Cvoid\n\n    function set_popover!(popover_button::PopoverButton, popover::Popover)\n        detail.set_popover!(popover_button._internal, popover._internal)\n    end\n    export set_popover!\n\n    function set_popover_menu!(popover_button::PopoverButton, popover_menu::PopoverMenu)\n        detail.set_popover_menu!(popover_button._internal, popover_menu._internal)\n    end\n    export set_popover_menu!\n\n    @export_function PopoverButton remove_popover! Cvoid\n    @export_function PopoverButton set_relative_position! Cvoid RelativePosition position\n    @export_function PopoverButton get_relative_position RelativePosition\n    @export_function PopoverButton set_always_show_arrow! Cvoid Bool b\n    @export_function PopoverButton get_always_show_arrow Bool\n    @export_function PopoverButton set_has_frame! Cvoid Bool b\n    @export_function PopoverButton get_has_frame Bool\n    @export_function PopoverButton popup! Cvoid\n    @export_function PopoverButton popdown! Cvoid\n    @export_function PopoverButton set_is_circular! Cvoid Bool b\n    @export_function PopoverButton get_is_circular Bool\n\n    @add_widget_signals PopoverButton\n    @add_signal_activate PopoverButton\n\n    Base.show(io::IO, x::PopoverButton) = show_aux(io, x)\n\n###### drop_down.jl\n\n    @export_type DropDown Widget\n    @declare_native_widget DropDown\n\n    DropDown() = DropDown(detail._DropDown())\n\n    const DropDownItemID = UInt64\n    export DropDownItemID\n\n    @export_function DropDown remove! Cvoid DropDownItemID id\n    @export_function DropDown set_always_show_arrow! Cvoid Bool b\n    @export_function DropDown get_always_show_arrow Bool\n    @export_function DropDown set_selected! Cvoid DropDownItemID id\n    @export_function DropDown get_selected DropDownItemID\n\n    get_item_at(drop_down::DropDown, i::Integer) = detail.get_item_at(drop_down._internal, from_julia_index(i))\n    export get_item_at\n\n    function push_back!(f, drop_down::DropDown, list_widget::Widget, label_widget::Widget, data::Data_t) where Data_t\n        typed_f = TypedFunction(f, Cvoid, (DropDown, Data_t))\n        return detail.push_back!(drop_down._internal, as_widget_pointer(list_widget), as_widget_pointer(label_widget), function (drop_down_internal_ref)\n            typed_f(DropDown(drop_down_internal_ref[]), data)\n        end)\n    end\n    function push_back!(f, drop_down::DropDown, list_widget::Widget, label_widget::Widget)\n        typed_f = TypedFunction(f, Cvoid, (DropDown,))\n        return detail.push_back!(drop_down._internal, as_widget_pointer(list_widget), as_widget_pointer(label_widget), function (drop_down_internal_ref)\n            typed_f(DropDown(drop_down_internal_ref[]))\n        end)\n    end\n    function push_back!(f, drop_down::DropDown, label::String, data::Data_t) where Data_t\n        return detail.push_back!(drop_down._internal, detail._Label(label).cpp_object, detail._Label(label).cpp_object, function (drop_down_internal_ref)\n            typed_f(DropDown(drop_down_internal_ref[]), data)\n        end)\n    end\n    function push_back!(f, drop_down::DropDown, label::String)\n        typed_f = TypedFunction(f, Cvoid, (DropDown,))\n        return detail.push_back!(drop_down._internal, detail._Label(label).cpp_object, detail._Label(label).cpp_object, function (drop_down_internal_ref)\n            typed_f(DropDown(drop_down_internal_ref[]))\n        end)\n    end\n\n    push_back!(drop_down::DropDown, list_widget::Widget, label_widget::Widget) = push_back!((_::DropDown) -> nothing, drop_down, list_widget, label_widget)\n    push_back!(drop_down::DropDown, label::String) = push_back!((_::DropDown) -> nothing, drop_down, label)\n    export push_back!\n\n    function push_front!(f, drop_down::DropDown, list_widget::Widget, label_widget::Widget, data::Data_t) where Data_t\n        return detail.push_front!(drop_down._internal, as_widget_pointer(list_widget), as_widget_pointer(label_widget), function (drop_down_internal_ref)\n            typed_f(DropDown(drop_down_internal_ref[]), data)\n        end)\n    end\n    function push_front!(f, drop_down::DropDown, list_widget::Widget, label_widget::Widget)\n        typed_f = TypedFunction(f, Cvoid, (DropDown,))\n        return detail.push_front!(drop_down._internal, as_widget_pointer(list_widget), as_widget_pointer(label_widget), function (drop_down_internal_ref)\n            typed_f(DropDown(drop_down_internal_ref[]))\n        end)\n    end\n    function push_front!(f, drop_down::DropDown, label::String, data::Data_t) where Data_t\n        return detail.push_front!(drop_down._internal, detail._Label(label).cpp_object, detail._Label(label).cpp_object, function (drop_down_internal_ref)\n            typed_f(DropDown(drop_down_internal_ref[]), data)\n        end)\n    end\n    function push_front!(f, drop_down::DropDown, label::String)\n        typed_f = TypedFunction(f, Cvoid, (DropDown,))\n        return detail.push_front!(drop_down._internal, detail._Label(label).cpp_object, detail._Label(label).cpp_object, function (drop_down_internal_ref)\n            typed_f(DropDown(drop_down_internal_ref[]))\n        end)\n    end\n\n    push_front!(drop_down::DropDown, list_widget::Widget, label_widget::Widget) = push_front!((_::DropDown) -> nothing, drop_down, list_widget, label_widget)\n    push_front!(drop_down::DropDown, label::String) = push_front!((_::DropDown) -> nothing, drop_down, label)\n    export push_front!\n\n    function insert_at!(f, drop_down::DropDown, index::Integer, list_widget::Widget, label_widget::Widget, data::Data_t) where Data_t\n        typed_f = TypedFunction(f, Cvoid, (DropDown, Data_t))\n        return detail.insert!(drop_down._internal, from_julia_index(index), as_widget_pointer(list_widget), as_widget_pointer(label_widget), function (drop_down_internal_ref)\n            typed_f(DropDown(drop_down_internal_ref[]), data)\n        end)\n    end\n    function insert_at!(f, drop_down::DropDown, index::Integer, list_widget::Widget, label_widget::Widget)\n        typed_f = TypedFunction(f, Cvoid, (DropDown,))\n        return detail.insert!(drop_down._internal, from_julia_index(index), as_widget_pointer(list_widget), as_widget_pointer(label_widget), function (drop_down_internal_ref)\n            typed_f(DropDown(drop_down_internal_ref[]))\n        end)\n    end\n    function insert_at!(f, drop_down::DropDown, index::Integer, label::String, data::Data_t) where Data_t\n        typed_f = TypedFunction(f, Cvoid, (DropDown, Data_t))\n        return detail.insert!(drop_down._internal, from_julia_index(index), detail._Label(label).cpp_object, detail._Label(label).cpp_object, function (drop_down_internal_ref)\n            typed_f(DropDown(drop_down_internal_ref[]), data)\n        end)\n    end\n    function insert_at!(f, drop_down::DropDown, index::Integer, label::String)\n        typed_f = TypedFunction(f, Cvoid, (DropDown,))\n        return detail.insert!(drop_down._internal, from_julia_index(index), detail._Label(label).cpp_object, detail._Label(label).cpp_object, function (drop_down_internal_ref)\n            typed_f(DropDown(drop_down_internal_ref[]))\n        end)\n    end\n\n    insert_at!(drop_down::DropDown, index::Integer, list_widget::Widget, label_widget::Widget) = insert_at!((_::DropDown) -> nothing, drop_down, index, list_widget, label_widget)\n    insert_at!(drop_down::DropDown, index::Integer, label::String) = insert_at!((_::DropDown) -> nothing, drop_down, index, label)\n    export insert_at!\n\n    @add_widget_signals DropDown\n\n    Base.show(io::IO, x::DropDown) = show_aux(io, x, :selected)\n\n###### event_controller.jl\n\n    abstract type EventController <: SignalEmitter end\n    export EventController\n\n    abstract type SingleClickGesture <: EventController end\n    export SingleClickGesture\n\n    @export_enum PropagationPhase begin\n        PROPAGATION_PHASE_NONE\n        PROPAGATION_PHASE_CAPTURE\n        PROPAGATION_PHASE_BUBBLE\n        PROPAGATION_PHASE_TARGET\n    end\n\n    set_propagation_phase!(controller::EventController, phase::PropagationPhase) = detail.set_propagation_phase!(controller._internal.cpp_object, phase)\n    export set_propagation_phase!\n\n    get_propagation_phase(controller::EventController) ::PropagationPhase = detail.get_propagation_phase(controller._internal.cpp_object)\n    export get_propagation_phase\n\n    @export_enum ButtonID begin\n        BUTTON_ID_NONE\n        BUTTON_ID_ANY\n        BUTTON_ID_BUTTON_01\n        BUTTON_ID_BUTTON_02\n        BUTTON_ID_BUTTON_03\n        BUTTON_ID_BUTTON_04\n        BUTTON_ID_BUTTON_05\n        BUTTON_ID_BUTTON_06\n        BUTTON_ID_BUTTON_07\n        BUTTON_ID_BUTTON_08\n        BUTTON_ID_BUTTON_09\n    end\n\n    get_current_button(gesture::SingleClickGesture) ::ButtonID = detail.get_current_button(gesture._internal.cpp_object)\n    export get_current_button\n\n    set_only_listens_to_button!(gesture::SingleClickGesture, button::ButtonID) = detail.set_only_listens_to_button!(gesture._internal.cpp_object, button)\n    export set_only_listens_to_button!\n\n    get_only_listens_to_button(gesture::SingleClickGesture) = detail.get_only_listens_to_button(gesture._internal.cpp_object)\n    export get_only_listens_to_button\n\n    set_touch_only!(gesture::SingleClickGesture, b::Bool) = detail.set_touch_only!(gesture._internal.cpp_object, b)\n    export set_touch_only!\n\n    get_touch_only(gesture::SingleClickGesture) = detail.get_touch_only(gesture._internal.cpp_object)\n    export get_touch_only\n\n###### drag_event_controller.jl\n\n    @export_type DragEventController SingleClickGesture\n    DragEventController() = DragEventController(detail._DragEventController())\n\n    get_start_position(controller::DragEventController) ::Vector2f = detail.get_start_position(controller._internal)\n    export get_start_position\n\n    get_current_offset(controller::DragEventController) ::Vector2f = detail.get_current_offset(controller._internal)\n    export get_current_offset\n\n    @add_signal_drag_begin DragEventController\n    @add_signal_drag DragEventController\n    @add_signal_drag_end DragEventController\n\n    Base.show(io::IO, x::DragEventController) = show_aux(io, x)\n\n###### click_event_controller.jl\n\n    @export_type ClickEventController SingleClickGesture\n    ClickEventController() = ClickEventController(detail._ClickEventController())\n\n    @add_signal_click_pressed ClickEventController\n    @add_signal_click_released ClickEventController\n    @add_signal_click_stopped ClickEventController\n\n    Base.show(io::IO, x::ClickEventController) = show_aux(io, x)\n\n###### focus_event_controller.jl\n\n    @export_type FocusEventController EventController\n    FocusEventController() = FocusEventController(detail._FocusEventController())\n\n    @export_function FocusEventController self_or_child_is_focused Bool\n    @export_function FocusEventController self_is_focused Bool\n\n    @add_signal_focus_gained FocusEventController\n    @add_signal_focus_lost FocusEventController\n\n    Base.show(io::IO, x::FocusEventController) = show_aux(io, x)\n\n###### key_event_controller.jl\n\n    const KeyCode = Cuint\n    export KeyCode\n\n    const ModifierState = detail._ModifierState\n    export ModifierState\n\n    @export_type KeyEventController EventController\n    KeyEventController() = KeyEventController(detail._KeyEventController())\n\n    @export_function KeyEventController should_shortcut_trigger_trigger Bool String trigger\n\n    @add_key_event_controller_signal KeyEventController key_pressed Cvoid\n    @add_key_event_controller_signal KeyEventController key_released Cvoid\n    @add_signal_modifiers_changed KeyEventController\n\n    shift_pressed(modifier_state::ModifierState) ::Bool = detail.shift_pressed(modifier_state);\n    export shift_pressed\n\n    control_pressed(modifier_state::ModifierState) ::Bool = detail.control_pressed(modifier_state);\n    export control_pressed\n\n    alt_pressed(modifier_state::ModifierState) ::Bool = detail.alt_pressed(modifier_state);\n    export alt_pressed\n\n    mouse_button_01_pressed(modifier_state::ModifierState) ::Bool = detail.mouse_button_01_pressed(modifier_state);\n    export mouse_button_01_pressed\n\n    mouse_button_02_pressed(modifier_state::ModifierState) ::Bool = detail.mouse_button_02_pressed(modifier_state);\n    export mouse_button_02_pressed\n\n    Base.show(io::IO, x::KeyEventController) = show_aux(io, x)\n\n###### long_press_event_controller.jl\n\n    @export_type LongPressEventController SingleClickGesture\n    LongPressEventController() = LongPressEventController(detail._LongPressEventController())\n\n    @export_function LongPressEventController set_delay_factor! Cvoid Number => Cfloat factor\n    @export_function LongPressEventController get_delay_factor Cfloat\n\n    @add_signal_pressed LongPressEventController\n    @add_signal_press_cancelled LongPressEventController\n\n    Base.show(io::IO, x::LongPressEventController) = show_aux(io, x)\n\n###### motion_event_controller.jl\n\n    @export_type MotionEventController EventController\n    MotionEventController() = MotionEventController(detail._MotionEventController())\n\n    @add_signal_motion_enter MotionEventController\n    @add_signal_motion MotionEventController\n    @add_signal_motion_leave MotionEventController\n\n    Base.show(io::IO, x::MotionEventController) = show_aux(io, x)\n\n###### pinch_zoom_event_controller.jl\n\n    @export_type PinchZoomEventController EventController\n    PinchZoomEventController() = PinchZoomEventController(detail._PinchZoomEventController())\n\n    @export_function PinchZoomEventController get_scale_delta Cfloat\n\n    @add_signal_scale_changed PinchZoomEventController\n\n    Base.show(io::IO, x::PinchZoomEventController) = show_aux(io, x)\n\n###### rotate_event_controller.jl\n\n    @export_type RotateEventController EventController\n    RotateEventController() = RotateEventController(detail._RotateEventController())\n\n    get_angle_delta(controller::RotateEventController) ::Angle = radians(detail.get_angle_delta(controller._internal))\n    export get_angle_delta\n\n    @add_signal_rotation_changed RotateEventController\n\n    Base.show(io::IO, x::RotateEventController) = show_aux(io, x)\n\n###### scroll_event_controller.jl\n\n    @export_type ScrollEventController EventController\n    ScrollEventController(kinetic_scrolling_enabled::Bool = false) = ScrollEventController(detail._ScrollEventController(kinetic_scrolling_enabled))\n\n    @export_function ScrollEventController get_kinetic_scrolling_enabled Bool\n    @export_function ScrollEventController set_kinetic_scrolling_enabled! Cvoid Bool b\n\n    @add_signal_kinetic_scroll_decelerate ScrollEventController\n    @add_signal_scroll_begin ScrollEventController\n    @add_signal_scroll ScrollEventController\n    @add_signal_scroll_end ScrollEventController\n\n    Base.show(io::IO, x::ScrollEventController) = show_aux(io, x)\n\n###### shortcut_event_controller.jl\n\n    @export_type ShortcutEventController EventController\n    ShortcutEventController() = ShortcutEventController(detail._ShortcutEventController())\n\n    add_action!(shortcut_controller::ShortcutEventController, action::Action) = detail.add_action!(shortcut_controller._internal, action._internal)\n    export add_action!\n\n    remove_action!(shortcut_controller::ShortcutEventController, action::Action) = detail.remove_action!(shortcut_controller._internal, action._internal)\n    export remove_action!\n\n    @export_enum ShortcutScope begin\n        SHORTCUT_SCOPE_LOCAL\n        SHORTCUT_SCOPE_GLOBAL\n        #SHORTCUT_SCOPE_MANAGED\n    end\n\n    set_scope!(controller::ShortcutEventController, scope::ShortcutScope) = detail.set_scope!(controller._internal, scope)\n    export set_scope!\n\n    get_scope(controller::ShortcutEventController) ::ShortcutScope = detail.get_scope(controller._internal)\n    export get_scope\n\n    Base.show(io::IO, x::ShortcutEventController) = show_aux(io, x, :scope)\n\n###### stylus_event_controller.jl\n\n    @export_enum ToolType begin\n        TOOL_TYPE_UNKNOWN\n        TOOL_TYPE_PEN\n        TOOL_TYPE_ERASER\n        TOOL_TYPE_BRUSH\n        TOOL_TYPE_PENCIL\n        TOOL_TYPE_AIRBRUSH\n        TOOL_TYPE_LENS\n        TOOL_TYPE_MOUSE\n    end\n\n    @export_enum DeviceAxis begin\n        DEVICE_AXIS_X\n        DEVICE_AXIS_Y\n        DEVICE_AXIS_DELTA_X\n        DEVICE_AXIS_DELTA_Y\n        DEVICE_AXIS_PRESSURE\n        DEVICE_AXIS_X_TILT\n        DEVICE_AXIS_Y_TILT\n        DEVICE_AXIS_WHEEL\n        DEVICE_AXIS_DISTANCE\n        DEVICE_AXIS_ROTATION\n        DEVICE_AXIS_SLIDER\n    end\n\n    device_axis_to_string(axis::DeviceAxis) ::String = detail.device_axis_to_string(axis)\n    export device_axis_to_string\n\n    @export_type StylusEventController SingleClickGesture\n    StylusEventController() = StylusEventController(detail._StylusEventController())\n\n    @export_function StylusEventController get_hardware_id Csize_t\n    @export_function StylusEventController get_tool_type ToolType\n    @export_function StylusEventController has_axis Bool DeviceAxis axis\n    @export_function StylusEventController get_axis_value Float64 DeviceAxis axis\n\n    @add_signal_stylus_up StylusEventController\n    @add_signal_stylus_down StylusEventController\n    @add_signal_proximity StylusEventController\n    @add_signal_motion StylusEventController\n\n    Base.show(io::IO, x::StylusEventController) = show_aux(io, x, :hardware_id)\n\n###### swipe_event_controller.jl\n\n    @export_type SwipeEventController SingleClickGesture\n    SwipeEventController() = SwipeEventController(detail._SwipeEventController())\n\n    get_velocity(swipe_controller::SwipeEventController) ::Vector2f = detail.get_velocity(swipe_controller._internal)\n    export get_velocity\n\n    @add_signal_swipe SwipeEventController\n\n    Base.show(io::IO, x::SwipeEventController) = show_aux(io, x)\n\n###### pan_event_controller.jl\n\n    @export_enum PanDirection begin\n        PAN_DIRECTION_LEFT\n        PAN_DIRECTION_RIGHT\n        PAN_DIRECTION_UP\n        PAN_DIRECTION_DOWN\n    end\n\n    @export_type PanEventController SingleClickGesture\n    PanEventController(orientation::Orientation) = PanEventController(detail._PanEventController(orientation))\n\n    set_orientation!(pan_controller::PanEventController, orientation::Orientation) = detail.set_orientation!(pan_controller._internal, orientation)\n    export set_orientation!\n\n    get_orientation(pan_controller::PanEventController) ::Orientation = detail.get_orientation(pan_controller._internal)\n    export get_orientation\n\n    @add_signal_pan PanEventController\n\n    Base.show(io::IO, x::PanEventController) = show_aux(io, x, :orientation)\n\n###### selection_model.jl\n\n    @export_enum SelectionMode begin\n        SELECTION_MODE_NONE\n        SELECTION_MODE_SINGLE\n        SELECTION_MODE_MULTIPLE\n    end\n\n    @export_type SelectionModel SignalEmitter\n    SelectionModel(internal::Ptr{Cvoid}) = SelectionModel(detail._SelectionModel(internal))\n\n    function get_selection(model::SelectionModel) ::Vector{Int64}\n        selection = detail.get_selection(model._internal)\n        return Int64[to_julia_index(x) for x in selection]\n    end\n    export get_selection\n\n    @export_function SelectionModel select_all! Cvoid\n    @export_function SelectionModel unselect_all! Cvoid\n    @export_function SelectionModel get_n_items Int64\n\n    @export_function SelectionModel get_selection_mode SelectionMode\n\n    select!(model::SelectionModel, i::Integer, unselect_others::Bool = true) = detail.select!(model._internal, from_julia_index(i), unselect_others)\n    export select!\n\n    unselect!(model::SelectionModel, i::Integer) = detail.unselect!(model._internal, from_julia_index(i))\n    export unselect!\n\n    @add_signal_selection_changed SelectionModel\n\n    Base.show(io::IO, x::SelectionModel) = show_aux(io, x, :selection_mode)\n\n###### list_view.jl\n\n    @export_type ListView Widget\n    @declare_native_widget ListView\n\n    ListView(orientation::Orientation, selection_mode::SelectionMode = SELECTION_MODE_NONE) = ListView(detail._ListView(orientation, selection_mode))\n\n    struct ListViewIterator\n        _internal::Ptr{Cvoid}\n    end\n    export ListViewIterator\n\n    push_back!(list_view::ListView, widget::Widget) = ListViewIterator(detail.push_back!(list_view._internal, as_widget_pointer(widget), Ptr{Cvoid}()))\n    push_back!(list_view::ListView, widget::Widget, iterator::ListViewIterator) = ListViewIterator(detail.push_back!(list_view._internal, as_widget_pointer(widget), iterator._internal))\n    export push_back!\n\n    push_front!(list_view::ListView, widget::Widget) = ListViewIterator(detail.push_front!(list_view._internal, as_widget_pointer(widget), Ptr{Cvoid}()))\n    push_front!(list_view::ListView, widget::Widget, iterator::ListViewIterator) = ListViewIterator(detail.push_front!(list_view._internal, as_widget_pointer(widget), iterator._internal))\n    export push_front!\n\n    insert_at!(list_view::ListView, index::Integer, widget::Widget) = ListViewIterator(detail.insert!(list_view._internal, from_julia_index(index), as_widget_pointer(widget), Ptr{Cvoid}()))\n    insert_at!(list_view::ListView, index::Integer, widget::Widget, iterator::ListViewIterator) = ListViewIterator(detail.insert!(list_view._internal, from_julia_index(index), as_widget_pointer(widget), iterator._internal))\n    export insert_at!\n\n    remove!(list_view::ListView, index::Integer) = detail.remove!(list_view._internal, from_julia_index(index), Ptr{Cvoid}())\n    remove!(list_view::ListView, index::Integer, iterator::ListViewIterator) = detail.remove!(list_view._internal, from_julia_index(index), iterator._internal)\n    export remove!\n\n    clear!(list_view::ListView) = detail.clear!(list_view._internal,Ptr{Cvoid}())\n    clear!(list_view::ListView, iterator::ListViewIterator) = detail.clear!(list_view._internal, iterator._internal)\n    export clear!\n\n    set_widget_at!(list_view::ListView, index::Integer, widget::Widget) = detail.set_widget_at!(list_view._internal, from_julia_index(index), as_widget_pointer(widget), Ptr{Cvoid}())\n    set_widget_at!(list_view::ListView, index::Integer, widget::Widget, iterator::ListViewIterator) = detail.set_widget_at!(list_view._internal, from_julia_index(index), as_widget_pointer(widget), iterator._internal)\n    export set_widget_at!\n\n    function find(list_view::ListView, widget::Widget, iterator::ListViewIterator) ::Signed\n        i = detail.find(list_view._internal, as_widget_pointer(widget), iterator._internal)\n        return i == -1 ? -1 : to_julia_index(i)\n    end\n    function find(list_view::ListView, widget::Widget) ::Signed\n        i = detail.find(list_view._internal, as_widget_pointer(widget), Ptr{Cvoid}())\n        return i == -1 ? -1 : to_julia_index(i)\n    end\n    export find\n\n    get_selection_model(list_view::ListView) ::SelectionModel = SelectionModel(detail.get_selection_model(list_view._internal))\n    export get_selection_model\n\n    @export_function ListView set_enable_rubberband_selection! Cvoid Bool b\n    @export_function ListView get_enable_rubberband_selection Bool\n    @export_function ListView set_show_separators! Cvoid Bool b\n    @export_function ListView get_show_separators Bool\n    @export_function ListView set_single_click_activate! Cvoid Bool b\n    @export_function ListView get_single_click_activate Bool\n    @export_function ListView get_n_items Int64\n    @export_function ListView set_orientation! Cvoid Orientation orientation\n    @export_function ListView get_orientation Orientation\n\n    @add_widget_signals ListView\n    @add_signal_activate_item ListView\n\n    Base.show(io::IO, x::ListView) = show_aux(io, x, :selection_model, :orientation)\n\n###### grid_view.jl\n\n    @export_type GridView Widget\n    @declare_native_widget GridView\n\n    GridView(orientation::Orientation = ORIENTATION_VERTICAL, selection_mode::SelectionMode = SELECTION_MODE_NONE) = GridView(detail._GridView(orientation, selection_mode))\n    GridView(selection_mode::SelectionMode) = GridView(ORIENTATION_VERTICAL, selection_mode)\n\n    push_back!(grid_view::GridView, widget::Widget) = detail.push_back!(grid_view._internal, as_widget_pointer(widget))\n    export push_back!\n\n    push_front!(grid_view::GridView, widget::Widget) = detail.push_front!(grid_view._internal, as_widget_pointer(widget))\n    export push_front!\n\n    insert_at!(grid_view::GridView, index::Integer, widget::Widget) = detail.insert!(grid_view._internal, from_julia_index(index), as_widget_pointer(widget))\n    export insert_at!\n\n    remove!(grid_view::GridView, index::Integer) = detail.remove!(grid_view._internal, from_julia_index(index))\n    export remove!\n\n    clear!(grid_view::GridView) = detail.clear!(grid_view._internal)\n    export clear!\n\n    function find(grid_view::GridView, widget::Widget) ::Signed\n        i = detail.find(grid_view._internal, as_widget_pointer(widget))\n        return i == -1 ? -1 : to_julia_index(i)\n    end\n    export find\n\n    @export_function GridView get_n_items Int64\n    @export_function GridView set_enable_rubberband_selection! Cvoid Bool b\n    @export_function GridView get_enable_rubberband_selection Bool\n    @export_function GridView get_single_click_activate Bool\n    @export_function GridView set_single_click_activate! Cvoid Bool b\n\n    set_max_n_columns!(grid_view::GridView, n::Integer) = detail.set_max_n_columns!(grid_view._internal, UInt64(n))\n    export set_max_n_columns!\n\n    get_max_n_columns(grid_view::GridView) ::Int64 = detail.get_max_n_columns(grid_view._internal)\n    export get_max_n_columns\n\n    set_min_n_columns!(grid_view::GridView, n::Integer) = detail.set_min_n_columns!(grid_view._internal, UInt64(n))\n    export set_min_n_columns!\n\n    get_min_n_columns(grid_view::GridView) ::Int64 = detail.get_min_n_columns(grid_view._internal)\n    export get_min_n_columns\n\n    @export_function GridView set_orientation! Cvoid Orientation orientation\n    @export_function GridView get_orientation Orientation\n\n    get_selection_model(grid_view::GridView) ::SelectionModel = SelectionModel(detail.get_selection_model(grid_view._internal))\n    export get_selection_model\n\n    @add_widget_signals GridView\n    @add_signal_activate_item GridView\n\n    Base.show(io::IO, x::GridView) = show_aux(io, x, :selection_model)\n\n###### grid.jl\n\n    @export_type Grid Widget\n    @declare_native_widget Grid\n\n    Grid() = Grid(detail._Grid())\n\n    function insert_at!(grid::Grid, widget::Widget, row_i::Signed, column_i::Signed, n_horizontal_cells::Integer = 1, n_vertical_cells::Integer = 1)\n        detail.insert!(grid._internal, as_widget_pointer(widget), row_i - 1, column_i - 1, n_horizontal_cells, n_vertical_cells)\n    end\n    export insert_at!\n\n    function insert_next_to!(grid::Grid, to_insert::Widget, already_in_grid::Widget, position::RelativePosition, n_horizontal_cells::Integer = 1, n_vertical_cells::Integer = 1)\n        detail.insert_next_to!(grid._internal, as_widget_pointer(to_insert), as_widget_pointer(already_in_grid), position, n_horizontal_cells, n_vertical_cells)\n    end\n    export insert_next_to!\n\n    remove!(grid::Grid, widget::Widget) = detail.remove!(grid._internal, as_widget_pointer(widget))\n    export remove!\n\n    function get_position(grid::Grid, widget::Widget) ::Vector2i\n        native_pos::Vector2i = detail.get_position(grid._internal, as_widget_pointer(widget))\n        return Vector2i(native_pos.x + 1, native_pos.y + 1)\n    end\n    export get_position\n\n    get_size(grid::Grid, widget::Widget) ::Vector2i = detail.get_size(grid._internal, as_widget_pointer(widget))\n    export get_size\n\n    insert_row_at!(grid::Grid, row_i::Signed) = detail.insert_row_at!(grid._internal, row_i -1)\n    export insert_row_at!\n\n    remove_row_at!(grid::Grid, row_i::Signed) = detail.remove_row_at!(grid._internal, row_i -1)\n    export remove_row_at!\n\n    insert_column_at!(grid::Grid, column_i::Signed) = detail.insert_column_at!(grid._internal, column_i -1)\n    export insert_column_at!\n\n    remove_column_at!(grid::Grid, column_i::Signed) = detail.remove_column_at!(grid._internal, column_i -1)\n    export remove_column_at!\n\n    @export_function Grid get_column_spacing Cfloat\n    @export_function Grid set_column_spacing! Cvoid Number => Cfloat spacing\n    @export_function Grid get_row_spacing Cfloat\n    @export_function Grid set_row_spacing! Cvoid Number => Cfloat spacing\n    @export_function Grid set_rows_homogeneous! Cvoid Bool b\n    @export_function Grid get_rows_homogeneous Bool\n    @export_function Grid set_columns_homogeneous! Cvoid Bool b\n    @export_function Grid get_columns_homogeneous Bool\n    @export_function Grid set_orientation! Cvoid Orientation orientation\n    @export_function Grid get_orientation Orientation\n\n    @add_widget_signals Grid\n\n    Base.show(io::IO, x::Grid) = show_aux(io, x, :orientation)\n\n###### stack.jl\n\n    @export_type Stack Widget\n    @declare_native_widget Stack\n    Stack() = Stack(detail._Stack())\n\n    @export_type StackSidebar Widget\n    @declare_native_widget StackSidebar\n    StackSidebar(stack::Stack) = StackSidebar(detail._StackSidebar(stack._internal))\n\n    @export_type StackSwitcher Widget\n    @declare_native_widget StackSwitcher\n    StackSwitcher(stack::Stack) = StackSwitcher(detail._StackSwitcher(stack._internal))\n\n    @export_enum StackTransitionType begin\n        STACK_TRANSITION_TYPE_NONE\n        STACK_TRANSITION_TYPE_CROSSFADE\n        STACK_TRANSITION_TYPE_SLIDE_RIGHT\n        STACK_TRANSITION_TYPE_SLIDE_LEFT\n        STACK_TRANSITION_TYPE_SLIDE_UP\n        STACK_TRANSITION_TYPE_SLIDE_DOWN\n        STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT\n        STACK_TRANSITION_TYPE_SLIDE_UP_DOWN\n        STACK_TRANSITION_TYPE_OVER_UP\n        STACK_TRANSITION_TYPE_OVER_DOWN\n        STACK_TRANSITION_TYPE_OVER_LEFT\n        STACK_TRANSITION_TYPE_OVER_RIGHT\n        STACK_TRANSITION_TYPE_UNDER_UP\n        STACK_TRANSITION_TYPE_UNDER_DOWN\n        STACK_TRANSITION_TYPE_UNDER_LEFT\n        STACK_TRANSITION_TYPE_UNDER_RIGHT\n        STACK_TRANSITION_TYPE_OVER_UP_DOWN\n        STACK_TRANSITION_TYPE_OVER_LEFT_RIGHT\n        STACK_TRANSITION_TYPE_ROTATE_LEFT\n        STACK_TRANSITION_TYPE_ROTATE_RIGHT\n        STACK_TRANSITION_TYPE_ROTATE_LEFT_RIGHT\n    end\n\n    get_selection_model(stack::Stack) ::SelectionModel = SelectionModel(detail.get_selection_model(stack._internal))\n    export get_selection_model\n\n    const StackID = String\n    export StackID\n\n    add_child!(stack::Stack, child::Widget, title::String) ::StackID = detail.add_child!(stack._internal, as_widget_pointer(child), title)\n    export add_child!\n\n    remove_child!(stack::Stack, id::StackID) = detail.remove_child!(stack._internal, id)\n    export remove_child!\n\n    set_visible_child!(stack::Stack, id::StackID) = detail.set_visible_child!(stack._internal, id)\n    export set_visible_child!\n\n    get_visible_child(stack::Stack) ::StackID = detail.get_visible_child(stack._internal)\n    export get_visible_child\n\n    get_child_at(stack::Stack, index::Integer) ::StackID = detail.get_child_at(stack._internal, from_julia_index(convert(Csize_t, index)))\n    export get_child_at\n\n    @export_function Stack set_transition_type! Cvoid StackTransitionType transition\n    @export_function Stack get_transition_type StackTransitionType\n\n    set_transition_duration!(stack::Stack, duration::Time) = detail.set_transition_duration!(stack._internal, convert(Cfloat, as_microseconds(duration)))\n    export set_transition_duration!\n\n    get_transition_duration(stack::Stack) ::Time = microseconds(detail.get_transition_duration(stack._internal))\n    export get_transition_duration\n\n    @export_function Stack set_is_horizontally_homogeneous! Cvoid Bool b\n    @export_function Stack get_is_horizontally_homogeneous Bool\n    @export_function Stack set_is_vertically_homogeneous! Cvoid Bool b\n    @export_function Stack get_is_vertically_homogeneous Bool\n    @export_function Stack set_should_interpolate_size! Cvoid Bool b\n    @export_function Stack get_should_interpolate_size Bool\n\n    @add_widget_signals Stack\n    @add_widget_signals StackSidebar\n    @add_widget_signals StackSwitcher\n\n    Base.show(io::IO, x::Stack) = show_aux(io, x, :selection_model, :transition_type)\n\n###### notebook.jl\n\n    @export_type Notebook Widget\n    @declare_native_widget Notebook\n\n    Notebook() = Notebook(detail._Notebook())\n\n    function push_front!(notebook::Notebook, child_widget::Widget, label_widget::Widget) ::Int64\n        return detail.push_front!(notebook._internal, as_widget_pointer(child_widget), as_widget_pointer(label_widget))\n    end\n    export push_front!\n\n    function push_back!(notebook::Notebook, child_widget::Widget, label_widget::Widget) ::Int64\n        return detail.push_back!(notebook._internal, as_widget_pointer(child_widget), as_widget_pointer(label_widget))\n    end\n    export push_back!\n\n    function insert_at!(notebook::Notebook, index::Integer, child_widget::Widget, label_widget::Widget) ::Int64\n        return detail.insert!(notebook._internal, from_julia_index(index), as_widget_pointer(child_widget), as_widget_pointer(label_widget))\n    end\n    export insert_at!\n\n    function move_page_to!(notebook::Notebook, current_index::Integer, new_index::Integer) ::Cvoid\n        detail.move_page_to!(notebook._internal, from_julia_index(current_index), from_julia_index(new_index))\n    end\n    export move_page_to!\n\n    remove!(notebook::Notebook, index::Integer) = detail.remove!(notebook._internal, from_julia_index(index))\n    export remove!\n\n    @export_function Notebook next_page! Cvoid\n    @export_function Notebook previous_page! Cvoid\n\n    goto_page!(notebook::Notebook, index::Integer) = detail.goto_page!(notebook._internal, from_julia_index(index))\n    export goto_page!\n\n    get_current_page(notebook::Notebook) ::Int64 = to_julia_index(detail.get_current_page(notebook._internal))\n    export get_current_page\n\n    @export_function Notebook get_n_pages Int64\n    @export_function Notebook set_is_scrollable! Cvoid Bool b\n    @export_function Notebook get_is_scrollable Bool\n    @export_function Notebook set_has_border! Cvoid Bool b\n    @export_function Notebook get_has_border Bool\n    @export_function Notebook set_tabs_visible! Cvoid Bool b\n    @export_function Notebook get_tabs_visible Bool\n    @export_function Notebook set_quick_change_menu_enabled! Cvoid Bool b\n    @export_function Notebook get_quick_change_menu_enabled Bool\n    @export_function Notebook set_tab_position! Cvoid RelativePosition relative_position\n    @export_function Notebook get_tab_position RelativePosition\n    @export_function Notebook set_tabs_reorderable! Cvoid Bool b\n    @export_function Notebook get_tabs_reorderable Bool\n\n    @add_widget_signals Notebook\n    @add_notebook_signal Notebook page_added\n    @add_notebook_signal Notebook page_reordered\n    @add_notebook_signal Notebook page_removed\n    @add_notebook_signal Notebook page_selection_changed\n\n    Base.show(io::IO, x::Notebook) = show_aux(io, x, :current_page, :n_pages)\n\n###### column_view.jl\n\n    @export_type ColumnViewColumn SignalEmitter\n\n    ColumnViewColumn(internal::Ptr{Cvoid}) = ColumnViewColumn(detail._ColumnViewColumn(internal))\n\n    @export_function ColumnViewColumn set_title! Cvoid String title\n    @export_function ColumnViewColumn get_title String\n    @export_function ColumnViewColumn set_fixed_width! Cvoid Number => Cfloat width\n    @export_function ColumnViewColumn get_fixed_width Cfloat\n\n    set_header_menu!(column::ColumnViewColumn, model::MenuModel) = detail.set_header_menu!(column._internal, model._internal)\n    export set_header_menu!\n\n    @export_function ColumnViewColumn set_is_visible! Cvoid Bool b\n    @export_function ColumnViewColumn get_is_visible Bool\n    @export_function ColumnViewColumn set_is_resizable! Cvoid Bool b\n    @export_function ColumnViewColumn get_is_resizable Bool\n\n    @export_type ColumnView Widget\n    @declare_native_widget ColumnView\n\n    ColumnView(selection_mode::SelectionMode = SELECTION_MODE_NONE) = ColumnView(detail._ColumnView(selection_mode))\n\n    function push_back_column!(column_view::ColumnView, title::String) ::ColumnViewColumn\n        return ColumnViewColumn(detail.push_back_column!(column_view._internal, title))\n    end\n    export push_back_column!\n\n    function push_front_column!(column_view::ColumnView, title::String) ::ColumnViewColumn\n        return ColumnViewColumn(detail.push_front_column!(column_view._internal, title))\n    end\n    export push_front_column!\n\n    function insert_column_at!(column_view::ColumnView, index::Integer, title::String) ::ColumnViewColumn\n        return ColumnViewColumn(detail.insert_column!(column_view._internal, from_julia_index(index), title))\n    end\n    export insert_column_at!\n\n    remove_column!(column_view::ColumnView, column::ColumnViewColumn) = detail.remove_column!(column_view._internal, column._internal)\n    export remove_column!\n\n    function get_column_at(column_view::ColumnView, index::Integer) ::ColumnViewColumn\n        return ColumnViewColumn(detail.get_column_at(column_view._internal, from_julia_index(index)))\n    end\n    export get_column_at\n\n    function get_column_with_title(column_view::ColumnView, title::String) ::ColumnViewColumn\n        return ColumnViewColumn(detail.get_column_with_title(column_view._internal, title))\n    end\n    export get_column_with_title\n\n    has_column_with_title(column_view::ColumnView, title::String) ::Bool = detail.has_column_with_title(column_view._internal, title)\n    export has_column_with_title\n\n    function set_widget_at!(column_view::ColumnView, column::ColumnViewColumn, row_i::Integer, widget::Widget)\n        detail.set_widget_at!(column_view._internal, column._internal, from_julia_index(row_i), as_widget_pointer(widget))\n    end\n    export set_widget_at!\n\n    function push_back_row!(column_view::ColumnView, widgets::Widget...)\n\n        if length(widgets) > get_n_columns(column_view)\n            @log_warning MOUSETRAP_DOMAIN \"In ColumnView.push_back_row!: Attempting to push $(length(widgets)) widgets, but ColumnView only has $(get_n_columns(column_view)) columns\"\n        end\n\n        row_i = get_n_rows(column_view) + 1\n        insert_row_at!(column_view, row_i, widgets...)\n    end\n    export push_back_row!\n\n    function push_front_row!(column_view::ColumnView, widgets::Widget...)\n\n        @log_critical MOUSETRAP_DOMAIN \"In ColumnView.push_front_row!: This method was deprecated in v0.3.2, use `insert_row_at!` instead\"\n\n        if length(widgets) > get_n_columns(column_view)\n            @log_warning MOUSETRAP_DOMAIN \"In ColumnView.push_front_row!: Attempting to push $(length(widgets)) widgets, but ColumnView only has $(get_n_columns(column_view)) columns\"\n        end\n\n        row_i = 1\n        for i in 1:get_n_columns(column_view)\n            column = get_column_at(column_view, i)\n            detail.set_widget_at!(column_view._internal, column._internal, 0, as_widget_pointer(widgets[i]))\n        end\n    end\n    export push_front_row!\n\n    function insert_row_at!(column_view::ColumnView, index::Integer, widgets::Widget...)\n\n        if length(widgets) > get_n_columns(column_view)\n            @log_warning MOUSETRAP_DOMAIN \"In ColumnView.insert_row_at!: Attempting to push $(length(widgets)) widgets, but ColumnView only has $(get_n_columns(column_view)) columns\"\n        end\n\n        row_i = index\n        for i in 1:get_n_columns(column_view)\n            column = get_column_at(column_view, i)\n            set_widget_at!(column_view, column, row_i, widgets[i])\n        end\n    end\n    export push_front_row!\n\n    get_selection_model(column_view::ColumnView) ::SelectionModel = SelectionModel(detail.get_selection_model(column_view._internal))\n    export get_selection_model\n\n    @export_function ColumnView set_enable_rubberband_selection! Cvoid Bool b\n    @export_function ColumnView get_enable_rubberband_selection Bool\n    @export_function ColumnView set_show_row_separators Cvoid Bool b\n    @export_function ColumnView get_show_row_separators Bool\n    @export_function ColumnView set_show_column_separators Cvoid Bool b\n    @export_function ColumnView get_show_column_separators Bool\n    @export_function ColumnView set_single_click_activate! Cvoid Bool b\n    @export_function ColumnView get_single_click_activate Bool\n    @export_function ColumnView get_n_rows Int64\n    @export_function ColumnView get_n_columns Int64\n\n    @add_widget_signals ColumnView\n    @add_signal_activate ColumnView\n\n    Base.show(io::IO, x::ColumnView) = show_aux(io, x, :n_rows, :n_columns)\n\n###### header_bar.jl\n\n    @export_type HeaderBar Widget\n    @declare_native_widget HeaderBar\n\n    HeaderBar() = HeaderBar(detail._HeaderBar())\n    HeaderBar(internal::Ptr{Cvoid}) = HeaderBar(detail._HeaderBar(internal))\n    HeaderBar(layout::String) = HeaderBar(detail._HeaderBar(layout))\n\n    @export_function HeaderBar set_layout! Cvoid String layout\n    @export_function HeaderBar get_layout String\n    @export_function HeaderBar set_show_title_buttons! Cvoid Bool b\n    @export_function HeaderBar get_show_title_buttons Bool\n\n    set_title_widget!(header_bar::HeaderBar, widget::Widget) = detail.set_title_widget!(header_bar._internal, as_widget_pointer(widget))\n    export set_title_widget!\n\n    @export_function HeaderBar remove_title_widget! Cvoid\n\n    push_front!(header_bar::HeaderBar, widget::Widget) = detail.push_front!(header_bar._internal, as_widget_pointer(widget))\n    export push_front!\n\n    push_back!(header_bar::HeaderBar, widget::Widget) = detail.push_back!(header_bar._internal, as_widget_pointer(widget))\n    export push_back!\n\n    remove!(header_bar::HeaderBar, widget::Widget) = detail.remove!(header_bar._internal, as_widget_pointer(widget))\n    export remove!\n\n    @add_widget_signals HeaderBar\n\n    Base.show(io::IO, x::HeaderBar) = show_aux(io, x, :layout)\n\n###### paned.jl\n\n    @export_type Paned Widget\n    @declare_native_widget Paned\n\n    Paned(orientation::Orientation) = Paned(detail._Paned(orientation))\n\n    function Paned(orientation::Orientation, start_child::Widget, end_child::Widget) ::Paned\n        out = Paned(orientation)\n        set_start_child!(out, start_child)\n        set_end_child!(out, end_child)\n        return out\n    end\n\n    @export_function Paned get_position Cint\n    @export_function Paned set_position! Cvoid Integer position\n\n    @export_function Paned set_has_wide_handle! Cvoid Bool b\n    @export_function Paned get_has_wide_handle Bool\n    @export_function Paned set_orientation! Cvoid Orientation orientation\n    @export_function Paned get_orientation Orientation\n\n    @export_function Paned set_start_child_resizable! Cvoid Bool b\n    @export_function Paned get_start_child_resizable Bool\n    @export_function Paned set_start_child_shrinkable! Cvoid Bool b\n    @export_function Paned get_start_child_shrinkable Bool\n\n    set_start_child!(paned::Paned, child::Widget) = detail.set_start_child!(paned._internal, as_widget_pointer(child))\n    export set_start_child!\n\n    @export_function Paned remove_start_child! Cvoid\n\n    @export_function Paned set_end_child_resizable! Cvoid Bool b\n    @export_function Paned get_end_child_resizable Bool\n    @export_function Paned set_end_child_shrinkable! Cvoid Bool b\n    @export_function Paned get_end_child_shrinkable Bool\n\n    set_end_child!(paned::Paned, child::Widget) = detail.set_end_child!(paned._internal, as_widget_pointer(child))\n    export set_end_child!\n\n    @export_function Paned remove_end_child! Cvoid\n\n    Base.show(io::IO, x::Paned) = show_aux(io, x, :start_child_resizable, :start_child_shrinkable, :end_child_resizable, :end_child_shrinkable)\n\n###### progress_bar.jl\n\n    @export_type ProgressBar Widget\n    @declare_native_widget ProgressBar\n\n    ProgressBar() = ProgressBar(detail._ProgressBar())\n\n    @export_function ProgressBar pulse Cvoid\n    @export_function ProgressBar set_fraction! Cvoid AbstractFloat => Cfloat zero_to_one\n    @export_function ProgressBar get_fraction Cfloat\n    @export_function ProgressBar set_is_inverted! Cvoid Bool b\n    @export_function ProgressBar get_is_inverted Bool\n    @export_function ProgressBar set_text! Cvoid String text\n    @export_function ProgressBar get_text String\n    @export_function ProgressBar set_show_text! Cvoid Bool b\n    @export_function ProgressBar get_show_text Bool\n    @export_function ProgressBar set_orientation! Cvoid Orientation orientation\n    @export_function ProgressBar get_orientation Orientation\n\n    Base.show(io::IO, x::ProgressBar) = show_aux(io, x, :fraction, :orientation, :show_text, :text)\n\n###### spinner.jl\n\n    @export_type Spinner Widget\n    @declare_native_widget Spinner\n\n    Spinner() = Spinner(detail._Spinner())\n\n    @export_function Spinner set_is_spinning! Cvoid Bool b\n    @export_function Spinner get_is_spinning Bool\n    @export_function Spinner start! Cvoid\n    @export_function Spinner stop! Cvoid\n\n    Base.show(io::IO, x::Spinner) = show_aux(io, x)\n\n###### revealer.jl\n\n    @export_enum RevealerTransitionType begin\n        REVEALER_TRANSITION_TYPE_NONE\n        REVEALER_TRANSITION_TYPE_CROSSFADE\n        REVEALER_TRANSITION_TYPE_SLIDE_RIGHT\n        REVEALER_TRANSITION_TYPE_SLIDE_LEFT\n        REVEALER_TRANSITION_TYPE_SLIDE_UP\n        REVEALER_TRANSITION_TYPE_SLIDE_DOWN\n        REVEALER_TRANSITION_TYPE_SWING_RIGHT\n        REVEALER_TRANSITION_TYPE_SWING_LEFT\n        REVEALER_TRANSITION_TYPE_SWING_UP\n        REVEALER_TRANSITION_TYPE_SWING_DOWN\n    end\n\n    @export_type Revealer Widget\n    @declare_native_widget Revealer\n\n    Revealer(transition_type::RevealerTransitionType = REVEALER_TRANSITION_TYPE_CROSSFADE) = Revealer(detail._Revealer(transition_type))\n    function Revealer(widget::Widget, transition_type::RevealerTransitionType = REVEALER_TRANSITION_TYPE_CROSSFADE) :: Revealer\n        out = Revealer(transition_type)\n        set_child!(out, widget)\n        return out\n    end\n\n    set_child!(revealer::Revealer, child::Widget) = detail.set_child!(revealer._internal, as_widget_pointer(child))\n    export set_child!\n\n    @export_function Revealer remove_child! Cvoid\n    @export_function Revealer set_is_revealed! Cvoid Bool child_visible\n    @export_function Revealer get_is_revealed Bool\n    @export_function Revealer set_transition_type! Cvoid RevealerTransitionType type\n    @export_function Revealer get_transition_type RevealerTransitionType\n\n    set_transition_duration!(revealer::Revealer, duration::Time) = detail.set_transition_duration!(revealer._internal, as_microseconds(duration))\n    export set_transition_duration!\n\n    get_transition_duration(revealer::Revealer) ::Time = microseconds(detail.get_transition_duration(revealer._internal))\n    export get_transition_duration\n\n    @add_widget_signals Revealer\n    @add_signal_revealed Revealer\n\n    Base.show(io::IO, x::Revealer) = show_aux(io, x, :is_revealed, :transition_type)\n\n###### action_bar.jl\n\n    @export_type ActionBar Widget\n    @declare_native_widget ActionBar\n\n    function push_back!(action_bar::ActionBar, widget::Widget)\n        detail.push_back!(action_bar._internal, as_widget_pointer(widget))\n    end\n    export push_back!\n\n    function push_front!(action_bar::ActionBar, widget::Widget)\n        detail.push_front!(action_bar._internal, as_widget_pointer(widget))\n    end\n    export push_front!\n\n    function set_center_widget(action_bar::ActionBar, widget::Widget)\n        detail.set_center_widget!(action_bar._internal, as_widget_pointer(widget))\n    end\n    export insert_after!\n\n    function remove!(action_bar::ActionBar, widget::Widget)\n        detail.remove!(action_bar._internal, as_widget_pointer(widget))\n    end\n    export remove!\n\n    @export_function ActionBar remove_center_child! Cvoid\n    @export_function ActionBar set_is_revealed! Cvoid Bool b\n    @export_function ActionBar get_is_revealed Bool\n\n    @add_widget_signals ActionBar\n\n    Base.show(io::IO, x::ActionBar) = show_aux(io, x, :is_revealed)\n\n###### scale.jl\n\n    @export_type Scale Widget\n    @declare_native_widget Scale\n\n    function Scale(lower::Number, upper::Number, step_increment::Number, orientation::Orientation = ORIENTATION_HORIZONTAL)\n        return Scale(detail._Scale(\n            convert(Cfloat, lower),\n            convert(Cfloat, upper),\n            convert(Cfloat, step_increment),\n            orientation\n        ))\n    end\n\n    get_adjustment(scale::Scale) ::Adjustment = Adjustment(detail.get_adjustment(scale._internal))\n    export get_adjustment\n\n    @export_function Scale get_lower Cfloat\n    @export_function Scale get_upper Cfloat\n    @export_function Scale get_step_increment Cfloat\n    @export_function Scale get_value Cfloat\n\n    @export_function Scale set_lower! Cvoid Number => Cfloat value\n    @export_function Scale set_upper! Cvoid Number => Cfloat value\n    @export_function Scale set_step_increment! Cvoid Number => Cfloat value\n    @export_function Scale set_value! Cvoid Number => Cfloat value\n\n    @export_function Scale set_should_draw_value! Cvoid Bool b\n    @export_function Scale get_should_draw_value Bool\n    @export_function Scale set_has_origin! Cvoid Bool b\n    @export_function Scale get_has_origin Bool\n    @export_function Scale set_orientation! Cvoid Orientation orientation\n    @export_function Scale get_orientation Orientation\n\n    function add_mark!(scale::Scale, value::Number, position::RelativePosition, label::String = \"\")\n        detail.add_mark!(scale._internal, convert(Cfloat, value), position, label)\n    end\n    export add_mark!\n\n    @export_function Scale clear_marks! Cvoid\n\n    @add_widget_signals Scale\n    @add_signal_value_changed Scale\n\n    Base.show(io::IO, x::Scale) = show_aux(io, x, :value, :lower, :upper, :step_increment)\n\n###### spin_button.jl\n\n    @export_type SpinButton Widget\n    @declare_native_widget SpinButton\n\n    function SpinButton(lower::Number, upper::Number, step_increment::Number, orientation::Orientation = ORIENTATION_HORIZONTAL)\n        return SpinButton(detail._SpinButton(\n            convert(Cfloat, lower),\n            convert(Cfloat, upper),\n            convert(Cfloat, step_increment),\n            orientation\n        ))\n    end\n\n    get_adjustment(spin_button::SpinButton) ::Adjustment = Adjustment(detail.get_adjustment(spin_button._internal))\n    export get_adjustment\n\n    @export_function SpinButton get_lower Cfloat\n    @export_function SpinButton get_upper Cfloat\n    @export_function SpinButton get_step_increment Cfloat\n    @export_function SpinButton get_value Cfloat\n\n    @export_function SpinButton set_lower! Cvoid Number => Cfloat value\n    @export_function SpinButton set_upper! Cvoid Number => Cfloat value\n    @export_function SpinButton set_step_increment! Cvoid Number => Cfloat value\n    @export_function SpinButton set_value! Cvoid Number => Cfloat value\n\n    @export_function SpinButton set_n_digits! Cvoid Integer n\n    @export_function SpinButton get_n_digits Int64\n    @export_function SpinButton set_should_wrap! Cvoid Bool b\n    @export_function SpinButton get_should_wrap Bool\n    @export_function SpinButton set_acceleration_rate! Cvoid Number => Cfloat factor\n    @export_function SpinButton get_acceleration_rate Cfloat\n    @export_function SpinButton set_should_snap_to_ticks! Cvoid Bool b\n    @export_function SpinButton get_should_snap_to_ticks Bool\n    @export_function SpinButton set_allow_only_numeric! Cvoid Bool b\n    @export_function SpinButton get_allow_only_numeric Bool\n    @export_function SpinButton set_orientation! Cvoid Orientation orientation\n    @export_function SpinButton get_orientation Orientation\n\n    function set_text_to_value_function!(f, spin_button::SpinButton, data::Data_t) where Data_t\n        set_allow_only_numeric!(spin_button, false)\n        typed_f = TypedFunction(f, AbstractFloat, (SpinButton, String, Data_t))\n        detail.set_text_to_value_function!(spin_button._internal, function (spin_button_ref, text::String)\n            return typed_f(SpinButton(spin_button_ref[]), text, data)\n        end)\n    end\n    function set_text_to_value_function!(f, spin_button::SpinButton)\n        set_allow_only_numeric!(spin_button, false)\n        typed_f = TypedFunction(f, AbstractFloat, (SpinButton, String))\n        detail.set_text_to_value_function!(spin_button._internal, function (spin_button_ref, text::String)\n            return typed_f(SpinButton(spin_button_ref[]), text)\n        end)\n    end\n    export set_text_to_value_function!\n\n    @export_function SpinButton reset_value_to_text_function! Cvoid\n\n    function set_value_to_text_function!(f, spin_button::SpinButton, data::Data_t) where Data_t\n        set_allow_only_numeric!(spin_button, false)\n        typed_f = TypedFunction(f, String, (SpinButton, AbstractFloat, Data_t))\n        detail.set_value_to_text_function!(spin_button._internal, function (spin_button_ref, value::Cfloat)\n            return typed_f(SpinButton(spin_button_ref[]), value, data)\n        end)\n    end\n    function set_value_to_text_function!(f, spin_button::SpinButton)\n        set_allow_only_numeric!(spin_button, false)\n        typed_f = TypedFunction(f, String, (SpinButton, AbstractFloat))\n        detail.set_value_to_text_function!(spin_button._internal, function (spin_button_ref, value::Cfloat)\n            return typed_f(SpinButton(spin_button_ref[]), value)\n        end)\n    end\n    export set_value_to_text_function!\n\n    @export_function SpinButton reset_text_to_value_function! Cvoid\n\n    @add_widget_signals SpinButton\n    @add_signal_value_changed SpinButton\n    @add_signal_wrapped SpinButton\n\n    Base.show(io::IO, x::SpinButton) = show_aux(io, x, :value, :lower, :upper, :step_increment, :orientation)\n\n###### scrollbar.jl\n\n    @export_type Scrollbar Widget\n    @declare_native_widget Scrollbar\n\n    Scrollbar(orientation::Orientation, adjustment::Adjustment) = Scrollbar(detail._Scrollbar(orientation, adjustment._internal))\n\n    get_adjustment(scrollbar::Scrollbar) ::Adjustment = Adjustment(detail.get_adjustment(scrollbar._internal))\n    export get_adjustment\n\n    @export_function Scrollbar set_orientation! Cvoid Orientation orientation\n    @export_function Scrollbar get_orientation Orientation\n\n    @add_widget_signals Scrollbar\n    Base.show(io::IO, x::Scrollbar) = show_aux(io, x, :orientation, :adjustment)\n\n###### separator.jl\n\n    @export_type Separator Widget\n    @declare_native_widget Separator\n\n    Separator(orientation::Orientation = ORIENTATION_HORIZONTAL) = Separator(detail._Separator(orientation))\n\n    @export_function Separator set_orientation! Cvoid Orientation orientation\n    @export_function Separator get_orientation Orientation\n\n    @add_widget_signals Separator\n    Base.show(io::IO, x::Separator) = show_aux(io, x)\n\n####### frame_clock.jl\n\n    @export_type FrameClock SignalEmitter\n    FrameClock(internal::Ptr{Cvoid}) = FrameClock(detail._FrameClock(internal))\n\n    get_time_since_last_frame(frame_clock::FrameClock) ::Time = microseconds(detail.get_time_since_last_frame(frame_clock._internal))\n    export get_time_since_last_frame\n\n    get_target_frame_duration(frame_clock::FrameClock) ::Time = microseconds(detail.get_target_frame_duration(frame_clock._internal))\n    export get_target_frame_duration\n\n    @add_signal_update FrameClock\n    @add_signal_paint FrameClock\n\n    Base.show(io::IO, x::FrameClock) = show_aux(io, x, :time_since_last_frame)\n\n####### widget.jl\n\n    function as_widget_pointer(widget::Widget)\n        as_native::Widget = widget\n        seen = Set{Type}()\n        while !is_native_widget(as_native)\n            if typeof(as_native) in seen\n                detail.log_critical(\"In as_widget_pointer: Type `$(typeof(as_native))`` has a malformed `as_widget` definition, this usually means `get_top_level_widget(x)` returns x itself, as opposed to the top-level widget component of x.\", MOUSETRAP_DOMAIN)\n                return Separator(opacity = 0.0)._internal.cpp_object # return placeholder to prevent segfault\n            else\n                push!(seen, typeof(as_native))\n            end\n            as_native = get_top_level_widget(as_native)\n        end\n        return as_native._internal.cpp_object\n    end\n    # no export\n\n    macro export_widget_function(name, return_t)\n\n        return_t = esc(return_t)\n\n        Mousetrap.eval(:(export $name))\n        return :(function $name(widget::Widget)\n            return Base.convert($return_t, detail.$name(as_widget_pointer(widget)))\n        end)\n        return out\n    end\n\n    macro export_widget_function(name, return_t, arg1_type, arg1_name)\n\n        return_t = esc(return_t)\n\n        if arg1_type isa Expr\n            arg1_origin_type = arg1_type.args[2]\n            arg1_destination_type = arg1_type.args[3]\n        else\n            arg1_origin_type = arg1_type\n            arg1_destination_type = arg1_type\n        end\n        arg1_name = esc(arg1_name)\n\n        Mousetrap.eval(:(export $name))\n        out = Expr(:toplevel)\n        return :(function $name(widget::Widget, $arg1_name::$arg1_origin_type)\n            return Base.convert($return_t, detail.$name(as_widget_pointer(widget), convert($arg1_destination_type, $arg1_name)))\n        end)\n        return out\n    end\n\n    @export_enum TickCallbackResult begin\n        TICK_CALLBACK_RESULT_CONTINUE\n        TICK_CALLBACK_RESULT_DISCONTINUE\n    end\n\n    @export_widget_function activate! Cvoid\n    @export_widget_function set_size_request! Cvoid Vector2f size\n    @export_widget_function get_size_request Vector2f\n\n    @export_widget_function set_margin_top! Cvoid Number => Cfloat margin\n    @export_widget_function get_margin_top Cfloat\n    @export_widget_function set_margin_bottom! Cvoid Number => Cfloat margin\n    @export_widget_function get_margin_bottom Cfloat\n    @export_widget_function set_margin_start! Cvoid Number => Cfloat margin\n    @export_widget_function get_margin_start Cfloat\n    @export_widget_function set_margin_end! Cvoid Number => Cfloat margin\n    @export_widget_function get_margin_end Cfloat\n    @export_widget_function set_margin_horizontal! Cvoid Number => Cfloat margin\n    @export_widget_function set_margin_vertical! Cvoid Number => Cfloat margin\n    @export_widget_function set_margin! Cvoid Number => Cfloat margin\n\n    @export_widget_function set_expand_horizontally! Cvoid Bool b\n    @export_widget_function get_expand_horizontally Bool\n    @export_widget_function set_expand_vertically! Cvoid Bool b\n    @export_widget_function get_expand_vertically Bool\n    @export_widget_function set_expand! Cvoid Bool b\n\n    @export_widget_function set_horizontal_alignment! Cvoid Alignment alignment\n    @export_widget_function get_horizontal_alignment Alignment\n    @export_widget_function set_vertical_alignment! Cvoid Alignment alignment\n    @export_widget_function get_vertical_alignment Alignment\n    @export_widget_function set_alignment! Cvoid Alignment both\n\n    @export_widget_function set_opacity! Cvoid Number => Cfloat opacity\n    @export_widget_function get_opacity Cfloat\n    @export_widget_function set_is_visible! Cvoid Bool b\n    @export_widget_function get_is_visible Bool\n\n    @export_widget_function set_tooltip_text! Cvoid String text\n\n    set_tooltip_widget!(widget::Widget, tooltip::Widget) = detail.set_tooltip_widget!(as_widget_pointer(widget), as_widget_pointer(tooltip))\n    export set_tooltip_widget!\n\n    @export_widget_function remove_tooltip_widget! Cvoid\n\n    @export_widget_function set_cursor! Cvoid CursorType cursor\n\n    function set_cursor_from_image!(widget::Widget, image::Image, offset::Vector2i = Vector2i(0, 0))\n        detail.set_cursor_from_image!(as_widget_pointer(widget), image._internal, offset.x, offset.y)\n    end\n    export set_cursor_from_image!\n\n    @export_widget_function hide! Cvoid\n    @export_widget_function show! Cvoid\n\n    function add_controller!(widget::Widget, controller::EventController)\n        detail.add_controller!(as_widget_pointer(widget), controller._internal.cpp_object)\n    end\n    export add_controller!\n\n    function remove_controller!(widget::Widget, controller::EventController)\n        detail.remove_controller!(as_widget_pointer(widget), controller._internal.cpp_object)\n    end\n    export remove_controller!\n\n    @export_widget_function set_is_focusable! Cvoid Bool b\n    @export_widget_function get_is_focusable Bool\n    @export_widget_function grab_focus! Cvoid\n    @export_widget_function get_has_focus Bool\n    @export_widget_function set_focus_on_click! Cvoid Bool b\n    @export_widget_function get_focus_on_click Bool\n    @export_widget_function set_can_respond_to_input! Cvoid Bool b\n    @export_widget_function get_can_respond_to_input Bool\n\n    @export_widget_function get_is_realized Bool\n    @export_widget_function get_minimum_size Vector2f\n    @export_widget_function get_natural_size Vector2f\n    @export_widget_function get_position Vector2f\n    @export_widget_function get_allocated_size Vector2f\n    @export_widget_function calculate_monitor_dpi Cfloat\n    @export_widget_function get_scale_factor Cfloat\n\n    @export_widget_function unparent! Cvoid\n\n    @export_widget_function set_hide_on_overflow! Cvoid Bool b\n    @export_widget_function get_hide_on_overflow Bool\n\n    function set_tick_callback!(f, widget::Widget, data::Data_t) where Data_t\n        typed_f = TypedFunction(f, TickCallbackResult, (FrameClock, Data_t))\n        detail.set_tick_callback!(as_widget_pointer(widget), function(frame_clock_ref)\n            typed_f(FrameClock(frame_clock_ref[]), data)\n        end)\n    end\n    function set_tick_callback!(f, widget::Widget)\n        typed_f = TypedFunction(f, TickCallbackResult, (FrameClock,))\n        detail.set_tick_callback!(as_widget_pointer(widget), function(frame_clock_ref)\n            typed_f(FrameClock(frame_clock_ref[]))\n        end)\n    end\n    export set_tick_callback!\n\n    @export_widget_function remove_tick_callback! Cvoid\n\n    function set_listens_for_shortcut_action!(widget::Widget, action::Action) ::Cvoid\n        detail.set_listens_for_shortcut_action!(as_widget_pointer(widget), action._internal)\n    end\n    export set_listens_for_shortcut_action!\n\n    Base.hash(x::Widget) = UInt64(Mousetrap.detail.as_native_widget(Mousetrap.as_widget_pointer(x)))\n\n####### clipboard.jl\n\n    @export_type Clipboard SignalEmitter\n\n    function Clipboard(internal::Ptr{Cvoid})\n        out = Clipboard(detail._Clipboard(internal))\n    end\n\n    @export_function Clipboard get_is_local Bool\n    @export_function Clipboard contains_string Bool\n\n    set_string!(clipboard::Clipboard, string::String) = detail.set_string!(clipboard._internal, string)\n    export set_string!\n\n    function get_string(f, clipboard::Clipboard, data::Data_t) where Data_t\n        typed_f = TypedFunction(f, Cvoid, (Clipboard, String, Data_t))\n        detail.get_string(clipboard._internal, function(internal_ref, string)\n            typed_f(Clipboard(internal_ref[]), String(string), data)\n        end)\n    end\n    function get_string(f, clipboard::Clipboard)\n        typed_f = TypedFunction(f, Cvoid, (Clipboard, String))\n        detail.get_string(clipboard._internal, function(internal_ref, string)\n            typed_f(Clipboard(internal_ref[]), String(string))\n        end)\n    end\n    export get_string\n\n    @export_function Clipboard contains_image Bool\n\n    set_image!(clipboard::Clipboard, image::Image) = detail.set_image!(clipboard._internal, image._internal)\n    export set_image!\n\n    function get_image(f, clipboard::Clipboard, data::Data_t) where Data_t\n        typed_f = TypedFunction(f, Cvoid, (Clipboard, Image, Data_t))\n        detail.get_image(clipboard._internal, function(internal_ref, image_ref)\n            typed_f(Clipboard(internal_ref[]), Image(image_ref[], data))\n        end)\n    end\n    function get_image(f, clipboard::Clipboard)\n        typed_f = TypedFunction(f, Cvoid, (Clipboard, Image))\n        detail.get_image(clipboard._internal, function(internal_ref, image_ref)\n            typed_f(Clipboard(internal_ref[]), Image(image_ref[]))\n        end)\n    end\n    export get_image\n\n    @export_function Clipboard contains_file Bool\n\n    set_file!(clipboard::Clipboard, file::FileDescriptor) = detail.set_file!(clipboard._internal, file._internal)\n    export set_file!\n\n    get_clipboard(widget::Widget) ::Clipboard = Clipboard(detail.get_clipboard(as_widget_pointer(widget)))\n    export get_clipboard\n\n    Base.show(io::IO, x::Clipboard) = show_aux(io, x)\n\n### opengl_common.jl\n\n    macro define_opengl_error_type(name)\n        message =  \"In Mousetrap::$name(): `$name` cannot be instantiated, because the Mousetrap OpenGL component is disabled for MacOS. It and any function operating on it cannot be used in any way.\\n\\nSee the manual chapter on native rendering for more information.\"\n        return :(struct $name\n            function $name()\n                Mousetrap.log_fatal(Mousetrap.MOUSETRAP_DOMAIN, $message)\n                return new()\n            end\n        end)\n    end\n\n@static if MOUSETRAP_ENABLE_OPENGL_COMPONENT\n\n####### blend_mode.jl\n\n    @export_enum BlendMode begin\n        BLEND_MODE_NONE\n        BLEND_MODE_NORMAL\n        BLEND_MODE_ADD\n        BLEND_MODE_SUBTRACT\n        BLEND_MODE_REVERSE_SUBTRACT\n        BLEND_MODE_MULTIPLY\n        BLEND_MODE_MIN\n        BLEND_MODE_MAX\n    end\n\n    function set_current_blend_mode(blend_mode::BlendMode; allow_alpha_blending = true)\n        detail.set_current_blend_mode(blend_mode, allow_alpha_blending)\n    end\n    export set_current_blend_mode\n\n####### gl_transform.jl\n\n    @export_type GLTransform SignalEmitter\n    GLTransform() = GLTransform(detail._GLTransform())\n\n    import Base.setindex!\n    function Base.setindex!(transform::GLTransform, x::Integer, y::Integer, value::Number)\n        if x == 0 || x > 4 || y == 0 || y > 4\n            throw(BoundsError(transform, (x, y)))\n        end\n\n        detail.setindex!(transform._internal, from_julia_index(x), from_julia_index(y), convert(Float32, value))\n    end\n\n    import Base.getindex\n    function Base.getindex(transform::GLTransform, x::Integer, y::Integer) ::Cfloat\n        if x == 0 || x > 4 || y == 0 || y > 4\n            throw(BoundsError(transform, (x, y)))\n        end\n\n        return detail.getindex(transform._internal, from_julia_index(x), from_julia_index(y))\n    end\n\n    apply_to(transform::GLTransform, v::Vector2f) ::Vector2f = detail.apply_to_2f(transform, v.x, v.y)\n    apply_to(transform::GLTransform, v::Vector3f) ::Vector3f = detail.apply_to_3f(transform, v.x, v.y, v.z)\n    export apply_to\n\n    combine_with(self::GLTransform, other::GLTransform) = GLTransform(detail.combine_with(self._internal, other._internal))\n    export combine_with\n\n    function rotate!(transform::GLTransform, angle::Angle, origin::Vector2f = Vector2f(0, 0))\n        detail.rotate!(transform._internal, convert(Float32, as_radians(angle)), origin.x, origin.y)\n    end\n    export rotate!\n\n    function translate!(transform::GLTransform, offset::Vector2f)\n        detail.translate!(transform._internal, offset.x, offset.y)\n    end\n    export translate!\n\n    function scale!(transform::GLTransform, x_scale::AbstractFloat, y_scale::AbstractFloat)\n        detail.scale!(transform._internal, convert(Float32, x_scale), convert(Float32, y_scale))\n    end\n    export scale!\n\n    @export_function GLTransform reset! Cvoid\n\n    function Base.:(==)(a::GLTransform, b::GLTransform)\n        for i in 1:4\n            for j in 1:4\n                if a[i, j] != b[i, j]\n                    return false\n                end\n            end\n        end\n        return true\n    end\n\n    Base.:(!=)(a::GLTransform, b::GLTransform) = !(a == b)\n    Base.show(io::IO, x::GLTransform) = show_aux(io, x)\n\n###### shader.jl\n\n    @export_type Shader SignalEmitter\n    Shader() = Shader(detail._Shader())\n\n    @export_enum ShaderType begin\n        SHADER_TYPE_FRAGMENT\n        SHADER_TYPE_VERTEX\n    end\n\n    @export_function Shader get_program_id Cuint\n    @export_function Shader get_fragment_shader_id Cuint\n    @export_function Shader get_vertex_shader_id Cuint\n\n    function create_from_string!(shader::Shader, type::ShaderType, glsl_code::String) ::Bool\n        return detail.create_from_string!(shader._internal, type, glsl_code)\n    end\n    export create_from_string!\n\n    function create_from_file!(shader::Shader, type::ShaderType, path::String) ::Bool\n        return detail.create_from_file!(shader._internal, type, path)\n    end\n    export create_from_file!\n\n    @export_function Shader get_uniform_location Cint String name\n\n    @export_function Shader set_uniform_float! Cvoid String name Cfloat float\n    @export_function Shader set_uniform_int! Cvoid String name Cint float\n    @export_function Shader set_uniform_uint! Cvoid String name Cuint float\n\n    set_uniform_vec2!(shader::Shader, name::String, vec2::Vector2f) = detail.set_uniform_vec2!(shader._internal, name, vec2)\n    export set_uniform_vec2!\n\n    set_uniform_vec3!(shader::Shader, name::String, vec3::Vector3f) = detail.set_uniform_vec3!(shader._internal, name, vec3)\n    export set_uniform_vec3!\n\n    set_uniform_vec4!(shader::Shader, name::String, vec4::Vector4f) = detail.set_uniform_vec4!(shader._internal, name, vec4)\n    export set_uniform_vec4!\n\n    set_uniform_transform!(shader::Shader, name::String, transform::GLTransform) = detail.set_uniform_transform!(shader._internal, name, transform._internal)\n    export set_uniform_transform!\n\n    get_vertex_position_location() = detail.shader_get_vertex_position_location()\n    export get_vertex_position_location\n\n    get_vertex_color_location() = detail.shader_get_vertex_color_location()\n    export get_vertex_color_location\n\n    get_vertex_texture_coordinate_location() = detail.shader_get_vertex_texture_coordinate_location()\n    export get_vertex_texture_coordinate_location\n\n    Base.show(io::IO, x::Shader) = show_aux(io, x)\n\n###### texture.jl\n\n    abstract type TextureObject <: SignalEmitter end\n    export TextureObject\n\n    @export_enum TextureWrapMode begin\n        TEXTURE_WRAP_MODE_ZERO\n        TEXTURE_WRAP_MODE_ONE\n        TEXTURE_WRAP_MODE_REPEAT\n        TEXTURE_WRAP_MODE_MIRROR\n        TEXTURE_WRAP_MODE_STRETCH\n    end\n\n    @export_enum TextureScaleMode begin\n        TEXTURE_SCALE_MODE_NEAREST\n        TEXTURE_SCALE_MODE_LINEAR\n    end\n\n    @export_type Texture TextureObject\n    Texture() = Texture(detail._Texture())\n\n    @export_type RenderTexture TextureObject\n    RenderTexture() = RenderTexture(detail._RenderTexture())\n\n    download(texture::TextureObject) ::Image = Image(detail.texture_download(texture._internal.cpp_object))\n    export download\n\n    bind(texture::TextureObject) = detail.texture_bind(texture._internal.cpp_object)\n    export bind\n\n    unbind(texture::TextureObject) = detail.texture_unbind(texture._internal.cpp_object)\n    export unbind\n\n    create!(texture::TextureObject, width::Integer, height::Integer) = detail.texture_create!(texture._internal.cpp_object, width, height)\n    export create!\n\n    create_from_image!(texture::TextureObject, image::Image) = detail.texture_create_from_image!(texture._internal.cpp_object, image._internal)\n    export create_from_image!\n\n    set_wrap_mode!(texture::TextureObject, mode::TextureWrapMode) = detail.texture_set_wrap_mode!(texture._internal.cpp_object, mode)\n    export set_wrap_mode!\n\n    set_scale_mode!(texture::TextureObject, mode::TextureScaleMode) = detail.texture_set_scale_mode!(texture._internal.cpp_object, mode)\n    export set_scale_mode!\n\n    get_wrap_mode(texture::TextureObject) ::TextureWrapMode = detail.texture_get_wrap_mode(texture._internal.cpp_object)\n    export get_wrap_mode\n\n    get_scale_mode(texture::TextureObject) ::TextureScaleMode = detail.texture_get_scale_mode(texture._internal.cpp_object)\n    export get_scale_mode\n\n    get_size(texture::TextureObject) ::Vector2i = detail.texture_get_size(texture._internal.cpp_object)\n    export get_size\n\n    get_native_handle(texture::TextureObject) ::Cuint = detail.texture_get_native_handle(texture._internal.cpp_object)\n    export get_native_handle\n\n    bind_as_render_target(render_texture::RenderTexture) = detail.render_texture_bind_as_render_target(render_texture._internal.cpp_object)\n    export bind_as_render_target\n\n    unbind_as_render_target(render_texture::RenderTexture) = detail.render_texture_unbind_as_render_target(render_texture._internal.cpp_object)\n    export unbind_as_render_target\n\n    Base.show(io::IO, x::TextureObject) = show_aux(io, x, :native_handle)\n\n###### shape.jl\n\n    @export_type Shape SignalEmitter\n    Shape() = Shape(detail._Shape())\n\n    @export_function Shape get_native_handle Cuint\n\n    as_point!(shape::Shape, position::Vector2f) = detail.as_point!(shape._internal, position)\n    export as_point!\n\n    function Point(position::Vector2f) ::Shape\n        out = Shape()\n        as_point!(out, position)\n        return out\n    end\n    export Point\n\n    function as_points!(shape::Shape, positions::Vector{Vector2f})\n        if isempty(positions)\n            @log_critical MOUSETRAP_DOMAIN \"In as_points!: Vector `positions` is empty.\"\n        end\n\n        return detail.as_points!(shape._internal, positions)\n    end\n    export as_points!\n\n    function Points(positions::Vector{Vector2f}) ::Shape\n        out = Shape()\n        as_points!(out, positions)\n        return out\n    end\n    export Points\n\n    as_triangle!(shape::Shape, a::Vector2f, b::Vector2f, c::Vector2f) = detail.as_triangle!(shape._internal, a, b, c)\n    export as_triangle!\n\n    function Triangle(a::Vector2f, b::Vector2f, c::Vector2f) ::Shape\n        out = Shape()\n        as_triangle!(out, a, b, c)\n        return out\n    end\n    export Triangle\n\n    as_rectangle!(shape::Shape, top_left::Vector2f, size::Vector2f) = detail.as_rectangle!(shape._internal, top_left, size)\n    export as_rectangle!\n\n    function Rectangle(top_left::Vector2f, size::Vector2f) ::Shape\n        out = Shape()\n        as_rectangle!(out, top_left, size)\n        return out\n    end\n    export Rectangle\n\n    as_circle!(shape::Shape, center::Vector2f, radius::Number, n_outer_vertices::Integer) = detail.as_circle!(shape._internal, center, convert(Cfloat, radius), n_outer_vertices)\n    export as_circle!\n\n    function Circle(center::Vector2f, radius::Number, n_outer_vertices::Integer) ::Shape\n        out = Shape()\n        as_circle!(out, center, radius, n_outer_vertices)\n        return out\n    end\n    export Circle\n\n    as_ellipse!(shape::Shape, center::Vector2f, x_radius::Number, y_radius::Number, n_outer_vertices::Integer) = detail.as_ellipse!(shape._internal, center, convert(Cfloat, x_radius), convert(Cfloat, y_radius), n_outer_vertices)\n    export as_ellipse!\n\n    function Ellipse(center::Vector2f, x_radius::Number, y_radius::Number, n_outer_vertices::Integer) ::Shape\n        out = Shape()\n        as_ellipse!(out, center, x_radius, y_radius, n_outer_vertices)\n        return out\n    end\n    export Ellipse\n\n    as_line!(shape::Shape, a::Vector2f, b::Vector2f) = detail.as_line!(shape._internal, a, b)\n    export as_line!\n\n    function Line(a::Vector2f, b::Vector2f)\n        out = Shape()\n        as_line!(out, a, b)\n        return out\n    end\n    export Line\n\n    as_lines!(shape::Shape, points::Vector{Pair{Vector2f, Vector2f}}) = detail.as_lines!(shape._internal, points)\n    export as_lines!\n\n    function Lines(points::Vector{Pair{Vector2f, Vector2f}})\n        out = Shape()\n        as_lines!(out, points)\n        return out\n    end\n    export Lines\n\n    as_line_strip!(shape::Shape, points::Vector{Vector2f}) = detail.as_line_strip!(shape._internal, points)\n    export as_line_strip!\n\n    function LineStrip(points::Vector{Vector2f})\n        out = Shape()\n        as_line_strip!(out, points)\n        return out\n    end\n    export LineStrip\n\n    as_polygon!(shape::Shape, points::Vector{Vector2f}) = detail.as_polygon!(shape._internal, points)\n    export as_polygon!\n\n    function Polygon(points::Vector{Vector2f})\n        out = Shape()\n        as_polygon!(out, points)\n        return out\n    end\n    export Polygon\n\n    function as_rectangular_frame!(shape::Shape, top_left::Vector2f, outer_size::Vector2f, x_width::Number, y_width::Number)\n        detail.as_rectangular_frame!(shape._internal, top_left, outer_size, convert(Cfloat, x_width), convert(Cfloat, y_width))\n    end\n    export as_rectangular_frame!\n\n    function RectangularFrame(top_left::Vector2f, outer_size::Vector2f, x_width::Number, y_width::Number)\n        out = Shape()\n        as_rectangular_frame!(out, top_left, outer_size, x_width, y_width)\n        return out\n    end\n    export RectangularFrame\n\n    function as_circular_ring!(shape::Shape, center::Vector2f, outer_radius::Number, thickness::Number, n_outer_vertices::Integer)\n        detail.as_circular_ring!(shape._internal, center, convert(Cfloat, outer_radius), convert(Cfloat, thickness), n_outer_vertices)\n    end\n    export as_circular_ring!\n\n    function CircularRing(center::Vector2f, outer_radius::Number, thickness::Number, n_outer_vertices::Integer)\n        out = Shape()\n        as_circular_ring!(out, center, outer_radius, thickness, n_outer_vertices)\n        return out\n    end\n    export CircularRing\n\n    function as_elliptical_ring!(shape::Shape, center::Vector2f, outer_x_radius::Number, outer_y_radius::Number, x_thickness::Number, y_thickness::Number, n_outer_vertices::Integer)\n        detail.as_elliptical_ring!(shape._internal, center, convert(Cfloat, outer_x_radius), convert(Cfloat, outer_y_radius), convert(Cfloat, x_thickness), convert(Cfloat, y_thickness), n_outer_vertices)\n    end\n    export as_elliptical_ring!\n\n    function EllipticalRing(center::Vector2f, outer_x_radius::Number, outer_y_radius::Number, x_thickness::Number, y_thickness::Number, n_outer_vertices::Integer) ::Shape\n        out = Shape()\n        as_elliptical_ring!(out, center, outer_x_radius, outer_y_radius, x_thickness, y_thickness, n_outer_vertices)\n        return out\n    end\n    export EllipticalRing\n\n    as_wireframe!(shape::Shape, points::Vector{Vector2f}) = detail.as_wireframe!(shape._internal, points)\n    export as_wireframe!\n\n    function Wireframe(points::Vector{Vector2f})\n        out = Shape()\n        as_wireframe!(out, points)\n        return out\n    end\n    export Wireframe\n\n    as_outline!(self::Shape, other::Shape) = detail.as_outline!(self._internal, other._internal)\n    export as_outline!\n\n    function Outline(other::Shape)\n        out = Shape()\n        as_outline!(out, other)\n        return out\n    end\n    export Outline\n\n    render(shape::Shape, shader::Shader, transform::GLTransform) = detail.render(shape._internal, shader._internal, transform._internal)\n    export render\n\n    function get_vertex_color(shape::Shape, index::Integer) ::RGBA\n        return detail.get_vertex_color(shape._internal, from_julia_index(index))\n    end\n    export get_vertex_color\n\n    function set_vertex_color!(shape::Shape, index::Integer, color::RGBA)\n        detail.set_vertex_color!(shape._internal, from_julia_index(index), color)\n    end\n    export set_vertex_color!\n\n    function get_vertex_texture_coordinate(shape::Shape, index::Integer) ::Vector2f\n        return detail.get_vertex_texture_coordinate(shape._internal, from_julia_index(index))\n    end\n    export get_vertex_texture_coordinate\n\n    function set_vertex_texture_coordinate!(shape::Shape, index::Integer, coordinate::Vector2f)\n        detail.set_vertex_texture_coordinate!(shape._internal, from_julia_index(index), coordinate)\n    end\n    export set_vertex_texture_coordinate!\n\n    function get_vertex_position(shape::Shape, index::Integer) ::Vector3f\n        detail.get_vertex_position(shape._internal, from_julia_index(index))\n    end\n    export get_vertex_position\n\n    function set_vertex_position!(shape::Shape, index::Integer, coordinate::Vector3f)\n        detail.set_vertex_position!(shape._internal, from_julia_index(index), coordinate)\n    end\n    export set_vertex_position!\n\n    @export_function Shape get_n_vertices Int64\n\n    @export_function Shape set_is_visible! Cvoid Bool b\n    @export_function Shape get_is_visible Bool\n\n    struct AxisAlignedRectangle\n        top_left::Vector2f\n        size::Vector2f\n    end\n    export AxisAlignedRectangle\n\n    @export_function Shape get_bounding_box AxisAlignedRectangle\n    @export_function Shape get_size Vector2f\n\n    @export_function Shape set_centroid! Cvoid Vector2f centroid\n    @export_function Shape get_centroid Vector2f\n    @export_function Shape set_top_left! Cvoid Vector2f top_left\n    @export_function Shape get_top_left Vector2f\n\n    function rotate!(shape::Shape, angle::Angle, origin::Vector2f = Vector2f(0, 0))\n        detail.rotate!(shape._internal, convert(Cfloat, as_radians(angle)), origin.x, origin.y)\n    end\n    export rotate!\n\n    set_texture!(shape::Shape, texture::TextureObject) = detail.set_texture!(shape._internal, texture._internal.cpp_object)\n    export set_texture!\n\n    remove_texture!(shape::Shape) = detail.set_texture!(shape._internal, Ptr{Cvoid}())\n    export remove_texture!\n\n    set_color!(shape::Shape, color::RGBA) = detail.set_color!(shape._internal, color)\n    set_color!(shape::Shape, color::HSVA) = detail.set_color!(shape._internal, hsva_to_rgba(color))\n    export set_color!\n\n    Base.show(io::IO, x::Shape) = show_aux(io, x, :native_handle)\n\n###### render_task.jl\n\n    @export_type RenderTask SignalEmitter\n\n    function RenderTask(shape::Shape;\n        shader::Union{Shader, Nothing} = nothing,\n        transform::Union{GLTransform, Nothing} = nothing,\n        blend_mode::BlendMode = BLEND_MODE_NORMAL\n    )\n        shader_ptr = isnothing(shader) ? Ptr{Cvoid}(0) : shader._internal.cpp_object\n        transform_ptr = isnothing(transform) ? Ptr{Cvoid}(0) : transform._internal.cpp_object\n\n        return RenderTask(detail._RenderTask(shape._internal, shader_ptr, transform_ptr, blend_mode))\n    end\n    export RenderTask\n\n    @export_function RenderTask render Cvoid\n\n    @export_function RenderTask set_uniform_float! Cvoid String name Cfloat v\n    @export_function RenderTask get_uniform_float Cfloat String name\n\n    @export_function RenderTask set_uniform_int! Cvoid String name Cint v\n    @export_function RenderTask get_uniform_int Cint String name\n\n    @export_function RenderTask set_uniform_uint! Cvoid String name Cuint v\n    @export_function RenderTask get_uniform_uint Cuint String name\n\n    set_uniform_vec2!(task::RenderTask, name::String, v::Vector2f) = detail.set_uniform_vec2!(task._internal, name, v)\n    export set_uniform_vec2!\n\n    get_uniform_vec2(task::RenderTask, name::String) ::Vector2f = detail.get_uniform_vec2(task._internal, name)\n    export get_uniform_vec2\n\n    set_uniform_vec3!(task::RenderTask, name::String, v::Vector3f) = detail.set_uniform_vec3!(task._internal, name, v)\n    export set_uniform_vec3!\n\n    get_uniform_vec3(task::RenderTask, name::String) ::Vector3f = detail.get_uniform_vec3(task._internal, name)\n    export get_uniform_vec3\n\n    set_uniform_vec4!(task::RenderTask, name::String, v::Vector4f) = detail.set_uniform_vec4!(task._internal, name, v)\n    export set_uniform_vec4!\n\n    get_uniform_vec4(task::RenderTask, name::String) ::Vector4f = detail.get_uniform_vec4(task._internal, name)\n    export get_uniform_vec4\n\n    set_uniform_rgba!(task::RenderTask, name::String, rgba::RGBA) = detail.set_uniform_rgba!(task._internal, name, rgba)\n    export set_uniform_rgba!\n\n    get_uniform_rgba(task::RenderTask, name::String) ::RGBA = detail.get_uniform_rgba(task._internal, name)\n    export get_uniform_rgba\n\n    set_uniform_hsva!(task::RenderTask, name::String, hsva::HSVA) = detail.set_uniform_hsva!(task._internal, name, hsva)\n    export set_uniform_hsva!\n\n    get_uniform_hsva(task::RenderTask, name::String) ::HSVA = detail.get_uniform_hsva(task._internal, name)\n    export get_uniform_hsva\n\n    set_uniform_transform!(task::RenderTask, name::String, transform::GLTransform) = detail.set_uniform_transform!(task._internal, name, transform._internal)\n    export set_uniform_transform!\n\n    get_uniform_transform(task::RenderTask, name::String) ::GLTransform = GLTransform(detail.get_uniform_transform(task._internal, name))\n    export get_uniform_transform\n\n    Base.show(io::IO, x::RenderTask) = show_aux(io, x)\n\n###### render_area.jl\n\n    @export_enum AntiAliasingQuality begin\n        ANTI_ALIASING_QUALITY_OFF\n        ANTI_ALIASING_QUALITY_MINIMAL\n        ANTI_ALIASING_QUALITY_GOOD\n        ANTI_ALIASING_QUALITY_BETTER\n        ANTI_ALIASING_QUALITY_BEST\n    end\n\n    @export_type RenderArea Widget\n    @declare_native_widget RenderArea\n\n    RenderArea(msaa_quality::AntiAliasingQuality = ANTI_ALIASING_QUALITY_OFF) = RenderArea(detail._RenderArea(msaa_quality))\n\n    add_render_task!(area::RenderArea, task::RenderTask) = detail.add_render_task!(area._internal, task._internal)\n    export add_render_task!\n\n    @export_function RenderArea clear_render_tasks! Cvoid\n\n    @export_function RenderArea make_current Cvoid\n    @export_function RenderArea queue_render Cvoid\n    @export_function RenderArea clear Cvoid\n    @export_function RenderArea render_render_tasks Cvoid\n    @export_function RenderArea flush Cvoid\n\n    function from_gl_coordinates(area::RenderArea, gl_coordinates::Vector2f) ::Vector2f\n        return detail.from_gl_coordinates(area._internal, gl_coordinates)\n    end\n    export from_gl_coordinates\n\n    function to_gl_coordinates(area::RenderArea, absolute_widget_space_coordinates::Vector2f) ::Vector2f\n        return detail.to_gl_coordinates(area._internal, absolute_widget_space_coordinates)\n    end\n    export to_gl_coordinates\n\n    @add_widget_signals RenderArea\n    @add_signal_resize RenderArea\n    @add_signal_render RenderArea\n\n    Base.show(io::IO, x::RenderArea) = show_aux(io, x)\n\nelse # if MOUSETRAP_ENABLE_OPENGL_COMPONENT\n\n    @define_opengl_error_type BlendMode\n    export BlendMode\n\n    @define_opengl_error_type GLTransform\n    export GLTransform\n\n    @define_opengl_error_type Shape\n    export Shape\n\n    @define_opengl_error_type Shader\n    export Shader\n\n    @define_opengl_error_type ShaderType\n    export ShaderType\n\n    @define_opengl_error_type TextureWrapMode\n    export TextureWrapMode\n\n    @define_opengl_error_type TextureScaleMode\n    export TextureScaleMode\n\n    @define_opengl_error_type Texture\n    export Texture\n\n    @define_opengl_error_type RenderTexture\n    export RenderTexture\n\n    @define_opengl_error_type RenderTask\n    export RenderTask\n\n    @define_opengl_error_type RenderArea\n    export RenderArea\n\nend # else MOUSETRAP_ENABLE_OPENGL_COMPONENT\n\n###### animation.jl\n\n    @export_enum AnimationState begin\n        ANIMATION_STATE_IDLE\n        ANIMATION_STATE_PAUSED\n        ANIMATION_STATE_PLAYING\n        ANIMATION_STATE_DONE\n    end\n\n    @export_enum AnimationTimingFunction begin\n        ANIMATION_TIMING_FUNCTION_LINEAR\n        ANIMATION_TIMING_FUNCTION_EXPONENTIAL_EASE_IN\n        ANIMATION_TIMING_FUNCTION_EXPONENTIAL_EASE_OUT\n        ANIMATION_TIMING_FUNCTION_EXPONENTIAL_SIGMOID\n        ANIMATION_TIMING_FUNCTION_SINE_EASE_IN\n        ANIMATION_TIMING_FUNCTION_SINE_EASE_OUT\n        ANIMATION_TIMING_FUNCTION_SINE_SIGMOID\n        ANIMATION_TIMING_FUNCTION_CIRCULAR_EASE_IN\n        ANIMATION_TIMING_FUNCTION_CIRCULAR_EASE_OUT\n        ANIMATION_TIMING_FUNCTION_CIRCULAR_SIGMOID\n        ANIMATION_TIMING_FUNCTION_OVERSHOOT_EASE_IN\n        ANIMATION_TIMING_FUNCTION_OVERSHOOT_EASE_OUT\n        ANIMATION_TIMING_FUNCTION_OVERSHOOT_SIGMOID\n        ANIMATION_TIMING_FUNCTION_ELASTIC_EASE_IN\n        ANIMATION_TIMING_FUNCTION_ELASTIC_EASE_OUT\n        ANIMATION_TIMING_FUNCTION_ELASTIC_SIGMOID\n        ANIMATION_TIMING_FUNCTION_BOUNCE_EASE_IN\n        ANIMATION_TIMING_FUNCTION_BOUNCE_EASE_OUT\n        ANIMATION_TIMING_FUNCTION_BOUNCE_SIGMOID\n    end\n\n    @export_type Animation SignalEmitter\n    function Animation(target::Widget, duration::Time)\n        return Animation(detail._Animation(as_widget_pointer(target), convert(Float32, as_microseconds(duration))))\n    end\n\n    @export_function Animation get_state AnimationState\n    @export_function Animation play! Cvoid\n    @export_function Animation pause! Cvoid\n    @export_function Animation reset! Cvoid\n\n    set_duration!(animation::Animation, duration::Time) = detail.set_duration(animation._internal, as_microseconds(duration))\n    export set_duration!\n\n    get_duration(animation::Animation) ::Time = microseconds(detail.get_duration(animation._internal))\n    export get_duration\n\n    @export_function Animation set_lower! Cvoid Number => Cdouble lower\n    @export_function Animation get_lower Cdouble\n    @export_function Animation set_upper! Cvoid Number => Cdouble upper\n    @export_function Animation get_upper Cdouble\n    @export_function Animation get_value Cdouble\n\n    @export_function Animation get_repeat_count Csize_t\n    @export_function Animation set_repeat_count! Cvoid Integer => Csize_t n\n\n    @export_function Animation get_is_reversed Bool\n    @export_function Animation set_is_reversed! Cvoid Bool is_reversed\n\n    @export_function Animation set_timing_function! Cvoid AnimationTimingFunction tweening_mode\n    @export_function Animation get_timing_function AnimationTimingFunction\n\n    function on_tick!(f, animation::Animation, data::Data_t) where Data_t\n        typed_f = TypedFunction(f, Cvoid, (Animation, AbstractFloat, Data_t))\n        detail.on_tick!(animation._internal, function(animation_ref, value::Cdouble )\n            typed_f(Animation(animation_ref[]), value, data)\n        end)\n    end\n    function on_tick!(f, animation::Animation)\n        typed_f = TypedFunction(f, Cvoid, (Animation, AbstractFloat))\n        detail.on_tick!(animation._internal, function(animation_ref, value::Cdouble)\n            typed_f(Animation(animation_ref[]), value)\n        end)\n    end\n    export on_tick!\n\n    function on_done!(f, animation::Animation, data::Data_t) where Data_t\n        typed_f = TypedFunction(f, Cvoid, (Animation, Data_t))\n        detail.on_done!(animation._internal, function(animation_ref)\n            typed_f(Animation(animation_ref[]), data)\n        end)\n    end\n    function on_done!(f, animation::Animation)\n        typed_f = TypedFunction(f, Cvoid, (Animation,))\n        detail.on_done!(animation._internal, function(animation_ref)\n            typed_f(Animation(animation_ref[]))\n        end)\n    end\n    export on_done!\n\n    Base.show(io::IO, x::Animation) = show_aux(io, x, :value, :lower, :upper, :state, :timing_function)\n\n###### transform_bin.jl\n\n    @export_type TransformBin Widget\n    @declare_native_widget TransformBin\n\n    TransformBin() = TransformBin(detail._TransformBin())\n    function TransformBin(child::Widget)\n        out = TransformBin()\n        set_child!(out, child)\n        return out\n    end\n\n    set_child!(transform_bin::TransformBin, child::Widget) = detail.set_child!(transform_bin._internal, as_widget_pointer(child))\n    export set_child!\n\n    @export_function TransformBin remove_child! Cvoid\n    @export_function TransformBin reset! Cvoid\n\n    rotate!(bin::TransformBin, angle::Angle) = detail.rotate!(bin._internal, convert(Float32, as_degrees(angle)))\n    export rotate!\n\n    translate!(bin::TransformBin, offset::Vector2f) = detail.translate!(bin._internal, convert(Float32, offset.x), convert(Float32, offset.y))\n    export translate!\n\n    @export_function TransformBin scale! Cvoid Number => Cfloat x Number => Cfloat y\n    scale!(bin::TransformBin, both::Number) = scale!(bin, both, both)\n\n    @export_function TransformBin skew! Cvoid Number => Cfloat x Number => Cfloat y\n\n    @add_widget_signals TransformBin\n    Base.show(io::IO, x::TransformBin) = show_aux(io, x)\n\n###### style.jl\n\n    add_css_class!(widget::Widget, class::String) = detail.add_css_class!(as_widget_pointer(widget), class)\n    export add_css_class!\n\n    remove_css_class!(widget::Widget, class::String) = detail.remove_css_class!(as_widget_pointer(widget), class)\n    export remove_css_class!\n\n    get_css_classes(widget::Widget) ::Vector{String} = detail.get_css_classes(as_widget_pointer(widget))\n    export get_css_classes\n\n    add_css!(code::String) = detail.style_manager_add_css!(code)\n    export add_css!\n\n    serialize(color::RGBA) ::String = detail.style_manager_color_to_css_rgba(color.r, color.g, color.b, color.a)\n    serialize(color::HSVA) ::String = detail.style_manager_color_to_css_hsva(color.h, color.s, color.v, color.a)\n    export serialize\n\n###### gl_canvas.jl\n\n    @export_type GLArea Widget\n    @declare_native_widget GLArea\n\n    GLArea() = GLArea(detail._GLArea())\n\n    @add_widget_signals GLArea\n    @add_signal_resize GLArea\n    @add_signal_render GLArea\n\n    @export_function GLArea make_current Cvoid\n    @export_function GLArea queue_render Cvoid\n    @export_function GLArea get_auto_render Bool\n    @export_function GLArea set_auto_render! Cvoid Bool b\n\n    Base.show(io::IO, x::GLArea) = show_aux(io, x)\n\n###### key_codes.jl\n\n    include(\"./key_codes.jl\")\n\n###### documentation\n\n    include(\"./docs.jl\")\n\n###### compound_widget.jl\n\n    macro define_compound_widget_signals()\n        out = Expr(:block)\n        for (signal, _) in Mousetrap.signal_descriptors\n            connect_signal_name = :connect_signal_ * signal * :!\n            push!(out.args, esc(:(\n                function Mousetrap.$connect_signal_name(f, x::Widget)\n                    $connect_signal_name(f, Mousetrap.get_top_level_widget(x))\n                end\n            )))\n\n            push!(out.args, esc(:(\n                function Mousetrap.$connect_signal_name(f, x::Widget, data::Data_t) where Data_t\n                    $connect_signal_name(f, Mousetrap.get_top_level_widget(x), data)\n                end\n            )))\n\n            emit_signal_name = :emit_signal_ * signal\n\n            push!(out.args, esc(:(\n                function $emit_signal_name(x::Widget, args...)\n                    $emit_signal_name(Mousetrap.get_top_level_widget(x), args)\n                end\n            )))\n\n            disconnect_signal_name = :disconnect_signal_ * signal * :!\n\n            push!(out.args, esc(:(\n                function $disconnect_signal_name(x::Widget)\n                    $disconnect_signal_name(Mousetrap.get_top_level_widget(x))\n                end\n            )))\n\n            set_signal_blocked_name = :set_signal_ * signal * :_blocked * :!\n\n            push!(out.args, esc(:(\n                function $set_signal_blocked_name(x::Widget, b)\n                    $set_signal_blocked_name(Mousetrap.get_top_level_widget(x), b)\n                end\n            )))\n\n            get_signal_blocked_name = :get_signal_ * signal * :_blocked\n\n            push!(out.args, esc(:(\n                function $get_signal_blocked_name(x::Widget)\n                    return $get_signal_blocked_name(Mousetrap.get_top_level_widget(x))\n                end\n            )))\n        end\n        return out\n    end\n    @define_compound_widget_signals()\nend"
  },
  {
    "path": "src/docgen/enums.jl",
    "content": "#\n# Author: C. Cords (mail@clemens-cords.com)\n# GitHub: https://github.com/clemapfel/mousetrap.jl\n# Documentation: https://clemens-cords.com/mousetrap\n#\n# Copyright © 2023, Licensed under lGPL-3.0\n#\n\n@document Alignment enum_docs(:Alignment,\n    \"Determines alignment of widgets along the horizontal or vertical axis.\", [\n    :ALIGNMENT_CENTER,\n    :ALIGNMENT_END,\n    :ALIGNMENT_START\n])\n@document ALIGNMENT_START \"Aligned left if horizontal, top if vertical\"\n@document ALIGNMENT_CENTER \"Aligned centered, regardless of orientation\"\n@document ALIGNMENT_END \"Aligned right if horizontal, bottom if vertical\"\n\n@document AnimationState enum_docs(:AnimationState, \n    \"Current state of the animation.\", [\n    :ANIMATION_STATE_IDLE,\n    :ANIMATION_STATE_PAUSED,\n    :ANIMATION_STATE_PLAYING,\n    :ANIMATION_STATE_DONE,\n])\n@document ANIMATION_STATE_IDLE \"Initial state of the animation\"\n@document ANIMATION_STATE_PAUSED \"Playing was started but `pause!` was called\"\n@document ANIMATION_STATE_PLAYING \"Playing was started using `play!`\"\n@document ANIMATION_STATE_DONE \"Animation went through all of its cycles and is now done\"\n\n@document AnimationTimingFunction enum_docs(:AnimationTimingFunction, \n    \"Shape of the mathematical function that will be used to generate the `Animation`s value over its duration.\", [\n    :ANIMATION_TIMING_FUNCTION_LINEAR,\n    :ANIMATION_TIMING_FUNCTION_EXPONENTIAL_EASE_IN,\n    :ANIMATION_TIMING_FUNCTION_EXPONENTIAL_EASE_OUT,\n    :ANIMATION_TIMING_FUNCTION_EXPONENTIAL_SIGMOID,\n    :ANIMATION_TIMING_FUNCTION_SINE_EASE_IN,\n    :ANIMATION_TIMING_FUNCTION_SINE_EASE_OUT,\n    :ANIMATION_TIMING_FUNCTION_SINE_SIGMOID,\n    :ANIMATION_TIMING_FUNCTION_CIRCULAR_EASE_IN,\n    :ANIMATION_TIMING_FUNCTION_CIRCULAR_EASE_OUT,\n    :ANIMATION_TIMING_FUNCTION_CIRCULAR_SIGMOID,\n    :ANIMATION_TIMING_FUNCTION_OVERSHOOT_EASE_IN,\n    :ANIMATION_TIMING_FUNCTION_OVERSHOOT_EASE_OUT,\n    :ANIMATION_TIMING_FUNCTION_OVERSHOOT_SIGMOID,\n    :ANIMATION_TIMING_FUNCTION_ELASTIC_EASE_IN,\n    :ANIMATION_TIMING_FUNCTION_ELASTIC_EASE_OUT,\n    :ANIMATION_TIMING_FUNCTION_ELASTIC_SIGMOID,\n    :ANIMATION_TIMING_FUNCTION_BOUNCE_EASE_IN,\n    :ANIMATION_TIMING_FUNCTION_BOUNCE_EASE_OUT,\n    :ANIMATION_TIMING_FUNCTION_BOUNCE_SIGMOID\n])\n@document ANIMATION_TIMING_FUNCTION_LINEAR \"strictly increasing, linear shape\"\n@document ANIMATION_TIMING_FUNCTION_EXPONENTIAL_EASE_IN \"strictly increasing, exponential ease in\"\n@document ANIMATION_TIMING_FUNCTION_EXPONENTIAL_EASE_OUT \"strictly decreasing, exponential ease out\"\n@document ANIMATION_TIMING_FUNCTION_EXPONENTIAL_SIGMOID \"strictly increasing, exponential east in and ease out\"\n@document ANIMATION_TIMING_FUNCTION_SINE_EASE_IN \"strictly increasing, sinusoid ease in\"\n@document ANIMATION_TIMING_FUNCTION_SINE_EASE_OUT \"strictly decreasing, sinusoid ease out\"\n@document ANIMATION_TIMING_FUNCTION_SINE_SIGMOID \"strictly increasing, sinusoid ease in and ease out\"\n@document ANIMATION_TIMING_FUNCTION_CIRCULAR_EASE_IN \"strictly increasing, circular ease in\"\n@document ANIMATION_TIMING_FUNCTION_CIRCULAR_EASE_OUT \"strictly decreasing, circular ease out\"\n@document ANIMATION_TIMING_FUNCTION_CIRCULAR_SIGMOID \"strictly increasing, circular east in and ease out\"\n@document ANIMATION_TIMING_FUNCTION_OVERSHOOT_EASE_IN \"undershoots `lower`, then increases, ease in\"\n@document ANIMATION_TIMING_FUNCTION_OVERSHOOT_EASE_OUT \"increases, then overshoots above `higher`, ease out\"\n@document ANIMATION_TIMING_FUNCTION_OVERSHOOT_SIGMOID \"undershoots `lower`, increases, then overshoots `higher`, ease in and ease out\"\n@document ANIMATION_TIMING_FUNCTION_ELASTIC_EASE_IN \"simulates stretching of a spring, ease in\"\n@document ANIMATION_TIMING_FUNCTION_ELASTIC_EASE_OUT \"simulates stretching of a spring, ease out\"\n@document ANIMATION_TIMING_FUNCTION_ELASTIC_SIGMOID \"simulates stretching of a spring, ease in and ease out\"\n@document ANIMATION_TIMING_FUNCTION_BOUNCE_EASE_IN \"simulates bouncing of a ball under gravity, ease in\"\n@document ANIMATION_TIMING_FUNCTION_BOUNCE_EASE_OUT \"simulates bouncing of a ball under gravity, ease out\"\n@document ANIMATION_TIMING_FUNCTION_BOUNCE_SIGMOID \"simulates bouncing of a ball under gravity, ease in and ease out\"\n\n@document AntiAliasingQuality enum_docs(:AntiAliasingQuality,\n    \"Number of samples when performing multi-sampled anti-aliasing (MSAA).\", [\n    :ANTI_ALIASING_QUALITY_OFF,\n    :ANTI_ALIASING_QUALITY_MINIMAL,\n    :ANTI_ALIASING_QUALITY_GOOD,\n    :ANTI_ALIASING_QUALITY_BETTER,\n    :ANTI_ALIASING_QUALITY_BEST\n])\n@document ANTI_ALIASING_QUALITY_OFF \"0 MSAA Samples, optimal speed\"\n@document ANTI_ALIASING_QUALITY_MINIMAL \"2 MSAA Samples\"\n@document ANTI_ALIASING_QUALITY_GOOD \"4 MSAA Samples\"\n@document ANTI_ALIASING_QUALITY_BETTER \"8 MSAA Samples\"\n@document ANTI_ALIASING_QUALITY_BEST \"16 MSAA Samples, optimal quality\"\n\n@document BlendMode enum_docs(:BlendMode,\n    \"Governs how colors are mixed when two fragments are rendered on top of each other.\", [\n    :BLEND_MODE_ADD,\n    :BLEND_MODE_MAX,\n    :BLEND_MODE_MIN,\n    :BLEND_MODE_MULTIPLY,\n    :BLEND_MODE_NONE,\n    :BLEND_MODE_REVERSE_SUBTRACT,\n    :BLEND_MODE_SUBTRACT\n])\n@document BLEND_MODE_NORMAL \"Traditional alpha blending, alpha component of both colors is treated as emission.\"\n@document BLEND_MODE_ADD \"result = source.rgb + destination.rgb\"\n@document BLEND_MODE_MAX \"result = max(source.rgb, destination.rgb)\"\n@document BLEND_MODE_MIN \"result = min(source.rgb, destination.rgb)\"\n@document BLEND_MODE_MULTIPLY \"result = source.rgb * destination.rgb\"\n@document BLEND_MODE_NONE \"result = destination.rgba\"\n@document BLEND_MODE_REVERSE_SUBTRACT \"result = source.rgb - destination.rgb\"\n@document BLEND_MODE_SUBTRACT \"result = destination.rgb - source.rgb\"\n\n@document ButtonID enum_docs(:ButtonID,\n    \"ID of a mouse button, manufacturer-specific.\", [\n    :BUTTON_ID_ANY,\n    :BUTTON_ID_BUTTON_01,\n    :BUTTON_ID_BUTTON_02,\n    :BUTTON_ID_BUTTON_03,\n    :BUTTON_ID_BUTTON_04,\n    :BUTTON_ID_BUTTON_05,\n    :BUTTON_ID_BUTTON_06,\n    :BUTTON_ID_BUTTON_07,\n    :BUTTON_ID_BUTTON_08,\n    :BUTTON_ID_BUTTON_09,\n    :BUTTON_ID_NONE\n])\n@document BUTTON_ID_ANY \"Any button, regardless of ID\"\n@document BUTTON_ID_BUTTON_01 \"Button #1, usually the left mouse button, or a touchscreen press\"\n@document BUTTON_ID_BUTTON_02 \"Button #2, usually the right mouse button\"\n@document BUTTON_ID_BUTTON_03 \"Button #3, manufacturer specific\"\n@document BUTTON_ID_BUTTON_04 \"Button #4, manufacturer specific\"\n@document BUTTON_ID_BUTTON_05 \"Button #5, manufacturer specific\"\n@document BUTTON_ID_BUTTON_06 \"Button #6, manufacturer specific\"\n@document BUTTON_ID_BUTTON_07 \"Button #7, manufacturer specific\"\n@document BUTTON_ID_BUTTON_08 \"Button #8, manufacturer specific\"\n@document BUTTON_ID_BUTTON_09 \"Button #9, manufacturer specific\"\n@document BUTTON_ID_NONE \"No button, regardless of ID\"\n\n@document CheckButtonState enum_docs(:CheckButtonState,\n    \"State of a [`CheckButton`](@ref)\", [\n    :CHECK_BUTTON_STATE_ACTIVE,\n    :CHECK_BUTTON_STATE_INACTIVE,\n    :CHECK_BUTTON_STATE_INCONSISTENT\n])\n@document CHECK_BUTTON_STATE_ACTIVE \"Active, usually displayed as a checkmark\"\n@document CHECK_BUTTON_STATE_INACTIVE \"Inactive, usually displayed as no check mark\"\n@document CHECK_BUTTON_STATE_INCONSISTENT \"Neither active nor inactive, usually displayed with a tilde `~`\"\n\n@document CornerPlacement enum_docs(:CornerPlacement,\n    \"Placement of both scrollbars relative to the center of a `Viewport`.\", [\n    :CORNER_PLACEMENT_BOTTOM_LEFT,\n    :CORNER_PLACEMENT_BOTTOM_RIGHT,\n    :CORNER_PLACEMENT_TOP_LEFT,\n    :CORNER_PLACEMENT_TOP_RIGHT\n])\n@document CORNER_PLACEMENT_BOTTOM_LEFT \"Horizontal scrollbar bottom, vertical scrollbar left\"\n@document CORNER_PLACEMENT_BOTTOM_RIGHT \"Horizontal scrollbar bottom, vertical scrollbar right\"\n@document CORNER_PLACEMENT_TOP_LEFT \"Horizontal scrollbar top, vertical scrollbar left\"\n@document CORNER_PLACEMENT_TOP_RIGHT \"Horizontal scrollbar top, vertical scrollbar right\"\n\n@document CursorType enum_docs(:CursorType,\n    \"Determines what the user cursor will look like while it is inside the allocated area of the widget.\", [\n    :CURSOR_TYPE_ALL_SCROLL,\n    :CURSOR_TYPE_CELL,\n    :CURSOR_TYPE_COLUMN_RESIZE,\n    :CURSOR_TYPE_CONTEXT_MENU,\n    :CURSOR_TYPE_CROSSHAIR,\n    :CURSOR_TYPE_DEFAULT,\n    :CURSOR_TYPE_EAST_RESIZE,\n    :CURSOR_TYPE_GRAB,\n    :CURSOR_TYPE_GRABBING,\n    :CURSOR_TYPE_HELP,\n    :CURSOR_TYPE_MOVE,\n    :CURSOR_TYPE_NONE,\n    :CURSOR_TYPE_NORTH_EAST_RESIZE,\n    :CURSOR_TYPE_NORTH_RESIZE,\n    :CURSOR_TYPE_NORTH_WEST_RESIZE,\n    :CURSOR_TYPE_NOT_ALLOWED,\n    :CURSOR_TYPE_POINTER,\n    :CURSOR_TYPE_PROGRESS,\n    :CURSOR_TYPE_ROW_RESIZE,\n    :CURSOR_TYPE_SOUTH_EAST_RESIZE,\n    :CURSOR_TYPE_SOUTH_RESIZE,\n    :CURSOR_TYPE_SOUTH_WEST_RESIZE,\n    :CURSOR_TYPE_TEXT,\n    :CURSOR_TYPE_WAIT,\n    :CURSOR_TYPE_WEST_RESIZE,\n    :CURSOR_TYPE_ZOOM_IN,\n    :CURSOR_TYPE_ZOOM_OUT\n])\n@document CURSOR_TYPE_ALL_SCROLL \"Omni-directional scrolling\"\n@document CURSOR_TYPE_CELL \"Cross, used for selecting cells from a table\"\n@document CURSOR_TYPE_COLUMN_RESIZE \"Left-right arrow\"\n@document CURSOR_TYPE_CONTEXT_MENU \"Questionmark, instructs the user that clicking will open a context menu\"\n@document CURSOR_TYPE_CROSSHAIR \"Crosshair, used for making pixel-perfect selections\"\n@document CURSOR_TYPE_DEFAULT \"Default arrow pointer\"\n@document CURSOR_TYPE_EAST_RESIZE \"Left arrow\"\n@document CURSOR_TYPE_GRAB \"Hand, not yet grabbing\"\n@document CURSOR_TYPE_GRABBING \"Hand, currently grabbing\"\n@document CURSOR_TYPE_HELP \"Questionmark, instructs the user that clicking or hovering above this element will open a help menu\"\n@document CURSOR_TYPE_MOVE \"4-directional arrow\"\n@document CURSOR_TYPE_NONE \"Invisible cursor\"\n@document CURSOR_TYPE_NORTH_EAST_RESIZE \"Up-left arrow\"\n@document CURSOR_TYPE_NORTH_RESIZE \"Up-arrow\"\n@document CURSOR_TYPE_NORTH_WEST_RESIZE \"Up-right arrow\"\n@document CURSOR_TYPE_NOT_ALLOWED \"Instructs the user that this action is currently disabled\"\n@document CURSOR_TYPE_POINTER \"Hand pointing\"\n@document CURSOR_TYPE_PROGRESS \"Spinning animation, signifies that the object is currently busy\"\n@document CURSOR_TYPE_ROW_RESIZE \"Up-down arrow\"\n@document CURSOR_TYPE_SOUTH_EAST_RESIZE \"Down-left arrow\"\n@document CURSOR_TYPE_SOUTH_RESIZE \"Down arrow\"\n@document CURSOR_TYPE_SOUTH_WEST_RESIZE \"Down-right arrow\"\n@document CURSOR_TYPE_TEXT \"Caret\"\n@document CURSOR_TYPE_WAIT \"Loading animation, Instructs the user that an action will become available soon\"\n@document CURSOR_TYPE_WEST_RESIZE \"Right arrow\"\n@document CURSOR_TYPE_ZOOM_IN \"Lens, usually with a plus icon\"\n@document CURSOR_TYPE_ZOOM_OUT \"Lens, usually with a minus icon\"\n\n@document DeviceAxis enum_docs(:DeviceAxis,\n    \"Axes of stylus- and touchpad device, captured by `StylusEventController`. Not all manufacturers support all or even any of these.\", [\n    :DEVICE_AXIS_DELTA_X,\n    :DEVICE_AXIS_DELTA_Y,\n    :DEVICE_AXIS_DISTANCE,\n    :DEVICE_AXIS_PRESSURE,\n    :DEVICE_AXIS_ROTATION,\n    :DEVICE_AXIS_SLIDER,\n    :DEVICE_AXIS_WHEEL,\n    :DEVICE_AXIS_X,\n    :DEVICE_AXIS_X_TILT,\n    :DEVICE_AXIS_Y,\n    :DEVICE_AXIS_Y_TILT\n])\n@document DEVICE_AXIS_DELTA_X \"Horizontal offset\"\n@document DEVICE_AXIS_DELTA_Y \"Vertical offset\"\n@document DEVICE_AXIS_DISTANCE \"Distance between the stylus' tip and the touchpad\"\n@document DEVICE_AXIS_PRESSURE \"Current pressure of the stylus\"\n@document DEVICE_AXIS_ROTATION \"Rotation of the stylus, usually in radians\"\n@document DEVICE_AXIS_SLIDER \"State of the stylus slider\"\n@document DEVICE_AXIS_WHEEL \"State of the stylus scroll wheel\"\n@document DEVICE_AXIS_X \"X-position of the stylus\"\n@document DEVICE_AXIS_X_TILT \"Tilt along the horizontal axis\"\n@document DEVICE_AXIS_Y \"Y-position of the stylus\"\n@document DEVICE_AXIS_Y_TILT \"Tilt along the vertical axis\"\n\n@document EllipsizeMode enum_docs(:EllipsizeMode,\n    \"Determines how ellipses are inserted when a `Label`s allocated area exceeds the space it is allowed to allocated.\", [\n    :ELLIPSIZE_MODE_END,\n    :ELLIPSIZE_MODE_MIDDLE,\n    :ELLIPSIZE_MODE_NONE,\n    :ELLIPSIZE_MODE_START\n])\n@document ELLIPSIZE_MODE_END \"Inserted at the end: `text...`\"\n@document ELLIPSIZE_MODE_MIDDLE \"Inserted in the middle: `te...xt`\"\n@document ELLIPSIZE_MODE_NONE \"No eclipsing will take place\"\n@document ELLIPSIZE_MODE_START \"Insert at the start: `...text`\"\n\n@document FileChooserAction enum_docs(:FileChooserAction,\n    \"Determines layout, which, and how many files/folders a user can select when using [`FileChooser`](@ref).\", [\n    :FILE_CHOOSER_ACTION_OPEN_FILE,\n    :FILE_CHOOSER_ACTION_OPEN_MULTIPLE_FILES,\n    :FILE_CHOOSER_ACTION_SAVE,\n    :FILE_CHOOSER_ACTION_SELECT_FOLDER,\n    :FILE_CHOOSER_ACTION_SELECT_MULTIPLE_FOLDERS\n])\n@document FILE_CHOOSER_ACTION_OPEN_FILE \"Select exactly one file\"\n@document FILE_CHOOSER_ACTION_OPEN_MULTIPLE_FILES \"Select one or more files\"\n@document FILE_CHOOSER_ACTION_SAVE \"Choose a name and location\"\n@document FILE_CHOOSER_ACTION_SELECT_FOLDER \"Select exactly one folder\"\n@document FILE_CHOOSER_ACTION_SELECT_MULTIPLE_FOLDERS \"Select one or more folders\"\n\n@document FileMonitorEvent enum_docs(:FileMonitorEvent,\n    \"Classifies user behavior that triggered the callback of [`FileMonitor`](@ref).\", [\n    :FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED,\n    :FILE_MONITOR_EVENT_CHANGED,\n    :FILE_MONITOR_EVENT_CHANGES_DONE_HINT,\n    :FILE_MONITOR_EVENT_CREATED,\n    :FILE_MONITOR_EVENT_DELETED,\n    :FILE_MONITOR_EVENT_MOVED_IN,\n    :FILE_MONITOR_EVENT_MOVED_OUT,\n    :FILE_MONITOR_EVENT_RENAMED\n])\n@document FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED \"Metadata about monitored file changed\"\n@document FILE_MONITOR_EVENT_CHANGED \"Content of monitored file changed\"\n@document FILE_MONITOR_EVENT_CHANGES_DONE_HINT \"Emitted to signal the end of a series of changes\"\n@document FILE_MONITOR_EVENT_CREATED \"A new file was created inside the monitored folder\"\n@document FILE_MONITOR_EVENT_DELETED \"File, Folder, or file inside the monitored folder was deleted\"\n@document FILE_MONITOR_EVENT_MOVED_IN \"File was moved into the monitored folder\"\n@document FILE_MONITOR_EVENT_MOVED_OUT \"File was moved out of the monitored folder\"\n@document FILE_MONITOR_EVENT_RENAMED \"File or folder was renamed\"\n\n@document InterpolationType enum_docs(:InterpolationType,\n    \"Determines interpolation algorithm used when scaling [`Image`](@ref).\", [\n    :INTERPOLATION_TYPE_BILINEAR,\n    :INTERPOLATION_TYPE_HYPERBOLIC,\n    :INTERPOLATION_TYPE_NEAREST,\n    :INTERPOLATION_TYPE_TILES\n])\n@document INTERPOLATION_TYPE_BILINEAR \"Linear interpolation, adequate speed and quality\"\n@document INTERPOLATION_TYPE_HYPERBOLIC \"Cubic interpolation, slow speed, high quality\"\n@document INTERPOLATION_TYPE_NEAREST \"Nearest neighbor interpolation, fastest but no filtering takes place\"\n@document INTERPOLATION_TYPE_TILES \"Linear when scaling down, nearest neighbor when scaling up.\"\n\n@document JustifyMode enum_docs(:JustifyMode,\n    \"Determines how words are arranged along the horizontal axis of a [`Label`](@ref) or [`TextView`](@ref).\", [\n    :JUSTIFY_MODE_CENTER,\n    :JUSTIFY_MODE_FILL,\n    :JUSTIFY_MODE_LEFT,\n    :JUSTIFY_MODE_RIGHT\n])\n@document JUSTIFY_MODE_CENTER \"Push towards the center\"\n@document JUSTIFY_MODE_FILL \"Expand such that the entire width is filled\"\n@document JUSTIFY_MODE_LEFT \"Push towards left\"\n@document JUSTIFY_MODE_RIGHT \"Push towards right\"\n\n@document LabelWrapMode enum_docs(:LabelWrapMode,\n    \"Determines at which point in a `Label`s contents a linebreak will be inserted.\", [\n    :LABEL_WRAP_MODE_NONE,\n    :LABEL_WRAP_MODE_ONLY_ON_CHAR,\n    :LABEL_WRAP_MODE_ONLY_ON_WORD,\n    :LABEL_WRAP_MODE_WORD_OR_CHAR\n])\n@document LABEL_WRAP_MODE_NONE \"Never wrap, will always be exactly one line\"\n@document LABEL_WRAP_MODE_ONLY_ON_CHAR \"Insert linebreaks after a character\"\n@document LABEL_WRAP_MODE_ONLY_ON_WORD \"Insert linebreaks before a space between two words\"\n@document LABEL_WRAP_MODE_WORD_OR_CHAR \"Insert linebreak after a character or before the space between two words\"\n\n@document LevelBarMode enum_docs(:LevelBarMode,\n    \"Determines how a [`LevelBar`](@ref) displays its fraction.\", [\n    :LEVEL_BAR_MODE_CONTINUOUS,\n    :LEVEL_BAR_MODE_DISCRETE\n])\n@document LEVEL_BAR_MODE_CONTINUOUS \"Continuous bar, displays floating point value\"\n@document LEVEL_BAR_MODE_DISCRETE \"Segmented bar, displays integer value\"\n\n@document Orientation enum_docs(:Orientation,\n    \"Determines orientation of a widget.\", [\n    :ORIENTATION_HORIZONTAL,\n    :ORIENTATION_VERTICAL\n])\n@document ORIENTATION_HORIZONTAL \"Align left-to-right along the x-axis\"\n@document ORIENTATION_VERTICAL \"Align top-to-bottom along the y-axis\"\n\n@document PanDirection enum_docs(:PanDirection,\n    \"Direction of a pan gesture recognized by [`PanEventController`](@ref).\", [\n    :PAN_DIRECTION_DOWN,\n    :PAN_DIRECTION_LEFT,\n    :PAN_DIRECTION_RIGHT,\n    :PAN_DIRECTION_UP\n])\n@document PAN_DIRECTION_DOWN \"Pan up-down\"\n@document PAN_DIRECTION_LEFT \"Pan left-right\"\n@document PAN_DIRECTION_RIGHT \"Pan right-left\"\n@document PAN_DIRECTION_UP \"Pen down-up\"\n\n@document PropagationPhase enum_docs(:PropagationPhase,\n    \"Determines at which part during the main loop event propagation an event controller will consume the event, cf. https://developer-old.gnome.org/gtk4/stable/event-propagation.html\", [\n    :PROPAGATION_PHASE_BUBBLE,\n    :PROPAGATION_PHASE_CAPTURE,\n    :PROPAGATION_PHASE_NONE,\n    :PROPAGATION_PHASE_TARGET\n])\n@document PROPAGATION_PHASE_BUBBLE \"Consume event during propagation \\\"upwards\\\", from child to parent\"\n@document PROPAGATION_PHASE_CAPTURE \"Consume event during propagation \\\"downwards\\\", from parent to child\"\n@document PROPAGATION_PHASE_NONE \"Do not capture events\"\n@document PROPAGATION_PHASE_TARGET \"Consume events when the widget targets its event controllers with events\"\n\n@document RelativePosition enum_docs(:RelativePosition,\n    \"Relative position of one object to another.\", [\n    :RELATIVE_POSITION_ABOVE,\n    :RELATIVE_POSITION_BELOW,\n    :RELATIVE_POSITION_LEFT_OF,\n    :RELATIVE_POSITION_RIGHT_OF\n])\n@document RELATIVE_POSITION_ABOVE \"Object is above another\"\n@document RELATIVE_POSITION_BELOW \"Object is below another\"\n@document RELATIVE_POSITION_LEFT_OF \"Object is left of another\"\n@document RELATIVE_POSITION_RIGHT_OF \"Object is right of another\"\n\n@document RevealerTransitionType enum_docs(:RevealerTransitionType,\n    \"Determines animation type when of [`Revealer`] showing or hiding its child.\", [\n    :REVEALER_TRANSITION_TYPE_CROSSFADE,\n    :REVEALER_TRANSITION_TYPE_NONE,\n    :REVEALER_TRANSITION_TYPE_SLIDE_DOWN,\n    :REVEALER_TRANSITION_TYPE_SLIDE_LEFT,\n    :REVEALER_TRANSITION_TYPE_SLIDE_RIGHT,\n    :REVEALER_TRANSITION_TYPE_SLIDE_UP,\n    :REVEALER_TRANSITION_TYPE_SWING_DOWN,\n    :REVEALER_TRANSITION_TYPE_SWING_LEFT,\n    :REVEALER_TRANSITION_TYPE_SWING_RIGHT,\n    :REVEALER_TRANSITION_TYPE_SWING_UP\n])\n@document REVEALER_TRANSITION_TYPE_CROSSFADE \"Crossfade, slowly increasing / decreasing opacity\"\n@document REVEALER_TRANSITION_TYPE_NONE \"Instantly reveal the widget\"\n@document REVEALER_TRANSITION_TYPE_SLIDE_DOWN \"Slide from top to bottom\"\n@document REVEALER_TRANSITION_TYPE_SLIDE_LEFT \"Slide from right to left\"\n@document REVEALER_TRANSITION_TYPE_SLIDE_RIGHT \"Slide from left to right\"\n@document REVEALER_TRANSITION_TYPE_SLIDE_UP \"Slide from bottom to top\"\n@document REVEALER_TRANSITION_TYPE_SWING_DOWN \"Swing from top to bottom\"\n@document REVEALER_TRANSITION_TYPE_SWING_LEFT \"Swing from right to left\"\n@document REVEALER_TRANSITION_TYPE_SWING_RIGHT \"Swing from left to right\"\n@document REVEALER_TRANSITION_TYPE_SWING_UP \"Swing from bottom to top\"\n\n@document ScrollType enum_docs(:ScrollType,\n    \"Classification of keyboard event that triggered the `scroll_child` event of a [`Viewport`](@ref).\", [\n    :SCROLL_TYPE_JUMP,\n    :SCROLL_TYPE_NONE,\n    :SCROLL_TYPE_PAGE_BACKWARD,\n    :SCROLL_TYPE_PAGE_DOWN,\n    :SCROLL_TYPE_PAGE_FORWARD,\n    :SCROLL_TYPE_PAGE_LEFT,\n    :SCROLL_TYPE_PAGE_RIGHT,\n    :SCROLL_TYPE_PAGE_UP,\n    :SCROLL_TYPE_SCROLL_END,\n    :SCROLL_TYPE_SCROLL_START,\n    :SCROLL_TYPE_STEP_BACKWARD,\n    :SCROLL_TYPE_STEP_DOWN,\n    :SCROLL_TYPE_STEP_FORWARD,\n    :SCROLL_TYPE_STEP_LEFT,\n    :SCROLL_TYPE_STEP_RIGHT,\n    :SCROLL_TYPE_STEP_UP\n])\n@document SCROLL_TYPE_JUMP \"Jump keybinding, if present\"\n@document SCROLL_TYPE_NONE \"No keybinding was used\"\n@document SCROLL_TYPE_PAGE_BACKWARD \"Move one page backward\"\n@document SCROLL_TYPE_PAGE_DOWN \"Move one page vertically down\"\n@document SCROLL_TYPE_PAGE_FORWARD \"Move one page forward\"\n@document SCROLL_TYPE_PAGE_LEFT \"Move one page vertically left\"\n@document SCROLL_TYPE_PAGE_RIGHT \"Move one page horizontally right\"\n@document SCROLL_TYPE_PAGE_UP \"Move one page vertically up\"\n@document SCROLL_TYPE_SCROLL_END \"Jump to the end\"\n@document SCROLL_TYPE_SCROLL_START \"Jump to the start\"\n@document SCROLL_TYPE_STEP_BACKWARD \"Move one scroll step backward\"\n@document SCROLL_TYPE_STEP_DOWN \"Move one scroll step vertically down\"\n@document SCROLL_TYPE_STEP_FORWARD \"Move on scroll step forward\"\n@document SCROLL_TYPE_STEP_LEFT \"Move one scroll step horizontally left\"\n@document SCROLL_TYPE_STEP_RIGHT \"Move one scroll step horizontally right\"\n@document SCROLL_TYPE_STEP_UP \"Move on scroll step vertically up\"\n\n@document ScrollbarVisibilityPolicy enum_docs(:ScrollbarVisibilityPolicy,\n    \"Determines when / if a scrollbar of a [`Viewport`](@ref) reveals itself.\", [\n    :SCROLLBAR_VISIBILITY_POLICY_ALWAYS,\n    :SCROLLBAR_VISIBILITY_POLICY_AUTOMATIC,\n    :SCROLLBAR_VISIBILITY_POLICY_NEVER\n])\n@document SCROLLBAR_VISIBILITY_POLICY_ALWAYS \"Stay revealed at all times\"\n@document SCROLLBAR_VISIBILITY_POLICY_AUTOMATIC \"Reveal when the user's cursor enters the [`Viewport`](@ref), hide when it exits\"\n@document SCROLLBAR_VISIBILITY_POLICY_NEVER \"Stay hidden at all times\"\n\n@document SectionFormat enum_docs(:SectionFormat,\n    \"Visual layout of a [`MenuModel`](@ref) \\\"section\\\"-type item.\", [\n    :SECTION_FORMAT_CIRCULAR_BUTTONS,\n    :SECTION_FORMAT_HORIZONTAL_BUTTONS,\n    :SECTION_FORMAT_HORIZONTAL_BUTTONS_LEFT_TO_RIGHT,\n    :SECTION_FORMAT_HORIZONTAL_BUTTONS_RIGHT_TO_LEFT,\n    :SECTION_FORMAT_INLINE_BUTTONS,\n    :SECTION_FORMAT_NORMAL\n])\n@document SECTION_FORMAT_CIRCULAR_BUTTONS \"Circular buttons\"\n@document SECTION_FORMAT_HORIZONTAL_BUTTONS \"Rectangular buttons\"\n@document SECTION_FORMAT_HORIZONTAL_BUTTONS_LEFT_TO_RIGHT \"Rectangular buttons, pushed to the left\"\n@document SECTION_FORMAT_HORIZONTAL_BUTTONS_RIGHT_TO_LEFT \"Rectangular buttons, pushed to the right\"\n@document SECTION_FORMAT_INLINE_BUTTONS \"Buttons are appended right of the section title\"\n@document SECTION_FORMAT_NORMAL \"Default layout\"\n\n@document SelectionMode enum_docs(:SelectionMode,\n    \"Governs if and how many elements can be selected.\", [\n    :SELECTION_MODE_MULTIPLE,\n    :SELECTION_MODE_NONE,\n    :SELECTION_MODE_SINGLE\n])\n@document SELECTION_MODE_MULTIPLE \"Zero or more widgets can be selected\"\n@document SELECTION_MODE_NONE \"Exactly zero widgets can be selected\"\n@document SELECTION_MODE_SINGLE \"Exactly one widget can be selected\"\n\n@document ShaderType enum_docs(:ShaderType,\n    \"Type of OpenGL shaderprogram component.\", [\n    :SHADER_TYPE_FRAGMENT,\n    :SHADER_TYPE_VERTEX\n])\n@document SHADER_TYPE_FRAGMENT \"Fragment shader\"\n@document SHADER_TYPE_VERTEX \"Vertex shader\"\n\n@document ShortcutScope enum_docs(:ShortcutScope,\n    \"Determines at which scope a shortcut will be captured.\", [\n    :SHORTCUT_SCOPE_GLOBAL,\n    :SHORTCUT_SCOPE_LOCAL\n    #:SHORTCUT_SCOPE_MANAGED\n])\n@document SHORTCUT_SCOPE_GLOBAL \"If the most top-level parent of the widget holds focus, the shortcut is captured\"\n@document SHORTCUT_SCOPE_LOCAL \"If the widget the event controller was added to holds focus, the shortcut is captured\"\n# @document SHORTCUT_SCOPE_MANAGED \"\"\n\n@document StackTransitionType enum_docs(:StackTransitionType,\n    \"Determines animation that plays when a [`Stack`](@ref) switches from one of its pages to another.\", [\n    :STACK_TRANSITION_TYPE_CROSSFADE,\n    :STACK_TRANSITION_TYPE_NONE,\n    :STACK_TRANSITION_TYPE_OVER_DOWN,\n    :STACK_TRANSITION_TYPE_OVER_LEFT,\n    :STACK_TRANSITION_TYPE_OVER_LEFT_RIGHT,\n    :STACK_TRANSITION_TYPE_OVER_RIGHT,\n    :STACK_TRANSITION_TYPE_OVER_UP,\n    :STACK_TRANSITION_TYPE_OVER_UP_DOWN,\n    :STACK_TRANSITION_TYPE_ROTATE_LEFT,\n    :STACK_TRANSITION_TYPE_ROTATE_LEFT_RIGHT,\n    :STACK_TRANSITION_TYPE_ROTATE_RIGHT,\n    :STACK_TRANSITION_TYPE_SLIDE_DOWN,\n    :STACK_TRANSITION_TYPE_SLIDE_LEFT,\n    :STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT,\n    :STACK_TRANSITION_TYPE_SLIDE_RIGHT,\n    :STACK_TRANSITION_TYPE_SLIDE_UP,\n    :STACK_TRANSITION_TYPE_SLIDE_UP_DOWN,\n    :STACK_TRANSITION_TYPE_UNDER_DOWN,\n    :STACK_TRANSITION_TYPE_UNDER_LEFT,\n    :STACK_TRANSITION_TYPE_UNDER_RIGHT,\n    :STACK_TRANSITION_TYPE_UNDER_UP\n])\n@document STACK_TRANSITION_TYPE_CROSSFADE \"Crossfade, slowly increasing opacity\"\n@document STACK_TRANSITION_TYPE_NONE \"Instantly transition\"\n@document STACK_TRANSITION_TYPE_OVER_DOWN \"Slide next page over current, from top to bottom\"\n@document STACK_TRANSITION_TYPE_OVER_LEFT \"Slide next page over current, from right to left\"\n@document STACK_TRANSITION_TYPE_OVER_LEFT_RIGHT \"Slide next page over current, exiting left and entering right\"\n@document STACK_TRANSITION_TYPE_OVER_RIGHT \"Slide next page over current, from left to right\"\n@document STACK_TRANSITION_TYPE_OVER_UP \"Slide next page over current, from bottom to top\"\n@document STACK_TRANSITION_TYPE_OVER_UP_DOWN \"Slide next page over current, exiting up and entering bottom\"\n@document STACK_TRANSITION_TYPE_ROTATE_LEFT \"Rotate the previous page from right to left, then enter the next page from right to left\"\n@document STACK_TRANSITION_TYPE_ROTATE_LEFT_RIGHT \"Rotate the previous page to the right to left, then enter next next page from left to right\"\n@document STACK_TRANSITION_TYPE_ROTATE_RIGHT \"Rotate the previous page from left to right, then enter the next page from left to right\"\n@document STACK_TRANSITION_TYPE_SLIDE_DOWN \"Slide the current page top to bottom, enter the next page from top to bottom\"\n@document STACK_TRANSITION_TYPE_SLIDE_LEFT \"Slide the current page right to left, enter the next page from right to left\"\n@document STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT \"Slide the current page left, enter the next page from left to right\"\n@document STACK_TRANSITION_TYPE_SLIDE_RIGHT \"Slide the current page left to right, enter the next page left to right\"\n@document STACK_TRANSITION_TYPE_SLIDE_UP \"Slide the current page bottom to top, enter the next page bottom to top\"\n@document STACK_TRANSITION_TYPE_SLIDE_UP_DOWN \"Slide the current page bottom top, enter next page top to bottom\"\n@document STACK_TRANSITION_TYPE_UNDER_DOWN \"Slide next page under current, from top to bottom\"\n@document STACK_TRANSITION_TYPE_UNDER_LEFT \"Slide next page under current, from left to right\"\n@document STACK_TRANSITION_TYPE_UNDER_RIGHT \"Slide next page under current, from right to left\"\n@document STACK_TRANSITION_TYPE_UNDER_UP \"Slide next page under current, from bottom to top\"\n\n@document TextureScaleMode enum_docs(:TextureScaleMode,\n    \"Determines how [`Texture`](@ref) filters when scaled.\", [\n    :TEXTURE_SCALE_MODE_LINEAR,\n    :TEXTURE_SCALE_MODE_NEAREST\n])\n@document TEXTURE_SCALE_MODE_LINEAR \"Linear interpolation\"\n@document TEXTURE_SCALE_MODE_NEAREST \"Nearest-neighbor interpolation\"\n\n@document TextureWrapMode enum_docs(:TextureWrapMode,\n    \"Determines color of fragments with a texture coordinate outside of `[0, 1]`.\", [\n    :TEXTURE_WRAP_MODE_MIRROR,\n    :TEXTURE_WRAP_MODE_ONE,\n    :TEXTURE_WRAP_MODE_REPEAT,\n    :TEXTURE_WRAP_MODE_STRETCH,\n    :TEXTURE_WRAP_MODE_ZERO\n])\n@document TEXTURE_WRAP_MODE_MIRROR \"Mirror along the closest edge\"\n@document TEXTURE_WRAP_MODE_ONE \"RGBA(1, 1, 1, 1)\"\n@document TEXTURE_WRAP_MODE_REPEAT \"Repeat along the closest edge\"\n@document TEXTURE_WRAP_MODE_STRETCH \"Stretch the outermost fragment of the closest edge\"\n@document TEXTURE_WRAP_MODE_ZERO \"RGBA(0, 0, 0, 0)\"\n\n@document Theme enum_docs(:Theme,\n    \"Determines the look of all widgets when made active using `Application`s `set_current_theme!`.\", [\n    :THEME_DEFAULT_LIGHT,\n    :THEME_DEFAULT_DARK,\n    :THEME_HIGH_CONTRAST_LIGHT,\n    :THEME_HIGH_CONTRAST_DARK\n])\n\n@document THEME_DEFAULT_LIGHT \"Default light theme, this theme is available for all operating systems.\"\n@document THEME_DEFAULT_DARK \"Default dark theme, this theme is available for all operating systems.\"\n@document THEME_HIGH_CONTRAST_LIGHT \"Default high contrast theme, light variant. Not all operating systems support this.\"\n@document THEME_HIGH_CONTRAST_DARK \"Default high contrast theme, dark variant. Not all operating systems support this.\"\n\n@document TickCallbackResult enum_docs(:TickCallbackResult,\n    \"Return value of a callback registered via [`set_tick_callback!`](@ref). Determines whether the callback should be removed.\", [\n    :TICK_CALLBACK_RESULT_CONTINUE,\n    :TICK_CALLBACK_RESULT_DISCONTINUE\n])\n@document TICK_CALLBACK_RESULT_CONTINUE \"Continue the callback, it will be invoked the next frame\"\n@document TICK_CALLBACK_RESULT_DISCONTINUE \"Remove the callback, it will no longer be invoked\"\n\n@document ToolType enum_docs(:ToolType,\n    \"Tool type classification of a stylus, not all manufactures support all or even any of these.\", [\n    :TOOL_TYPE_AIRBRUSH,\n    :TOOL_TYPE_BRUSH,\n    :TOOL_TYPE_ERASER,\n    :TOOL_TYPE_LENS,\n    :TOOL_TYPE_MOUSE,\n    :TOOL_TYPE_PEN,\n    :TOOL_TYPE_PENCIL,\n    :TOOL_TYPE_UNKNOWN\n])\n@document TOOL_TYPE_AIRBRUSH \"Airbrush tool\"\n@document TOOL_TYPE_BRUSH \"Variable-width brush\"\n@document TOOL_TYPE_ERASER \"Erase tool\"\n@document TOOL_TYPE_LENS \"Zoom tool\"\n@document TOOL_TYPE_MOUSE \"Cursor tol\"\n@document TOOL_TYPE_PEN \"Basic pen tool\"\n@document TOOL_TYPE_PENCIL \"Fixed-width brush\"\n@document TOOL_TYPE_UNKNOWN \"None of the other values of `ToolType`\"\n\n@document WindowCloseRequestResult enum_docs(:WindowCloseRequestResult,\n    \"Return value of signal `close_request` of [`Window`](@ref). Determines whether the window should close when requested to.\", [\n    :WINDOW_CLOSE_REQUEST_RESULT_ALLOW_CLOSE,\n    :WINDOW_CLOSE_REQUEST_RESULT_PREVENT_CLOSE\n])\n@document WINDOW_CLOSE_REQUEST_RESULT_ALLOW_CLOSE \"Allow invocation of the default handler, the window will close.\"\n@document WINDOW_CLOSE_REQUEST_RESULT_PREVENT_CLOSE \"Prevent the window from closing.\"\n"
  },
  {
    "path": "src/docgen/functions.jl",
    "content": "#\n# Author: C. Cords (mail@clemens-cords.com)\n# GitHub: https://github.com/clemapfel/mousetrap.jl\n# Documentation: https://clemens-cords.com/mousetrap\n#\n# Copyright © 2023, Licensed under lGPL-3.0\n#\n\n@document Circle \"\"\"\n```\nCircle(center::Vector2f, radius::Number, n_outer_vertices::Integer) -> Shape\n```\nCreate a shape as a circle, defined by its center and radius. In OpenGL coordinates.\n\"\"\"\n\n@document CircularRing \"\"\"\n```\nCircularRing(center::Vector2f, outer_radius::Number, thickness::Number, n_outer_vertices::Integer) -> Shape \n```\nCreate a shape as a circular ring, defined by its center, `outer_radius`, which is the distance to the out perimeter, and `thickness` which is the distance between the inner and outer perimeter.\n\"\"\"\n\n@document Ellipse \"\"\"\n```\nEllipse(center::Vector2f, x_radius::Number, y_radius::Number, n_outer_vertices::Integer) -> Shape\n```\nCreate a shape as an ellipse, defined by its center and radii along the x- and y-dimension, in OpenGL coordinates.\n\"\"\"\n\n@document EllipticalRing \"\"\"\n```\nEllipticalRing(center::Vector2f, outer_x_radius::Number, outer_y_radius::Number, x_thickness::Number, y_thickness::Number, n_outer_vertices::Unsigned) -> Shape\n```\nCreate a shape as an elliptical ring, where\n+ `center`: center of the ring\n+ `outer_x_radius`: distance between the center and the outer perimeter along the x-axis\n+ `outer_y_radius`: distance between the center and the outer perimeter along the y-axis\n+ `x_thickness`: distance between the outer and inner perimeter along the x-axis\n+ `y_thickness``: distance between the outer and inner perimeter along the y-axis\n\nIn OpenGL coordinates.\n\"\"\"\n\n@document Line \"\"\"\n```\nLine(a::Vector2f, b::Vector2f) -> Shape\n```\nCreate a shape as a 1-fragment thick line between two points, in OpenGL coordinates.\n\"\"\"\n\n@document Lines \"\"\"\n```\nLines(::Vector{Pair{Vector2f, Vector2f}) -> Shape\n```\nCreate a shape as a set of unconnected lines, vertex positions in OpenGL coordinates.\n\"\"\"\n\n@document LineStrip \"\"\"\n```\nLineStrip(points::Vector{Vector2f}) -> Shape\n```\nCreate a shape as a line strip. For points `{a1, a2, ..., an}`, this will be a set of connected lines `{a1, a2}, {a2, a3}, ..., {an-1, an}`, in OpenGL coordinates.\n\"\"\"\n\n@document Outline \"\"\"\n```\nOutline(other::Shape) -> Shape\n```\nCreate a shape as an outline of another shape.\n\"\"\"\n\n@document Point \"\"\"\n```\nPoint(position::Vector2f) -> Shape\n```\nCreate a shape as 1-fragment point, in OpenGL coordinates.\n\"\"\"\n\n@document Points \"\"\"\n```\nPoint(positions::Vector{Vector2f}) -> Shape\n```\nCreate a shape as a set of unconnected points, in OpenGL coordinates.\n\"\"\"\n\n@document Polygon \"\"\"\n```\nPolygon(points::Vector{Vector2f}) -> Shape\n```\nCreate a shape as a convex polygon, the outer hull of `points` will be computed, but no vertex elimination will take place. In OpenGL coordiantes.\n\"\"\"\n\n@document Rectangle \"\"\"\n```\nRectangle(top_left::Vector2f, size::Vector2f) -> Shape\n```\nCreate a shape as an axis-aligned rectangle, in OpenGL coordinates.\n\"\"\"\n\n@document RectangularFrame \"\"\"\n```\nRectangularFrame(top_left::Vector2f, outer_size::Vector2f, x_width::Number, y_width::Number) -> Shape\n```\nCreate a shape as a rectangular frame, where `x_width`, `y_width` are the \"thickness\" of the frames filled area. In OpenGL coordinates.\n\"\"\"\n\n@document Triangle \"\"\"\n```\nTriangle(a::Vector2f, b::Vector2f, c::Vector2f) -> Shape\n```\nCreate a shape as a triangle defined by three points, in OpenGL coordinates.\n\"\"\"\n\n@document Wireframe \"\"\"\n```\nWireframe(points::Vector{Vector2f}) -> Shape\n```\nCreate a shape as a wireframe. For points `{a1, a2, a3, ..., an}`, the shape\nwill be a connected series of lines `{a1, a2}, {a2, a3}, ..., {an-1, an}, {an, a1}`. In OpenGL coordinates.\n\"\"\"\n\n@document activate! \"\"\"\n```\nactivate!(::Widget)\n```\nIf the widget is activatable, trigger it. Depending on the widget, this may not necessarily emit signal `activate`. Use [`emit_signal_activate`](@ref) to manually emit the signal instead.\n\n---\n\n```\nactivate!(::Action) \n```\nTrigger the action's callback. This will also emit signal `activated`.\n\n\"\"\"\n\n@document add_action! \"\"\"\n```\nadd_action!(app::Application, action::Action)\n```\nRegister an action with the application. This is usually done automatically.\n\n---\n\n```\nadd_action!(model::MenuModel, label::String, action::Action) \n```\nAdd an \"action\"-type item to the menu model\n\n---\n\n```\nadd_action!(shortcut_controller::ShortcutEventController, action::Action) \n```\nRegister an action with the shortcut controller. Once connected to a widget, the\ncontroller will listen for keyboard events associated with any registered action,\ntriggering that action when the shortcut is recognized.\n\"\"\"\n\n@document add_allow_all_supported_image_formats! \"\"\"\n```\nadd_allow_all_supported_image_formats!(::FileFilter) \n```\nLet all file formats pass through the filter that can be loaded `Image`, `ImageDisplay`, or `Icon`.\n\"\"\"\n\n@document add_allowed_mime_type! \"\"\"\n```\nadd_allowed_mime_type!(::FileFilter, mime_type_id::String) \n```\nLet all files pass through the filter whose MIME type is equal to the given string.\n\"\"\"\n\n@document add_allowed_pattern! \"\"\"\n```\nadd_allowed_pattern!(::FileFilter, pattern::String) \n```\nLet all files pass through the filter whose name match the given shell-style glob.\n\n## Example\n```julia\nfilter = FilterFilter(\"pass_julia_files\")\nadd_allowed_pattern!(filter, \"*.jl\")\n```\n\"\"\"\n\n@document add_allowed_suffix! \"\"\"\n```\nadd_allowed_suffix!(::FileFilter, suffix::String) \n```\nLet all files pass through the filter whose file extension is equal to the given string, where `suffix` should **not** contain a dot.\n\n## Example\n```julia\nfilter = FilterFilter(\"pass_julia_files\")\nadd_allowed_suffix!(filter, \"jl\") # \"jl\", not \".jl\"\n```\n````\n\"\"\"\n\n@document add_button! \"\"\"\n```\nadd_button!(::AlertDialog, label::String) -> Integer\n```\nAdd a new button to the dialog. If the button is clicked, the dialog closes automatically. The return value of this function is the button's ID, which should be stored to later reference the button.\n\"\"\"\n\n@document add_child! \"\"\"\n```\nadd_child!(stack::Stack, ::Widget, title::String) \n```\nAdd a new stack page that will be uniquely identified by `title`.\n\n---\n\n```\nadd_child!(fixed::Fixed, ::Widget, position::Vector2f) \n```\nAdd a widget at given position, in absolute widget-space coordinates.\n\"\"\"\n\n@document add_css! \"\"\"\n```\nadd_css!(code::String) -> Cvoid\n```\nExecute CSS code and add it to the global style manager. If compiled successfully, \nany class defined will be available to be applied to a widget using `add_css_class!`.\n\n## Example\n```julia\nmousetrap.add_css!(\\\"\\\"\\\"\n.custom {\n    color: green;\n    border-radius: 10%;\n}\n\\\"\\\"\\\")\nadd_css_class!(window, \".custom\")\n```\n\"\"\"\n\n@document add_css_class! \"\"\"\n```\nadd_css_class!(::Widget, class::String)\n```\nApply a custom style class to the widget. Use `add_css!` to define a CSS\nclass.\n\n## Example\n```julia\nmousetrap.add_css!(\\\"\\\"\\\"\n.custom {\n    color: green;\n    border-radius: 10%;\n}\n\\\"\\\"\\\")\nadd_css_class!(window, \".custom\")\n```\n\"\"\"\n\n@document add_controller! \"\"\"\n```\nadd_controller!(::Widget, controller::EventController) \n```\nAdd an event controller to the widget. Once that widget is realized, the controller will start listening for events.\n\"\"\"\n\n@document add_filter! \"\"\"\n```\nadd_filter!(::FileChooser, ::FileFilter)\n```\nAdd a filter to the selection of available file filters. Use [`set_initial_filter!`](@ref) to make it the currently selected filter.\n\"\"\"\n\n@document add_icon! \"\"\"\n```\nadd_icon!(model::MenuModel, icon::Icon, action::Action) \n```\nAdd an \"icon\"-type item to the menu model. \n\"\"\"\n\n@document add_mark! \"\"\"\n```\nadd_mark!(::Scale, value::Number, position::RelativePosition, [label::String])\n```\nAdd a mark with an option label. `position` determines where the mark is shown relative to the scales center.\n\"\"\"\n\n@document add_marker! \"\"\"\n```\nadd_marker!(::LevelBar, name::String, value::AbstractFloat) \n```\nAdd a marker with label to the level bar.\n\"\"\"\n\n@document add_overlay! \"\"\"\n```\nadd_overlay!(overlay::Overlay, child::Widget ; [include_in_measurement::Bool = true, clip::Bool = false]) \n```\nAdd an additional overlay widget. It will be display \"on top\" of previously added widgets. \n\nIf `include_in_measurement` is `true`, the overlaid widget will be included in size-allocation of \nthe entire `Overlay`.\n\nIf `clip` is `true`, if part of a widget goes outside the overlays allocated area, it will be truncated.\n\"\"\"\n\n@document add_render_task! \"\"\"\n```\nadd_render_task!(area::RenderArea, task::RenderTask) \n```\nRegister a new render task with the area. Unless a custom handle was connected to the `RenderArea` using `connect_signal_render!`, \nthe render task will be drawn every frame.\n\"\"\"\n\n@document add_resource_path! \"\"\"\n```\nadd_resource_path!(::IconTheme, path::String) \n```\nAdd a folder that the `IconTheme` should lookup icons from. This is in addition to the default search path for icons. \n\nThe folder has to adhere to the [Freedesktop icon theme specificatins](https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html).\n\"\"\"\n\n@document add_section! \"\"\"\n```\nadd_section!(self::MenuModel, title::String, to_add::MenuModel, [::SectionFormat]) \n```\nAdd a \"section\"-type menu item to the model, which will be constructed based on `to_add`.\n\"\"\"\n\n@document add_shortcut! \"\"\"\n```\nadd_shortcut!(::Action, shortcut::ShortcutTrigger) \n```\nAdd a shortcut trigger to the list of shortcuts. To make a widget listen for action shortcuts, use [`set_listens_for_shortcut_action!`](@ref), or use a [`ShortcutEventController`](@ref).\n\"\"\"\n\n@document add_submenu! \"\"\"\n```\nadd_submenu!(model::MenuModel, label::String, to_add::MenuModel) \n```\nAdd a \"submenu\"-type menu item to the model, which will be constructed based on `to_add`.\n\"\"\"\n\n@document add_widget! \"\"\"\n```\nadd_widget!(model::MenuModel, ::Widget) \n```\nAdd a \"widget\"-type item to the model. This widget should be interactable.\n\"\"\"\n\n@document alt_pressed \"\"\"\n```\nalt_pressed(::ModifierState) -> Bool\n```\nCheck if modifier state indicates that the `Alt` key is currently pressed.\n\"\"\"\n\n@document apply_to \"\"\"\n```\napply_to(::GLTransform, ::Vector2f) -> Vector2f\napply_to(::GLTransform, ::Vector3f) -> Vector3f\n```\nApply transform to a vector, both operate in OpenGL coordinates.\n\"\"\"\n\n@document as_circle! \"\"\"\n```\nas_circle!(::Shape, center::Vector2f, radius::Number, n_outer_vertices::Integer) \n```\nCreate a shape as a circle, defined by its center and radius. In OpenGL coordinates.\n\"\"\"\n\n@document as_circular_ring! \"\"\"\n```\nas_circular_ring!(::Shape, center::Vector2f, outer_radius::Number, thickness::Number, n_outer_vertices::Integer) \n```\nCreate a shape as a circular ring, defined by its center, `outer_radius`, which is the distance to the out perimeter, and `thickness` which is the distance between the inner and outer perimeter.\n\"\"\"\n\n@document as_cropped \"\"\"\n```\nas_cropped(image::Image, offset_x::Signed, offset_y::Signed, new_width::Integer, new_height::Integer) -> Image\n```\nCrop the image, this is similar to the \"resize canvas\" operation in many image manipulation programs.\n`offset_x` and `offset_y` is the offset between the top-left pixel of the image and the top-left pixel of the\nnewly allocated area, where an offset of 0 means the new image is anchored at the same pixel as the original.\nOffsets can be negative.\n\nThis function does not modify the original image.\n\"\"\"\n\n@document as_degrees \"\"\"\n```\nas_degrees(angle::Angle) -> Float64\n```\nConvert the angle to degrees, in [0°, 360°].\n\"\"\"\n\n@document as_ellipse! \"\"\"\n```\nas_ellipse!(::Shape, center::Vector2f, x_radius::Number, y_radius::Number, n_outer_vertices::Integer) \n```\nCreate a shape as an ellipse, defined by its center and radii along the x- and y-dimension, in OpenGL coordinates.\n\"\"\"\n\n@document as_elliptical_ring! \"\"\"\n```\nas_elliptical_ring!(::Shape, center::Vector2f, outer_x_radius::Number, outer_y_radius::Number, x_thickness::Number, y_thickness::Number, n_outer_vertices::Unsigned) \n```\nCreate a shape as an elliptical ring, where\n+ `center`: center of the ring\n+ `outer_x_radius`: distance between the center and the outer perimeter along the x-axis\n+ `outer_y_radius`: distance between the center and the outer perimeter along the y-axis\n+ `x_thickness`: distance between the outer and inner perimeter along the x-axis\n+ `y_thickness``: distance between the outer and inner perimeter along the y-axis\n\nIn OpenGL coordinates.\n\"\"\"\n\n@document as_flipped \"\"\"\n```\nas_flipped(::Image, flip_horizontally::Bool, flip_vertically::Bool) -> Image\n```\nCreate a new image that is a horizontally and/or vertically mirrored.\n\nThis function does not modify the original image.\n\"\"\"\n\n@document as_line! \"\"\"\n```\nas_line!(::Shape, a::Vector2f, b::Vector2f) \n```\nCreate a shape as a 1-fragment thick line between two points, in OpenGL coordinates.\n\"\"\"\n\n@document as_line_strip! \"\"\"\n```\nas_line_strip!(::Shape, points::Vector{Vector2f}) \n```\nCreate a shape as a line strip. For points `{a1, a2, ..., an}`, this will be a set of connected lines `{a1, a2}, {a2, a3}, ..., {an-1, an}`, in OpenGL coordinates.\n\"\"\"\n\n@document as_lines! \"\"\"\n```\nas_lines!(::Shape, points::Vector{Pair{Vector2f, Vector2f}}) \n```\nCreate a shape as a set of unconnected lines, vertex positions in OpenGL coordinates.\n\"\"\"\n\n@document as_microseconds \"\"\"\n```\nas_microseconds(time::Time) -> Float64\n```\nConvert to microseconds.\n\"\"\"\n\n@document as_milliseconds \"\"\"\n```\nas_milliseconds(time::Time) -> Float64\n```\nConvert to milliseconds.\n\"\"\"\n\n@document as_minutes \"\"\"\n```\nas_minutes(time::Time) -> Float64\n```\nConvert to minutes.\n\"\"\"\n\n@document as_nanoseconds \"\"\"\n```\nas_nanoseconds(time::Time) -> UInt64\n```\nConvert to number of nanoseconds.\n\"\"\"\n\n@document as_outline! \"\"\"\n```\nas_outline!(self::Shape, other::Shape) \n```\nCreate a shape as an outline of another shape.\n\"\"\"\n\n@document as_point! \"\"\"\n```\nas_point!(::Shape, position::Vector2f) \n```\nCreate a shape as 1-fragment point, in OpenGL coordinates.\n\"\"\"\n\n@document as_points! \"\"\"\n```\nas_points!(::Shape, positions::Vector{Vector2f}) \n```\nCreate a shape as a set of unconnected points, in OpenGL coordinates.\n\"\"\"\n\n@document as_polygon! \"\"\"\n```\nas_polygon!(::Shape, points::Vector{Vector2f}) \n```\nCreate a shape as a convex polygon, the outer hull of `points` will be computed, but no vertex elimination will take place. In OpenGL coordiantes.\n\"\"\"\n\n@document as_radians \"\"\"\n```\nas_radians(angle::Angle) \n```\nConvert to radians, in [0, 2π].\n\"\"\"\n\n@document as_rectangle! \"\"\"\n```\nas_rectangle!(::Shape, top_left::Vector2f, size::Vector2f) \n```\nCreate a shape as an axis-aligned rectangle, in OpenGL coordinates.\n\"\"\"\n\n@document as_rectangular_frame! \"\"\"\n```\nas_rectangular_frame!(::Shape, top_left::Vector2f, outer_size::Vector2f, x_width::Number, y_width::Number) \n```\nCreate a shape as a rectangular frame, where `x_width`, `y_width` are the \"thickness\" of the frames filled area. In OpenGL coordinates.\n\"\"\"\n\n@document as_scaled \"\"\"\n```\nas_scaled(::Image, size_x::Integer, size_y::Integer, ::InterpolationType) -> Image\n```\nScale the image to a new size. This is similar to the \"scale image\" option in many image manipulation programs.\n\nNote that this does not modify the original image.\n\"\"\"\n\n@document as_seconds \"\"\"\n```\nas_seconds(time::Time) -> Float64\n```\nConvert to seconds.\n\"\"\"\n\n@document as_string \"\"\"\n```\nas_string(::KeyFile) -> String\n```\nSerialize file into a string.\n\"\"\"\n\n@document as_triangle! \"\"\"\n```\nas_triangle!(::Shape, a::Vector2f, b::Vector2f, c::Vector2f) \n```\nCreate a shape as a triangle defined by three points, in OpenGL coordinates.\n\"\"\"\n\n@document as_wireframe! \"\"\"\n```\nas_wireframe!(::Shape, points::Vector{Vector2f}) \n```\nCreate a shape as a wire-frame. For points `{a1, a2, a3, ..., an}`, the shape\nwill be a connected series of lines `{a1, a2}, {a2, a3}, ..., {an-1, an}, {an, a1}`. In OpenGL coordinates.\n\"\"\"\n\n@document attach_to! \"\"\"\n```\nattach_to!(popover::Popover, attachment::Widget) \n```\nAttach a popover to a widget, which anchors the graphical element of the popover such that it points to the widget.\n\"\"\"\n\n@document bind \"\"\"\n```\nbind(::TextureObject) \n```\nBind a texture for rendering, which will make it available at `GL_TEXTURE_UNIT_0`. This is usually done automatically when a texture was registered with a shape using [`set_texture!`](@ref).\n\"\"\"\n\n@document bind_as_render_target \"\"\"\n```\nbind_as_render_target(render_texture::RenderTexture) \n```\nBind a render texture as the current frame buffer. This should be done inside the signal handler \nof `RenderArea`'s signal `render`.\n\nUse [`unbind_as_render_target`](@ref) to restore the previously bound frame buffer.\n\"\"\"\n\n@document cancel! \"\"\"\n```\ncancel!(::FileChooser)\n```\nCancel the file chooser, this will behave identically to the user clicking the cancel button.\n\n---\n\n```\ncancel!(::FileMonitor) \n```\nCancel the file monitor. It will no longer monitor the file.\n\"\"\"\n\n@document calculate_monitor_dpi \"\"\"\n```\ncalculate_monitor_dpi(::Widget) -> Float32\n```\nCalculate the dpi (dots per inch) of the monitor that is currently displaying the widgets associated window.\n\"\"\"\n\n@document clear \"\"\"\n```\nclear(::RenderArea) \n```\nClear the current framebuffer, this will reset `GL_COLOR_BUFFER_BIT` and replace the contents of the currently bound framebuffer with `RGBA(0, 0, 0, 0)`.\n\"\"\"\n\n@document clear! \"\"\"\n```\nclear!(::Box) \nclear!(::FlowBox)\nclear!(::ImageDisplay) \nclear!(::ListView) \nclear!(::ListView, [iterator::ListViewIterator]) \nclear!(::GridView) \n```\nRemove all children from the widget.\n\"\"\"\n\n@document clear_filters! \"\"\"\n```\nclear_filters!(::FileChooser)\n```\nRemove all registered file filters.\n\"\"\"\n\n@document clear_marks! \"\"\"\n```\nclear_marks!(::Scale)\n```\nRemove all marks added via [`add_mark!`](@ref).\n\"\"\"\n\n@document clear_render_tasks! \"\"\"\n```\nclear_render_tasks!(::RenderArea) \n```\nRemove all registered render tasks.\n\"\"\"\n\n@document clear_shortcuts! \"\"\"\n```\nclear_shortcuts!(::Action) \n```\nRemove all registered shortcut triggers.\n\"\"\"\n\n@document close! \"\"\"\n```\nclose!(::Window) \n```\nAttempt to close the window, this will emit signal `close_request`.\n\"\"\"\n\n@document combine_with \"\"\"\n```\ncombine_with(self::GLTransform, other::GLTransform) -> GLTransform\n```\nPerform matrix-multiplication and return the resulting transform, in OpenGL coordinates.\n\"\"\"\n\n@document contains_file \"\"\"\n```\ncontains_file(::Clipboard) -> Bool\n```\nCheck whether the clipboard contains a file path.\n\"\"\"\n\n@document contains_image \"\"\"\n```\ncontains_image(::Clipboard) -> Bool\n```\nCheck whether the clipboard contains an image.\n\"\"\"\n\n@document contains_string \"\"\"\n```\ncontains_string(::Clipboard) -> Bool\n```\nCheck whether the clipboard contains a string.\n\"\"\"\n\n@document control_pressed \"\"\"\n```\ncontrol_pressed(modifier_state::ModifierState) -> Bool\n```\nCheck whether the modifier state indicates that `Control` is currently pressed.\n\"\"\"\n\n@document copy! \"\"\"\n```\ncopy!(from::FileDescriptor, to::FileDescriptor, allow_overwrite::Bool ; make_backup::Bool = false, follow_symlink::Bool = false) -> Bool\n```\nCopy a file from one location to another. Returns `true` if the operation was successful.\n\"\"\"\n\n@document create! \"\"\"\n```\ncreate!(::Image, width::Integer, height::Integer, [color::RGBA]) \ncreate!(::TextureObject, width::Integer, height::Integer) \n```\nClear the current pixeldata and reinitialize it at given size. Will fille each pixel with `RGBA(0, 0, 0, 0)`, unless otherwise specified.\n\"\"\"\n\n@document create_as_file_preview! \"\"\"\n```\ncreate_as_file_preview!(image_display::ImageDisplay, file::FileDescriptor) \n```\nIf the `file` points to an image file, create a preview for that image, otherwise create from the \nfiles default icon.\n\"\"\"\n\n@document create_directory_at! \"\"\"\n```\ncreate_directory_at!(destination::FileDescriptor) -> Bool\n```\nCreate folder at given location, returns `true` if the operation was successful.\n\"\"\"\n\n@document create_file_at! \"\"\"\n```\ncreate_file_at!(destination::FileDescriptor, should_replace::Bool) -> Bool\n```\nCreate file at given location, returns `true` if the operation was successful\n\"\"\"\n\n@document create_from_file! \"\"\"\n```\ncreate_from_file!(::Icon, path::String) -> Bool\ncreate_from_file!(::Image, path::String) -> Bool\ncreate_from_file!(::ImageDisplay, path::String) -> Bool\ncreate_from_file!(::KeyFile, path::String) -> Bool\ncreate_from_file!(::Shader, type::ShaderType, file::String) -> Bool \n```\nInitialize the object from a file. Returns `true` if the operation was successful.\n\"\"\"\n\n@document create_from_icon! \"\"\"\n```\ncreate_from_icon!(::ImageDisplay, icon::Icon) \n```\nCreate as preview of an icon.\n\"\"\"\n\n@document create_from_image! \"\"\"\n```\ncreate_from_image!(::TextureObject, ::Image) \ncreate_from_image!(::ImageDisplay, ::Image) \n```\nInitialize from an image. This will replace the current pixel data and size.\n\"\"\"\n\n@document create_from_path! \"\"\"\n```\ncreate_from_path!(::FileDescriptor, path::String) \n```\nCreate as file descriptor pointing to given path. Use [`exists`](@ref) to check if this location is valid and contains a file or folde.\n\"\"\"\n\n@document create_from_string! \"\"\"\n```\ncreate_from_string!(::KeyFile, file::String) \n```\nDe-serialize from a string. If you are loading a `KeyFile` from a file on disk, [`create_from_file!`](@ref) should be preferred.\n\n---\n\n```\ncreate_from_string!(::Shader, type::ShaderType, glsl_code::String) \n```\nCreate from GLSL code. See the manual chapter on native rendering for more information.\n\"\"\"\n\n@document create_from_theme! \"\"\"\n```\ncreate_from_theme!(::Icon, theme::IconTheme, id::String, square_resolution::Integer, [scale::Integer = 1]) \n```\nCreate an icon from the icon theme.\n\"\"\"\n\n@document create_from_uri! \"\"\"\n```\ncreate_from_uri!(::FileDescriptor, uri::String) \n```\nCreate as file descriptor pointing to given URI. There is no guarantee that the location exists or points to a valid file or folder.\n\"\"\"\n\n@document create_monitor \"\"\"\n```\ncreate_monitor(descriptor::FileDescriptor) -> FileMonitor\n```\nCreate a `FileMonitor` monitoring the current file or folder. This may fail if the location does not contain a valid file.\n\"\"\"\n\n@document degrees \"\"\"\n```\ndegrees(::Number) -> Angle\n```\nCreate angle from degrees, automatically clamped to [0°, 360°].\n\"\"\"\n\n@document delete_at! \"\"\"\n```\ndelete_at!(::FileDescriptor) -> Bool\n```\nIrreversibly delete file at given location, returns `true` if the operation was successful. \n\"\"\"\n\n@document device_axis_to_string \"\"\"\n```\ndevice_axis_to_string(axis::DeviceAxis) -> String\n```\nSerialize the axes identification.\n\"\"\"\n\n@document destroy! \"\"\"\n```\ndestroy!(::Window) -> Cvoid\n```\nFree the internally held reference, causing the window to be deallocated. This is only necessary if [`set_hide_on_close!`](@ref) was set to `true`.\n\"\"\"\n\n@document download \"\"\"\n```\ndownload(texture::TextureObject) -> Image\n```\nRetrieve the pixel data from the graphics card and return it as an image. This is an extremely costly operation.\n\"\"\"\n\n@document elapsed \"\"\"\n```\nelapsed(clock::Clock) -> Time\n```\nGet time since the clock was last restarted.\n\"\"\"\n\n@document exists \"\"\"\n```\nexists(::FileDescriptor) -> Bool\n```\nCheck if file location contains a valid file or folder.\n\"\"\"\n\n@document find \"\"\"\n```\nfind(::ListView, ::Widget, [::ListViewIterator]) -> Signed\nfind(::GridView, ::Widget) -> Signed\n``` \nGet index of widget in view, or -1 if widget is not part of view.\n\"\"\"\n\n@document flush \"\"\"\n```\nflush(::RenderArea) -> Cvoid\n```\nEquivalent to `glFlush`, requests for the bound framebuffer to be pushed to the screen. This may not immediately \nupdate the `RenderArea`.\n\"\"\"\n\n@document from_gl_coordinates \"\"\"\n```\nfrom_gl_coordinates(area::RenderArea, gl_coordinates::Vector2f) -> Vector2f\n```\nConvert OpenGL coordinates to absolute widget-space coordinates. This will take into account the `RenderArea`s currently allocated size on screen.\n\"\"\"\n\n@document get_acceleration_rate \"\"\"\n```\nget_acceleration_rate(::SpinButton) -> Float64\n```\nGet the current rate at which the spin button accelerates when one of the buttons is held down.\n\"\"\"\n\n@document get_accept_label \"\"\"\n```\nget_accept_label(::FileChooser) -> String\n```\nGet the label used for the \"accept\" button.\n\"\"\"\n\n@document get_action \"\"\"\n```\nget_action(app::Application, id::String) -> Action\n```\nRetrieve an action registered with the application.\n\"\"\"\n\n@document get_adjustment \"\"\"\n```\nget_adjustment(::Scale) -> Adjustment\nget_adjustment(::SpinButton) -> Adjustment\nget_adjustment(::Scrollbar) -> Adjustment\n```\nRetrieve the adjustment of the widget. Modifying the adjustment will modify the widget, and vice-versa.\n\"\"\"\n\n@document get_allocated_size \"\"\"\n```\nget_allocated_size(::Widget) -> Vector2f\n```\nGet the size the widget currently occupies on screen, in pixels. If the widget is not currently shown, this function will return `(0, 0)`.\n\"\"\"\n\n@document get_allow_only_numeric \"\"\"\n```\nget_allow_only_numeric(::SpinButton) -> Bool\n```\nGet whether the spin button only accepts numerical strings for its text-entry.\n\"\"\"\n\n@document get_angle_delta \"\"\"\n```\nget_angle_delta(::RotateEventController) -> Angle\n```\nGet the difference between the current angle and the angle recognized when the gesture started.\n\"\"\"\n\n@document get_autohide \"\"\"\n```\nget_autohide(::Popover) -> Bool\n```\nGet whether the popover should automatically hide when it loses focus.\n\"\"\"\n\n@document get_auto_render \"\"\"\n```\nget_auto_render(::GLArea) -> Bool\n```\nGet whether the `render` signal is emitted any time the widget is drawn.\n\"\"\"\n\n@document get_axis_value \"\"\"\n```\nget_axis_value(::StylusEventController, ::DeviceAxis) -> Float32\n```\nGet value for the devices axis, or 0 if no such axis is present. This value will usually be in `[0, 1]`.\n\"\"\"\n\n@document get_button_action_id \"\"\"\n```\nget_button_action_id(::PopupMessage) -> ActionID\n```\nGet the ID of the messages button, or `\"\"` if no button is present.\n\"\"\"\n\n@document get_button_label \"\"\"\n```\nget_button_label!(::AlertDialog, index::Integer) -> String\n```\nGet label of the button at given ID, obtained when calling `add_button!`.\n\n--\n\n```\nget_button_label!(::PopupMessage) -> String\n```\nGet label of the singular button, or `\"\"` if no button is present.\n\"\"\"\n\n@document get_bottom_margin \"\"\"\n```\nget_bottom_margin(::TextView) -> Float32\n```\nGet the distance between the bottom of the text and the bottom of the text views frame.\n\"\"\"\n\n@document get_bounding_box \"\"\"\n```\nget_bounding_box(::Shape) -> AxisAlignedRectangle\n```\nGet the axis-aligned bounding box of the shape. This is the smallest rectangle that contains all vertices, in OpenGL coordinates.\n\"\"\"\n\n@document get_can_respond_to_input \"\"\"\n```\nget_can_respond_to_input(::Widget) -> Bool\n```\nGet whether the widget can receive and capture input events.\n\"\"\"\n\n@document get_centroid \"\"\"\n```\nget_centroid(::Shape) -> Vector2f\n```\nGet the centroid of the shape, this is the mathematical average of all its vertices positions, in OpenGL coordinates.\n\"\"\"\n\n@document get_child_at \"\"\"\n```\nget_child_at(::Stack, index::Integer) -> StackID\n```\nRetrieve the ID of the stack page at given position, or `\"\"` if the index is out of bounds.\n\"\"\"\n\n@document get_child_x_alignment \"\"\"\n```\nget_child_x_alignment(::AspectFrame) -> Float32\n```\nGet the horizontal alignment of the aspect frames child, in `[0, 1]`.\n\"\"\"\n\n@document get_child_y_alignment \"\"\"\n```\nget_child_y_alignment(::AspectFrame) \n```\nGet the vertical alignment of the aspect frames child, in `[0, 1]`.\n\"\"\"\n\n@document get_children \"\"\"\n```\nget_children(descriptor::FileDescriptor ; [recursive = false]) -> Vector{FileDescriptor}\n```\nGet all children of a folder. If the location pointed to by `descriptor` is a file or does not exist, the resulting vector will be empty.\n\"\"\"\n\n@document get_clipboard \"\"\"\n```\nget_clipboard(::Widget) -> Clipboard\n```\nRetrieve the clipboard from a widget, which should usually be the top-level window.\n\"\"\"\n\n@document get_color \"\"\"\n```\nget_color(::ColorChooser) -> RGBA\n```\nGet the currently selected color.\n\"\"\"\n\n@document get_column_at \"\"\"\n```\nget_column_at(column_view::ColumnView, index::Integer) -> ColumnViewColumn\n```\nGet column at specified position, 1-based.\n\"\"\"\n\n@document get_column_spacing \"\"\"\n```\nget_column_spacing!(::FlowBox) -> Float32\nget_column_spacing(::Grid) -> Float32\n```\nGet spacing between columns, in pixels.\n\"\"\"\n\n@document get_column_with_title \"\"\"\n```\nget_column_with_title(column_view::ColumnView, title::String) -> ColumnViewColumn\n```\nGet column with specified title.\n\"\"\"\n\n@document get_columns_homogeneous \"\"\"\n```\nget_columns_homogeneous(::Grid) -> Bool\n```\nGet whether all columns should allocate the same width.\n\"\"\"\n\n@document get_comment_above \"\"\"\n```\nget_comment_above(::KeyFile, group::GroupID) -> String\nget_comment_above(::KeyFile, group::GroupID, key::KeyID) -> String\n```\nGet the singular comment above a group or key declaration. \n\"\"\"\n\n@document get_content_type \"\"\"\n```\nget_content_type(::FileDescriptor) -> String\n```\nGet the file type as a MIME identification string.\n\"\"\"\n\n@document get_css_classes \"\"\"\n```\nget_css_classes(::Widget) -> Vector{String}\n```\nGet all CSS classes currently applied to that widget.\n\"\"\"\n\n@document get_current_button \"\"\"\n```\nget_current_button(gesture::SingleClickGesture) -> ButtonID\n```\nGet the ID of the button that triggered the current event.\n\"\"\"\n\n@document get_current_offset \"\"\"\n```\nget_current_offset(::DragEventController) -> Vecto2f\n```\nGet the distance between the current cursor position and the point of origin for the drag-gestured, in absolute widget-space coordinates.\n\"\"\"\n\n@document get_current_theme \"\"\"\n```\nget_current_theme(::Application) -> Theme\n```\nGet the currently used theme, or `THEME_DEFAULT_LIGHT` if the application is uninitialized and no theme was chosen yet.\n\"\"\"\n\n@document get_current_page \"\"\"\n```\nget_current_page(::Notebook) -> Int64\n```\nGet index of currently active page.\n\"\"\"\n\n@document get_cursor_visible \"\"\"\n```\nget_cursor_visible(::TextView) -> Bool\n```\nGet whether the text caret should be visible.\n\"\"\"\n\n@document get_delay_factor \"\"\"\n```\nget_delay_factor(::LongPressEventController) -> Float32\n```\nGet multiplier that determines after how much time a long press gesture is recognized, where `1` is no change as compared to the default, `2` is twice as long, `0.5` is half as long.\n\"\"\"\n\n@document get_destroy_with_parent \"\"\"\n```\nget_destroy_with_parent(::Window) -> Bool\n```\nGet whether the window should be closed and deallocated when its parent window is.\n\"\"\"\n\n@document get_detailed_description \"\"\"\n```\nget_detailed_description(::AlertDIalog) -> String\n```\nGet detailed message, this is the text shown below the dialogs title.\n\"\"\"\n\n@document get_duration \"\"\"\n```\nget_duration(::Animation) -> Time\n```\nGet the target duration of the animation.\n\"\"\"\n\n@document get_editable \"\"\"\n```\nget_editable(::TextView) -> Bool\n```\nGet whether the user can edit the text.\n\"\"\"\n\n@document get_ellipsize_mode \"\"\"\n```\nget_ellipsize_mode(::Label) -> EllipsizeMode\n```\nGet the ellipsize mode of a label, `ELLIPSIZE_MODE_NONE` by default.\n\"\"\"\n\n@document get_enabled \"\"\"\n```\nget_enabled(::Action) -> Bool\n```\nGet whether the action is enabled. A disabled action cannot be activated and all its connected widgets are disabled.\n\"\"\"\n\n@document get_enable_rubberband_selection \"\"\"\n```\nget_enable_rubberband_selection(::ListView) -> Bool\nget_enable_rubberband_selection(::GridView) -> Bool\nget_enable_rubberband_selection(::ColumnView) -> Bool\n```\nGet whether the user can select multiple children by click-dragging with the cursor. The selectable widgets\nselection mode has to be `SELECTION_MODE_MULTIPLE` in order for this to be possible.\n\"\"\"\n\n@document get_end_child_resizable \"\"\"\n```\nget_end_child_resizable(::Paned) -> Bool\n```\nGet whether the end child should resize when the `Paned` is resized.\n\"\"\"\n\n@document get_end_child_shrinkable \"\"\"\n```\nget_end_child_shrinkable(::Paned) -> Bool\n```\nGet whether the user can resize the end child such that its allocated area inside the `Paned` is smaller than the natural size of the child.\n\"\"\"\n\n@document get_expand_horizontally \"\"\"\n```\nget_expand_horizontally(::Widget) -> Bool\n```\nGet whether the widget can expand along the x-axis.\n\"\"\"\n\n@document get_expand_vertically \"\"\"\n```\nget_expand_vertically(::Widget) -> Bool\n```\nGet whether the widget can expand along the y-axis.\n\"\"\"\n\n@document get_is_expanded \"\"\"\n```\nget_is_expanded(::Expander) -> Bool\n```\nGet whether the `Expander`'s child is currently visible.\n\"\"\"\n\n@document get_file_chooser_action \"\"\"\n```\nget_file_chooser_action(::FileChooser) -> FileChooserAction\n```\nGet the file chooser action type.\n\"\"\"\n\n@document get_file_extension \"\"\"\n```\nget_file_extension(::FileDescriptor) -> String\n```\nGet the file extension of the file. This will be any characters after the last `.`.\n\"\"\"\n\n@document get_fixed_width \"\"\"\n```\nget_fixed_width(::ColumnViewColumn) -> Float32\n```\nGet the target width of the column, in pixels.\n\"\"\"\n\n@document get_focus_on_click \"\"\"\n```\nget_focus_on_click(::Widget) -> Bool\n```\nGet whether the widget should grab focus when it is clicked.\n\"\"\"\n\n@document get_focus_visible \"\"\"\n```\nget_focus_visible(::Window) -> Bool\n```\nGet whether which widget currently holds input focus should be highlighted using a border.\n\"\"\"\n\n@document get_fraction \"\"\"\n```\nget_fraction(::ProgressBar) -> Float32\n```\nGet the currently displayed fraction of the `ProgressBar`, in `[0, 1]`.\n\"\"\"\n\n@document get_fragment_shader_id \"\"\"\n```\nget_fragment_shader_id(::Shader) -> Cuint\n```\nGet the native OpenGL handle of the shader programs fragment shader component.\n\"\"\"\n\n@document get_groups \"\"\"\n```\nget_groups(::KeyFile) -> Vector{GroupID}\n```\nGet all group IDs currently present in the key file.\n\"\"\"\n\n@document get_hardware_id \"\"\"\n```\nget_hardware_id(::StylusEventController) -> Cuint\n```\nGet the native ID of the stylus-device that caused the current event.\n\"\"\"\n\n@document get_has_base_arrow \"\"\"\n```\nget_has_base_arrow(::Popover) -> Bool\n```\nGet whether the arrow-shaped \"tail\" of the popover is visible.\n\"\"\"\n\n@document get_has_border \"\"\"\n```\nget_has_border(::Notebook) -> Bool\n```\nGet whether a border should be drawn around the notebooks perimeter.\n\"\"\"\n\n@document get_has_close_button \"\"\"\n```\nget_has_close_button(::Window) -> Bool\n```\nGet whether the \"x\" button is present.\n\"\"\"\n\n@document get_has_focus \"\"\"\n```\nget_has_focus(::Widget) -> Bool\n```\nCheck whether the input currently holds input fcus.\n\"\"\"\n\n@document get_has_frame \"\"\"\n```\nget_has_frame(::Button) -> Bool\nget_has_frame(::Viewport) -> Bool\nget_has_frame(::Entry) -> Bool\nget_has_frame(::PopoverButton) -> Bool \n```\nGet whether the widget's outline should be displayed, it will remain interactable.\n\"\"\"\n\n@document get_has_origin \"\"\"\n```\nget_has_origin(::Scale) -> Bool\n```\nGet whether the area between the origin of the scales trough and the current value should be fille with a solid color.\n\"\"\"\n\n@document get_has_wide_handle \"\"\"\n```\nget_has_wide_handle(::Paned) -> Bool\n```\nGet whether the barrier in between the `Paned`'s two children is wide or thin, wide by default.\n\"\"\"\n\n@document get_header_bar \"\"\"\n```\nget_header_bar(::Window) -> header_bar\n```\nAccess the `HeaderBar` instance used as the window's titlebar widget.\n\"\"\"\n\n@document get_hide_on_close \"\"\"\n```\nget_hide_on_close(::Window) -> Bool\n```\nGet whether the window will be hidden when it is closed, as opposed to destroyed.\n\"\"\"\n\n@document get_hide_on_overflow \"\"\"\n```\nget_hide_on_overflow(::Widget) -> Bool\n```\nGet whether the entire widget should be hidden if its allocated area is smaller than its natural size. If `false`, the overflow part of the widget will be truncated.\n\"\"\"\n\n@document get_homogeneous \"\"\"\n```\nget_homogeneous(::Box) -> Bool\n```\nGet whether all of the `Box`'s children should be allocated the same width (or height, if orientation is `ORIENTATION_VERTICAL`).\n\"\"\"\n\n@document get_horizontal_adjustment \"\"\"\n```\nget_horizontal_adjustment(viewport::Viewport) -> Adjustment\n```\nGet the adjustment controlling the horizontal scrollbar.\n\"\"\"\n\n@document get_horizontal_alignment \"\"\"\n```\nget_horizontal_alignemtn(::Widget) -> Alignment\n```\nGet alignment along the x-axis.\n\"\"\"\n\n@document get_horizontal_scrollbar_policy \"\"\"\n```\nget_horizontal_scrollbar_policy(::Viewport) -> ScrollbarVisibilityPolicy\n```\nGet the policy governing how and if the horizontal scrollbar is revealed / hidden.\n\"\"\"\n\n@document get_icon_names \"\"\"\n```\nget_icon_names(theme::IconTheme) -> Vector{String}\n```\nGet the ID of all icons available in the icon theme.\n\"\"\"\n\n@document get_id \"\"\"\n```\nget_id(::Application) -> ApplicationID\nget_id(::Action) -> ActionID\n```\nAccess the ID specified during the object's construction.\n\"\"\"\n\n@document get_image \"\"\"\n```\nget_image(f, ::Clipboard, [::Data_t])\n```\nRegister a callback to read an image from the clipboad. Once the clipboard is ready, the callback will be invoked.\n\n`f` is required to be invocable as a function with signature\n\n```julia\n(::Clipboard, ::Image, [::Data_t]) -> Nothing\n```\n\n## Example\n```julia\nclipboard = get_clipboard(window)\nif contains_image(clipboard)\n    get_image(clipboard) do x::Clipboard, image::Image\n        # use image here\n    end\nend\n```\n\"\"\"\n\n@document get_inverted \"\"\"\n```\nget_inverted(::LevelBar) -> Bool\n```\nGet whether the level bar should be mirrored along the horizontal or vertical axis, depending on orientation.\n\"\"\"\n\n@document get_is_active \"\"\"\n```\nget_is_active(::CheckButton) -> Bool\nget_is_active(::Switch) -> Bool\nget_is_active(::ToggleButton) -> Bool\n```\nGet whether the internal state of the widget is active.\n\"\"\"\n\n@document get_is_circular \"\"\"\n```\nget_is_circular(::Button) -> Bool\nget_is_circular(::ToggleButton) -> Bool\nget_is_circular(::PopoverButton)  -> Bool\n```\nGet whether the button is circular (as opposed to rectangular, the default).\n\"\"\"\n\n@document get_is_closed \"\"\"\n```\nget_is_closed(::Window) -> Bool\n```\nReturns `false` if the window is currently active and visible to the user, `true` otherwise.\n\"\"\"\n\n@document get_is_decorated \"\"\"\n```\nget_is_decorated(::Window) -> Bool\n```\nGet whether the header bar area of the window is visible.\n\"\"\"\n\n@document is_executable \"\"\"\n```\nis_executable(::FileDescriptor) -> Bool\n```\nGet whether the file location contains an executable.\n\"\"\"\n\n@document get_is_focusable \"\"\"\n```\nget_is_focusable(::Widget) -> Bool\n```\nGet whether the widget can grab input focus.\n\"\"\"\n\n@document get_is_high_priority \"\"\"\n```\nget_is_high_priority(::PopupMessage) -> Bool\n```\nGet whether this message has a high priority. High priority messages will be shown before non-high-priority if queued with `PopupMessageOverlay`.\n\"\"\"\n\n@document get_is_holding \"\"\"\n```\nget_is_holding(::Application) -> Bool\n```\nGet whether [`hold!`](@ref) was called and the application currently blocks attempts at exiting.\n\"\"\"\n\n@document get_is_horizontally_homogeneous \"\"\"\n```\nget_is_horizontally_homogeneous(::Stack) -> Bool\n```\nGet whether all pages of the stack should allocate the same width.\n\"\"\"\n\n@document get_is_inverted \"\"\"\n```\nget_is_inverted(::ProgressBar) -> Bool\n```\nGet whether the progress bar should fill from right-to-left, instead of left-to-right\n\"\"\"\n\n@document get_is_modal \"\"\"\n```\nget_is_modal(::Window) -> Bool\nget_is_modal(::FileChooser) -> Bool\nget_is_modal(::ColorChooser) -> Bool\nget_is_modal(::AlertDialog) -> Bool\n```\nGet whether all other windows should be paused while this window is active.\n\"\"\"\n\n@document get_is_marked_as_busy \"\"\"\n```\nget_is_marked_as_busy(::Application) -> Bool\n```\nReturns `true` if [`mark_as_busy!`](@ref) was called before.\n\"\"\"\n\n@document get_is_realized \"\"\"\n```\nget_is_realized(::Widget) -> Bool\n```\nGet whether the widget was initialized and is now shown on screen.\n\"\"\"\n\n@document get_is_resizable \"\"\"\n```\nget_is_resizable(::ColumnViewColumn) -> Bool\n```\nGet whether the user can choose the width of this column by click-dragging.\n\"\"\"\n\n@document get_is_reversed \"\"\"\n```\nget_is_reversed(::Animation) -> Bool\n```\nIf `false`, the animation will interpolate its value from the lower to upper bound, or the other way around if `true`.\n\"\"\"\n\n@document get_is_scrollable \"\"\"\n```\nget_is_scrollable(::Notebook) -> Bool\n```\nGet whether the user can scroll between pages using the mouse scroll wheel or touchscreen.\n\"\"\"\n\n@document get_is_spinning \"\"\"\n```\nget_is_spinning(::Spinner) -> Bool\n```\n Get whether the `Spinner`s animation is currently playing.\n\"\"\"\n\n@document get_is_vertically_homogeneous \"\"\"\n```\nget_is_vertically_homogeneous(::Stack) -> Bool\n```\nGet whether the stack should allocate the same height for all its pages.\n\"\"\"\n\n@document get_is_visible \"\"\"\n```\nget_is_visible(::Widget) -> Bool\nget_is_visible(::ColumnViewColumn) -> Bool \n```\nGet whether the object is currently shown on screen.\n\n---\n\n```\nget_is_visible(::Shape) -> Bool\n```\nGet whether the shape should be omitted from rendering, where `false` means it will be ommitted.\n\"\"\"\n\n@document get_item_at \"\"\"\n```\nget_item_at(::DropDown, i::Integer) -> DropDownID\n```\nGet ID of the item at given position.\n\"\"\"\n\n@document get_justify_mode \"\"\"\n```\nget_justify_mode(::Label) -> JustifyMode\nget_justify_mode(::TextView) -> JustifyMode\n```\nGet the currently used justify mode.\n\"\"\"\n\n@document get_keys \"\"\"\n```\nget_keys(::KeyFile, group::GroupID) -> Vector{KeyID}\n```\nGet all keys in this group, or an empty vector if the group does not exist.\n\"\"\"\n\n@document get_kinetic_scrolling_enabled \"\"\"\n```\nget_kinetic_scrolling_enabled(::Viewport) -> Bool\nget_kinetic_scrolling_enabled(::ScrollEventController) -> Bool\n```\nGet whether scrolling should continue once the user stops operating the mouse wheel or touchscreen, simulating \"inertia\".\n\"\"\"\n\n@document get_label_x_alignment \"\"\"\n```\nget_label_x_alignment(::Frame) -> Float32\n```\nGet the horizontal alignment of the `Frame`'s optional label widget, in `[0, 1]`.\n\"\"\"\n\n@document get_layout \"\"\"\n```\nget_layout(::HeaderBar) -> String\n```\nGet the layout string of the header bar. See the manual section on `HeaderBar` in the chapter on widgets for how layout-syntax works.\n\"\"\"\n\n@document get_left_margin \"\"\"\n```\nget_left_margin(::TextView) -> Float32\n```\nGet distance between the left side of the text and the `TextView`'s frame.\n\"\"\"\n\n@document get_lower \"\"\"\n```\nget_lower(::Adjustment) -> Float32\nget_lower(::Scale) -> Float32\nget_lower(::SpinButton) -> Float32\nget_lower(::Animation) -> Float64\n```\nGet the lower bound of the underlying range.\n\"\"\"\n\n@document get_margin_bottom \"\"\"\n```\nget_margin_bottom(::Widget) -> Float32\n```\nGet the bottom margin of the widget, in pixels.\n\"\"\"\n\n@document get_margin_end \"\"\"\n```\nget_margin_end(::Widget) -> Float32\n```\nGet the right margin of the widget, in pixels.\n\"\"\"\n\n@document get_margin_start \"\"\"\n```\nget_margin_start(::Widget) -> Float32\n```\nGet the left margin of the widget, in pixels.\n\"\"\"\n\n@document get_margin_top \"\"\"\n```\nget_margin_top(::Widget) -> Float32\n```\nGet the top margin of the widget, in pixels.\n\"\"\"\n\n@document get_maximum_size \"\"\"\n```\nget_maximum_size(::ClampFrame) -> Float32\n```\nGet the maximum width (or height, if vertical) the frame should constrain its child to, in pixels.\n\"\"\"\n\n@document get_max_n_columns \"\"\"\n```\nget_max_n_columns(grid_view::GridView) -> Signed\n```\nGet the maximum number of columns, (or rows if orientation is vertical), or `-1` if unlimited.\n\"\"\"\n\n@document get_max_value \"\"\"\n```\nget_max_value(::LevelBar) -> Float32\n```\nGet the upper bound of the underlying range.\n\"\"\"\n\n@document get_max_width_chars \"\"\"\n```\nget_max_width_chars(::Entry) -> Signed\nget_max_width_chars(::Label) -> Signed\n```\nGet the maximum number of characters for which the label should allocate horizontal space, or `-1` if unlimited.\n\"\"\"\n\n@document get_message \"\"\"\n```\nget_message(::AlertDialog) -> String\n```\nGet the current message, this is the title of the dialog.\n\"\"\"\n\n@document get_min_n_columns \"\"\"\n```\nget_min_n_columns(grid_view::GridView) -> Signed\n```\nGet the minimum number of columns, or `-1` if unlimited.\n\"\"\"\n\n@document get_min_value \"\"\"\n```\nget_min_value(::LevelBar) -> Float32\n```\nGet the lower bound of the underlying range.\n\"\"\"\n\n@document get_minimum_size \"\"\"\n```\nget_minimum_size(::Widget) -> Vector2f\n```\nGet the minimum possible size the widget would have to allocate in order for it to be fully visible.\n\"\"\"\n\n@document get_mode \"\"\"\n```\nget_mode(::LevelBar) -> LevelBarMode\n```\nGet whether the `LevelBar` should display its value as a continuous bar, or segmented.\n\"\"\"\n\n@document get_n_buttons \"\"\"\n```\nget_n_buttons(::AlertDialog) -> Int64\n```\nGet the number of buttons the dialog currently has.\n\"\"\"\n\n@document get_n_columns \"\"\"\n```\nget_n_columns(::ColumnView) -> Unsigned\n```\nGet the current number of columns.\n\"\"\"\n\n@document get_n_digits \"\"\"\n```\nget_n_digits(::SpinButton) -> Signed\n```\nGet the number of digits the spin button should display.\n\"\"\"\n\n@document get_n_items \"\"\"\n```\nget_n_items(::Box) -> Unsigned\nget_n_items(::FlowBox) -> Unsigned\nget_n_items(::ListView) -> Unsigned\nget_n_items(::GridView) -> Unsigned\n```\nGet the number of children.\n\"\"\"\n\n@document get_n_pages \"\"\"\n```\nget_n_pages(::Notebook) -> Unsigned \n```\nGet the number of pages.\n\"\"\"\n\n@document get_n_pixels \"\"\"\n```\nget_n_pixels(::Image) -> Unsigned\n```\nGet the number of pixels, equal to `width * height`.\n\"\"\"\n\n@document get_n_rows \"\"\"\n```\nget_n_rows(::ColumnView) -> Unsigned\n```\nGet the current number of rows.\n\"\"\"\n\n@document get_n_vertices \"\"\"\n```\nget_n_vertices(::Shape) -> Unsigned\n```\nGet the number of OpenGL vertices.\n\"\"\"\n\n@document get_name \"\"\"\n```\nget_name(::Icon) -> String\nget_name(::FileDescriptor) -> String \nget_name(::FileFilter) -> String\n```\nGet a cleartext identifier for the object.\n\"\"\"\n\n@document get_native_handle \"\"\"\n```\nget_native_handle(::TextureObject) -> Cuint\nget_native_handle(::Shape) -> Cuint\n```\nGet the native OpenGL handle of the texture- or vertex buffer.\n\"\"\"\n\n@document get_natural_size \"\"\"\n```\nget_natural_size(::Widget) -> Vector2f\n```\nGet the size the widget would prefer to display at, if given infinite space and no expansion.\n\"\"\"\n\n@document get_only_listens_to_button \"\"\"\n```\nget_only_listens_to_button(::SingleClickGesture) -> Bool\n```\nGet whether the event controller should not capture events send by a touch device.\n\"\"\"\n\n@document get_opacity \"\"\"\n```\nget_opacity(::Widget) -> Float32\n```\nGet the widget's current opacity, in `[0, 1]`.\n\"\"\"\n\n@document get_orientation \"\"\"\n```\nget_orientation(::Box) -> Orientation\nget_orientation(::FlowBox) -> Orientation\nget_orientation(::CenterBox) -> Orientation \nget_orientation(::ClampFrame) -> Orientation\nget_orientation(::LevelBar) -> Orientation \nget_orientation(::ListView) -> Orientation \nget_orientation(::GridView) -> Orientation \nget_orientation(::Grid) -> Orientation \nget_orientation(::Paned) -> Orientation \nget_orientation(::ProgressBar) -> Orientation \nget_orientation(::Scrollbar) -> Orientation \nget_orientation(::Separator) -> Orientation \nget_orientation(::SpinButton) -> Orientation\nget_orientation(::SpinButton) -> Orientation\n```\nGet whether the widget is oriented horizontally or vertically.\n\n---\n\n```\nget_orientation(::PanEventController) -> Orientation \n```\nGet along which axis the event controller should recognize pan gestures.\n\"\"\"\n\n@document get_parent \"\"\"\n```\nget_parent(self::FileDescriptor) -> FileDescriptor\n```\nGet the files parent folder. If `self` points to root or a location that does not exist, the result may be invalid.\n\"\"\"\n\n@document get_path \"\"\"\n```\nget_path(::FileDescriptor) -> String\n```\nGet the absolute path to the file location.\n\"\"\"\n\n@document get_path_relative_to \"\"\"\n```\nget_path_relative_to(self::FileDescriptor, other::FileDescriptor) -> String\n```\nGet the relative path from `self` to `other`.\n\"\"\"\n\n@document get_pixel \"\"\"\n```\nget_pixel(image::Image, x::Integer, y::Integer) -> RGBA\n```\nGet the color of the pixel at given position, 1-indexed.\n\"\"\"\n\n@document get_position \"\"\"\n```\nget_position(::Widget) -> Vector2f\n```\nGet the current position on screen, relative to the toplevel window's origin, in pixels.\n\n---\n\n```\nget_position(::Grid, ::Widget) -> Vector2i\n```\nGet row- and column-index of the widget, 1-indexed.\n\n---\n\n```\nget_position(::Paned) -> Int32\n```\nGet the offset of the draggable handle, in absolute widget-space coordinates.\n\"\"\"\n\n@document get_program_id \"\"\"\n```\nget_program_id(::Shader) -> Cuint\n```\nGet the native handle of the OpenGL shader program.\n\"\"\"\n\n@document get_propagate_natural_height \"\"\"\n```\nget_propagate_natural_height(::Viewport) -> Bool\n```\nGet whether the viewport should assume the natural height of its child.\n\"\"\"\n\n@document get_propagate_natural_width \"\"\"\n```\nget_propagate_natural_width(::Viewport) -> Bool\n```\nGet whether the viewport should assume the natural width of its child.\n\n\"\"\"\n\n@document get_propagation_phase \"\"\"\n```\nget_propagation_phase(::EventController) -> PropagationPhase\n```\nGet the phase at which the event controller will capture events, see [here](https://developer-old.gnome.org/gtk4/stable/event-propagation.html) for more information.\n\"\"\"\n\n@document get_quick_change_menu_enabled \"\"\"\n```\nget_quick_change_menu_enabled(::Notebook) -> Bool\n```\nGet whether the user can open a menu that lets them skip to any page of the notebook.\n\"\"\"\n\n@document get_ratio \"\"\"\n```\nget_ratio(::AspectFrame) -> Float32\n```\nGet width-to-height aspect ratio.\n\"\"\"\n\n@document get_relative_position \"\"\"\n```\nget_relative_position(::Popover) -> RelativePosition\nget_relative_position(::PopoverButton) -> RelativePosition\n```\nGet the position of the popover relative to the widget it is attached to.\n\"\"\"\n\n@document get_repeat_count \"\"\"\n```\nget_repeat_count(::Animation) -> Unsigned\n```\nGet the number of cycles the animation will perform, or `0` if the animation loops endlessly.\n\"\"\"\n\n@document get_is_revealed \"\"\"\n```\nget_is_revealed(::Revealer) -> Bool\nget_is_revealed(::ActionBar) -> Bool\n```\nGet whether the widget's child is currently visible.\n\"\"\"\n\n@document get_right_margin \"\"\"\n```\nget_right_margin(::TextView) -> Float32\n```\nGet distance between the right end of the text and the `TextView`'s frame.\n\"\"\"\n\n@document get_row_spacing \"\"\"\n```\nget_row_spacing(::Grid) -> Float32\nget_row_spacing(::FlowBox) -> Float32\n```\nGet the margin between two rows, in pixels.\n\"\"\"\n\n@document get_rows_homogeneous \"\"\"\n```\nget_rows_homogeneous(::Grid) -> Bool\n```\nGet whether all rows should allocate the same height.\n\"\"\"\n\n@document get_scale \"\"\"\n```\nget_scale(::ImageDisplay) -> Signed\n```\nGet scale factor, in `{1, 2, 3, ...}``.\n\"\"\"\n\n@document get_scale_delta \"\"\"\n```\nget_scale_delta(::PinchZoomEventController) -> Float32\n```\nGet the difference between the current scale of the pinch-zoom-gesture and the scale at the point the gesture started, in absolute widget-space coordinates.\n\"\"\"\n\n@document get_scale_factor \"\"\"\n```\nget_scale_factor(::Widget) -> Float32\n```\nRetrieves the internal scale factor that maps from window coordinates to the actual device pixels.\nOn traditional systems this is 1, on high density outputs, it can be a higher value (typically 2).\n\nQuoted from: https://docs.gtk.org/gtk4/method.Widget.get_scale_factor.html\n\"\"\"\n\n@document get_scale_mode \"\"\"\n```\nget_scale_mode(::TextureObject) -> ScaleMode\n```\nGet the OpenGL scale mode the texture uses.\n\"\"\"\n\n@document get_scope \"\"\"\n```\nget_scope(::ShortcutEventController) -> ShortcutScope\n```\nGet the scope in which the controller listens for shortcut events, see [here](https://docs.gtk.org/gtk4/method.ShortcutController.set_scope.html) for more information.\n\"\"\"\n\n@document get_scrollbar_placement \"\"\"\n```\nget_scrollbar_placement(::Viewport) -> CornerPlacement\n```\nGet the position of both scrollbars relative to the `Viewport`'s center.\n\"\"\"\n\n@document get_is_selectable \"\"\"\n```\nget_is_selectable(::Label) -> Bool\n```\nGet whether the user can select part of the label, as would be needed to copy its text.\n\"\"\"\n\n@document get_selected \"\"\"\n```\nget_selected(::DropDown) -> DropDownID\n```\nGet the ID of the currently selected item.\n\"\"\"\n\n@document get_selection \"\"\"\n```\nget_selection(::SelectionModel) -> Vector{Int64}\n```\nGet all currently selected items' indices, 1-based.\n\"\"\"\n\n@document get_selection_model \"\"\"\n```\nget_selection_model(::ListView) -> SelectionModel\nget_selection_model(::GridView) -> SelectionModel\nget_selection_model(::Stack) -> SelectionModel\nget_selection_model(::ColumnView) -> SelectionModel\n```\nGet the underlying selection model of the selectable widget.\n\"\"\"\n\n@document get_selection_mode \"\"\"\n```\nget_selection_mode(::SelectionModel) -> SelectionMode\n```\nGet the underlying selection mode of the model.\n\"\"\"\n\n@document get_shortcuts \"\"\"\n```\nget_shortcuts(action::Action) -> Vector{ShortcutTrigger}\n```\nGet all registered shortcuts for the action.\n\"\"\"\n\n@document get_should_draw_value \"\"\"\n```\nget_should_draw_value(::Scale) -> Bool\n```\nGet whether the value of the `Scale`'s `Adjustment` is drawn as a label next to the slider.\n\"\"\"\n\n@document get_should_interpolate_size \"\"\"\n```\nget_should_interpolate_size(::Stack) -> Bool\n```\nGet whether the stack should slowly transition its size when switching from one page to another.\n\"\"\"\n\n@document get_should_snap_to_ticks \"\"\"\n```\nget_should_snap_to_ticks(::SpinButton) -> Bool\n```\nGet whether when the user enters a value using the `SpinButton`'s text entry, that value should be clamped to the nearest tick.\n\"\"\"\n\n@document get_should_wrap \"\"\"\n```\nget_should_wrap(::SpinButton) -> Bool\n```\nGet whether the spin button should over- or underflow when reaching the upper or lower end of its range. This needs to be set to `true` in order for `SpinButton` to emit its signal `wrapped`.\n\"\"\"\n\n@document get_always_show_arrow \"\"\"\n```\nget_always_show_arrow(::DropDown) -> Bool\nget_always_show_arrow(::PopoverButton) -> Bool\n```\nGet whether an arrow should be drawn next to the label.\n\"\"\"\n\n@document get_show_column_separators \"\"\"\n```\nget_show_column_separators(::ColumnView) -> Bool\n```\nGet whether a separator should be drawn between two columns.\n\"\"\"\n\n@document get_show_row_separators \"\"\"\n```\nget_show_row_separators(::ColumnView) -> Bool\n```\nGet whether a separator should be drawn between two rows.\n\"\"\"\n\n@document get_show_separators \"\"\"\n```\nget_show_separators(::ListView) -> Bool\n```\nGet whether a separator should be drawn between two items.\n\"\"\"\n\n@document get_show_text \"\"\"\n```\nget_show_text(::ProgressBar) - Bool\n```\nGet whether a percentage or custom label should be displayed above the `ProgressBar`. User [`set_text!`](@ref) to choose the custom label.\n\"\"\"\n\n@document get_show_title_buttons \"\"\"\n```\nget_show_title_buttons(::HeaderBar) -> Bool\n```\nGet whether the \"close\", \"minimize\", and / or \"maximize\" button should be visible.\n\"\"\"\n\n@document get_single_click_activate \"\"\"\n```\nget_single_click_activate(::ListView) -> Bool\nget_single_click_activate(::GridView) -> Bool\nget_single_click_activate(::ColumnView) -> Bool\n```\nGet whether simply hovering over an item will select it.\n\"\"\"\n\n@document get_size \"\"\"\n```\nget_size(::TextureObject) -> Vector2i\nget_size(::Icon) -> Vector2i\nget_size(::Image) -> Vector2i\n```\nGet resolution of the underlying image.\n\n---\n\n```\nget_size(::Grid, ::Widget) -> Vector2i\n```\nGet the number of rows and columns.\n\n---\n\n```\nget_size(::Shape) -> Vector2f\n```\nGet width and height of the axis-aligned bounding box, in OpenGL coordinates.\n\"\"\"\n\n@document get_size_request \"\"\"\n```\nget_size_request(::Widget) -> Vector2f\n```\nGet the size request, where a `0` for either width or height indicates that no size request was made.\n\"\"\"\n\n@document get_spacing \"\"\"\n```\nget_spacing(::Box) -> Float32\n```\nGet spacing drawn between any two items, in pixels.\n\"\"\"\n\n@document get_start_child_resizable \"\"\"\n```\nget_start_child_resizable(::Paned) -> Bool\n```\nGet whether the start child should resize itself when the `Paned` is resized.\n\"\"\"\n\n@document get_start_child_shrinkable \"\"\"\n```\nget_start_child_shrinkable(::Paned) -> Bool\n```\nGet whether the start child can be resized such that its allocated area in the paned is less than its minimum size.\n\"\"\"\n\n@document get_start_position \"\"\"\n```\nget_start_position(controller::DragEventController) -> Vector2f\n```\nGet position at which the drag gesture was first recognized, in absolute widget-space coordinates.\n\"\"\"\n\n@document get_state \"\"\"\n```\nget_state(::CheckButton) -> CheckButtonState\n```\nGet current state of the check button.\n\n---\n\n```\nget_state(::Animation) -> AnimationState\n```\nGet current state of the animation.\n\"\"\"\n\n@document get_step_increment \"\"\"\n```\nget_step_increment(::Adjustment) -> Float32\nget_step_increment(::Scale) -> Float32\nget_step_increment(::SpinButton) -> Float32\n```\nGet minimum step increment of the underlying adjustment. \n\"\"\"\n\n@document get_string \"\"\"\n```\nget_string(f, clipboard::Clipboard, [::Data_t]) -> Cvoid\n```\nRegister a callback with the signature:\n\n```\n(::Clipboad, ::String, [::Data_t]) -> Cvoid\n```\n\nWhen a string is read from the clipboard, the callback will be invoked and the string will be provided as the \nsecond argument for the callback.\n\n## Example\n```julia\nclipboard = get_clipboard(window)\nif contains_string(clipboard)\n    get_string(clipboard) do x::Clipboard, string::String\n        # use string here\n    end\nend\n```\n\"\"\"\n\n@document get_surpress_debug \"\"\"\n```\nget_surpress_debug(::LogDomain) -> Bool\n```\nGet whether log messages of level \"DEBUG\" will be omitted from the console output.\n\"\"\"\n\n@document get_surpress_info \"\"\"\n```\nget_surpress_info(::LogDomain) -> Bool\n```\nGet whether log message of level \"INFO\" will be omitted from the console output.\n\"\"\"\n\n@document get_tab_position \"\"\"\n```\nget_tab_position(::Notebook) -> RelativePosition\n```\nGet position of the tab bar relative to the center of the notebook.\n\"\"\"\n\n@document get_tabs_reorderable \"\"\"\n```\nget_tabs_reorderable(::Notebook) -> Bool\n```\nGet whether the user can reorder tabs.\n\"\"\"\n\n@document get_tabs_visible \"\"\"\n```\nget_tabs_visible(::Notebook) -> Bool\n```\nGet whether the tab bar is visible.\n\"\"\"\n\n@document get_target_frame_duration \"\"\"\n```\nget_target_frame_duration(::FrameClock) -> Time\n```\nGet the intended duration of a frame. For example, if the monitor has a refresh rate of 60hz, the target frame duration is `1/60s`.\n\"\"\"\n\n@document get_text \"\"\"\n```\nget_text(::Entry) -> String\nget_text(::Label) -> String\nget_text(::TextView) -> String\n```\nGet the content of the underlying text buffer.\n\n---\n\n```\nget_text(::ProgressBar) -> String\n```\nGet text currently displayed by the `ProgressBar`, or `\"\"` if the percentage is displayed instead.\n\"\"\"\n\n@document get_text_visible \"\"\"\n```\nget_text_visible(::Entry) -> Bool\n```\nGet whether the text entry is in \"password mode\".\n\"\"\"\n\n@document get_timeout \"\"\"\n```\nget_timeout(::PopupMessage) -> Time \n```\nGet the duration after which the message should hide itself, or `0` for it to never hide on its own. Microsecond precision.\n\"\"\"\n\n@document get_time_since_last_frame \"\"\"\n```\nget_time_since_last_frame(::FrameClock) -> Time\n```\nGet the actual duration of the last rendered frame.\n\"\"\"\n\n@document get_timing_function \"\"\"\n```\nget_timing_function(::Animation) -> AnimationTimingFunction\n```\nGet the shape of the function used to interpolate the animation's underlying value over time.\n\"\"\"\n\n@document get_title \"\"\"\n```\nget_title(::Window) -> String\nget_title(::FileChooser) -> String\nget_title(::ColorChooser) -> String\n```\nGet the window title.\n\n---\n\n```\nget_title(::ColumnViewColumn) -> String \n```\nGet the title for this column, which uniquely identifies it.\n\n---\n\n```\nget_title!(::PopupMessage) -> String\n````\nGet the `PopupMessage`s text.\n\"\"\"\n\n@document get_tool_type \"\"\"\n```\nget_tool_type(::StylusEventController) -> ToolType\n```\nGet the currently set tool type of the stylus device, or `TOOL_TYPE_UNKNOWN` if tool types are not supported.\n\"\"\"\n\n@document get_top_left \"\"\"\n```\nget_top_left(::Shape) -> Vector2f\n```\nGet the position of the top left corner of the axis-aligned bounding box, in OpenGL coordinates.\n\"\"\"\n\n@document get_top_margin \"\"\"\n```\nget_top_margin(::TextView) -> Float32\n```\nGet distance between the top of the text and the `TextView`'s frame.\n\"\"\"\n\n@document get_touch_only \"\"\"\n```\nget_touch_only(::SingleClickGesture) -> Bool\n```\nGet whether the event controller should exclusively react to events from touch devices.\n\"\"\"\n\n@document get_top_level_widget \"\"\"\n```\nget_top_level_widget(::Widget) -> Widget\n```\nFunction that maps a non-native compound widget (subtyping `Widget`) to its top-level widget component. \n\nSee the manual section on compound widgets in the chapter on widgets for more information.\n\n## Example\n```julia\nstruct CompoundWidget <: Widget\n    box::Box\n    CompoundWidget() = new(hbox(Label(\"this is a compound widget\")))\nend\n\nMousetrap.get_top_level_widget(x::CompoundWidget) = x.box\n# after this definition, `CompoundWidget` can be used like any native Mousetrap widget\n\"\"\"\n\n@document get_transition_duration \"\"\"\n```\nget_transition_duration(::Stack) -> Time\nget_transition_duration(::Revealer) -> Time\n```\nGet the duration of the transition animation.\n\"\"\"\n\n@document get_transition_type \"\"\"\n```\nget_transition_type(::Stack) -> StackTransitionType\nget_transition_type(::Revealer) -> RevealerTransitionType\n```\nGet type of animation used for the transition animation.\n\"\"\"\n\n@document get_uniform_float \"\"\"\n```\nget_uniform_float(::RenderTask, name::String) -> Cfloat\n```\nGet a registered uniform `float`, or `0.0` if no such uniform was registered.\n\"\"\"\n\n@document get_uniform_hsva \"\"\"\n```\nget_uniform_hsva(task::RenderTask, name::String) -> HSVA\n```\nGet uniform `vec4`, or `HSVA(0, 0, 0, 0)` if no such uniform exists.\n\"\"\"\n\n@document get_uniform_int \"\"\"\n```\nget_uniform_int(::RenderTask, name::String) -> Cint\n```\nGet a registered uniform `int`, or `0` if no such uniform was registered.\n\"\"\"\n\n@document get_uniform_location \"\"\"\n```\nget_uniform_location(::Shader, name::String) -> Cuint\n```\nGet the OpenGL shader program uniform location for the given uniform name, or `-1` if no such uniform exists. Note that uniform names may be optimized away by the GLSL compiler if they go unused.\n\"\"\"\n\n@document get_uniform_rgba \"\"\"\n```\nget_uniform_rgba(task::RenderTask, name::String) -> RGBA\n```\nGet uniform `vec4`, or `RGBA(0, 0, 0, 0)` if no such uniform exists.\n\"\"\"\n\n@document get_uniform_transform \"\"\"\n```\nget_uniform_transform(task::RenderTask, name::String) -> GLTransform\n```\nGet uniform `mat4x4`, or the identity transform if no such uniform exists.\n\"\"\"\n\n@document get_uniform_uint \"\"\"\n```\nget_uniform_uint(::RenderTask, name::String) -> Cuint\n```\nGet uniform `uint`, or `0` if no such uniform exists.\n\"\"\"\n\n@document get_uniform_vec2 \"\"\"\n```\nget_uniform_vec2(task::RenderTask, name::String) -> Vector2f\n```\nGet uniform `vec2`, or `Vector2f(0, 0)` if no such uniform exists.\n\"\"\"\n\n@document get_uniform_vec3 \"\"\"\n```\nget_uniform_vec3(task::RenderTask, name::String) \n```\nGet uniform `vec3`, or `Vector3f(0, 0, 0)` if no such uniform exists.\n\"\"\"\n\n@document get_uniform_vec4 \"\"\"\n```\nget_uniform_vec4(task::RenderTask, name::String) \n```\nGet uniform `vec4`, or `Vector4f(0, 0, 0, 0)` if no such uniform exists.\n\"\"\"\n\n@document get_upper \"\"\"\n```\nget_upper(::Adjustment) -> Float32\nget_upper(::Scale) -> Float32\nget_upper(::SpinButton) -> Float32\nget_upper(::Animation) -> Float64\n```\nGet upper bound of the underlying range.\n\"\"\"\n\n@document get_uri \"\"\"\n```\nget_uri(::FileDescriptor) -> String\n```\nTransform the descriptor's path to URI format.\n\"\"\"\n\n@document get_use_markup \"\"\"\n```\nget_use_markup(::Label) -> Bool\n```\nSet whether the label should respect [pango markup syntax](https://docs.gtk.org/Pango/pango_markup.html), `true` by default.\n\"\"\"\n\n@document get_value \"\"\"\n```\nget_value(::Adjustment) -> Float32\nget_value(::SpinButton) -> Float32\nget_value(::Scale) -> Float32\nget_value(::LevelBar) -> Float32\nget_value(::Animation) -> Float64\n```\nGet current value of the underlying adjustment.\n\n---\n\n```\nget_value(file::KeyFile, ::GroupID, ::KeyID, ::Type{<:AbstractFloat})\nget_value(file::KeyFile, ::GroupID, ::KeyID, ::Type{Vector{T}}) where T <: AbstractFloat \nget_value(file::KeyFile, ::GroupID, ::KeyID, ::Type{<:Signed}) \nget_value(file::KeyFile, ::GroupID, ::KeyID, ::Type{Vector{T}}) where T <: Signed \nget_value(file::KeyFile, ::GroupID, ::KeyID, ::Type{<:Unsigned}) \nget_value(file::KeyFile, ::GroupID, ::KeyID, ::Type{Vector{T}}) where T <: Unsigned \nget_value(file::KeyFile, ::GroupID, ::KeyID, ::Type{Bool}) \nget_value(file::KeyFile, ::GroupID, ::KeyID, ::Type{Vector{Bool}}) \nget_value(file::KeyFile, ::GroupID, ::KeyID, ::Type{String}) \nget_value(file::KeyFile, ::GroupID, ::KeyID, ::Type{Vector{String}}) \nget_value(file::KeyFile, ::GroupID, ::KeyID, ::Type{RGBA}) \nget_value(file::KeyFile, ::GroupID, ::KeyID, ::Type{HSVA}) \nget_value(file::KeyFile, ::GroupID, ::KeyID, ::Type{Image}) \n```\nDeserialize a value from the keyfile, then return it as a the specfied type. Returns a default value if the key-value pair or group does not exist, or it cannot be converted to the given type.\n\"\"\"\n\n@document get_velocity \"\"\"\n```\nget_velocity(SwipeEventController) -> Vector2f\n```\nGet the swipe's current velocity, in absolute widget-space coordinates.\n\"\"\"\n\n@document get_vertex_color \"\"\"\n```\nget_vertex_color(::Shape, index::Integer) -> RGBA\n```\nGet color of the vertex at given index, or `RGBA(0, 0, 0, 0)` if `index` is out of bounds.\n\"\"\"\n\n@document get_vertex_color_location \"\"\"\n```\nget_vertex_color_location() -> Cuint\n```\nGet the native uniform location for the `_vertex_color` input value of all vertex shaders.\n\"\"\"\n\n@document get_vertex_position \"\"\"\n```\nget_vertex_position(::Shape, ::Integer) -> Vector3f\n```\nGet the position of vertex at given index, in 3D OpenGL coordinates.\n\"\"\"\n\n@document get_vertex_position_location \"\"\"\n```\nget_vertex_position_location() -> Cuint\n```\nGet the native uniform location for `_vertex_position` input value of all vertex shaders.\n\"\"\"\n\n@document get_vertex_shader_id \"\"\"\n```\nget_vertex_shader_id(::Shader) -> Cuint\n```\nGet the native OpenGL handle of vertex shader component of a shader program.\n\"\"\"\n\n@document get_vertex_texture_coordinate \"\"\"\n```\nget_vertex_texture_coordinate(::Shape, index::Integer) -> Vector2f\n```\nGet the texture coordinate of a vertex at given index, in relative texture-space coordinates.\n\"\"\"\n\n@document get_vertex_texture_coordinate_location \"\"\"\n```\nget_vertex_texture_coordinate_location() -> Cuint\n```\nGet the native uniform location for `_vertex_texture_coordinate` input value of all vertex shaders.\n\"\"\"\n\n@document get_vertical_adjustment \"\"\"\n```\nget_vertical_adjustment(::Viewport) -> Adjustment\n```\nGet the underlying adjustment of the vertical scrollbar.\n\"\"\"\n\n@document get_vertical_alignment \"\"\"\n```\nget_vertical_alignment(::Widget) -> Alignment\n```\nGet widget alignment along the y-axis.\n\"\"\"\n\n@document get_vertical_scrollbar_policy \"\"\"\n```\nget_vertical_scrollbar_policy(::Viewport) -> ScrollbarVisibilityPolicy\n```\nGet the policy governing how and if the vertical scrollbar is revealed / hidden.\n\"\"\"\n\n@document get_visible_child \"\"\"\n```\nget_visible_child(stack::Stack) -> StackID\n```\nGet the ID of currently selected child.\n\"\"\"\n\n@document get_was_modified \"\"\"\n```\nget_was_modified(::TextView) -> Bool\n```\nGet whether the \"was modified\" flag of a `TextView` was set to `true`. \n\"\"\"\n\n@document get_wrap_mode \"\"\"\n```\nget_wrap_mode(::Label) -> LabelWrapMode\n```\nGet the mode used to determine at which point in a line a linebreak will be inserted.\n\n---\n\n```\nget_wrap_mode(::TextureObject) -> TextureWrapMode\n```\nGet the OpenGL texture wrap mode.\n\"\"\"\n\n@document get_x_alignment \"\"\"\n```\nget_x_alignment(::Label) -> Float32\n```\nGet the horizontal alignment of the label's text.\n\"\"\"\n\n@document get_y_alignment \"\"\"\n```\nget_y_alignment(::Label) -> Float32\n```\nGet the vertical alignment of the label's text.\n\"\"\"\n\n@document goto_page! \"\"\"\n```\ngoto_page!(::Notebook, position::Integer) \n```\nJump to page at given index, or the last page if `position` is out of bounds.\n\"\"\"\n\n@document grab_focus! \"\"\"\n```\ngrab_focus!(::Widget) \n```\nAttempt to grab input focus. This may fail.\n\"\"\"\n\n@document has_action \"\"\"\n```\nhas_action(::Application, id::String) -> Bool\n```\nCheck whether an action with the given ID is registered.\n\"\"\"\n\n@document has_axis \"\"\"\n```\nhas_axis(::StylusEventController, ::DeviceAxis) -> Bool\n```\nCheck whether a stylus device supports the given axis.\n\"\"\"\n\n@document has_column_with_title \"\"\"\n```\nhas_column_with_title(::ColumnView, title::String) -> Bool\n```\nCheck whether the `ColumnVIew` has a column with the given title.\n\"\"\"\n\n@document has_group \"\"\"\n```\nhas_group(::KeyFile, group::GroupID) -> Bool\n```\nCheck if the `KeyFile` has a group with given ID.\n\"\"\"\n\n@document has_icon \"\"\"\n```\nhas_icon(::IconTheme, icon::Icon) -> Bool\nhas_icon(::IconTheme, id::String) -> Bool\n```\nCheck whether icon theme has an icon.\n\"\"\"\n\n@document has_key \"\"\"\n```\nhas_key(::KeyFile, group::GroupID, key::KeyID) -> Bool\n```\nCheck whether the key file has a group with the given ID and whether that group has a key with given ID.\n\"\"\"\n\n@document hbox \"\"\"\n```\nhbox(::Widget...) -> Box\n```\nConvenience function that wraps list of a widget in a horizontally oriented box. \n\"\"\"\n\n@document hide! \"\"\"\n```\nhide!(::Widget) \n```\nHide the widget, this means its allocated size will become `0` and all of its children will be hidden.\n\"\"\"\n\n@document hold! \"\"\"\n```\nhold!(::Application) \n```\nPrevent the application from closing. Use [`release!`](@ref) to undo this.\n\"\"\"\n\n@document hsva_to_rgba \"\"\"\n```\nhsva_to_rgba(hsva::HSVA) -> RGBA\n```\nConvert HSVA to RGBA.\n\"\"\"\n\n@document html_code_to_rgba \"\"\"\n```\nhtml_code_to_rgba(code::String) -> RGBA\n```\nRead an html color code of the form `#RRGGBB` or `#RRGGBBAA`, in hexadecimal.\n\"\"\"\n\n@document insert_at! \"\"\"\n```\ninsert_at!(::FlowBox, ::Widget, index::Integer) -> Cvoid\ninsert_at!(::ListView, ::Widget, index::Integer, [::ListViewIterator]) -> ListViewIterator\ninsert_at!(::GridView, inde::Integer, ::Widget) -> Cvoid\ninsert_at!(::Grid, ::Widget, row_i::Signed, column_i::Signed, [n_horizontal_cells:Unsigned = 1, n_vertical_cells::Unsigned = 1]) -> Cvoid\n```\nInsert a widget at the given position.\n\n---\n\n```\ninsert_at!(::DropDown, index::Integer, label_for_both::String) -> DropDownItemID\ninsert_at!(::DropDown, index::Integer, list_widget::Widget, label_widget::Widget) -> DropDownItemID\ninsert_at!(f, ::DropDown, index::Integer, list_widget::Widget, label_widget::Widget, [::Data_t]) -> DropDownItemID\ninsert_at!(f, ::DropDown, index::Integer, label_for_both::String, [::Data_t]) -> DropDownItemID\n```\nAdd an item to the `DropDown` at given index. When it is selected `label_widget` will appear as the \nchild of the `DropDown`, while `list_widget` will be used as the widget displayed when the `DropDown` menu is open.\n\n`f` is called when the corresponding item is selected. `f` is required to be invocable as a function with the signature\n```\n(::DropDown, [::Data_t]) -> Cvoid\n```\nReturns a unique ID identifying the inserted item.\n\nSee the manual section on `DropDown` in the chapter on widgets for more information.\n\n---\n\n```\ninsert_at!(::Notebook, index::Integer, child_widget::Widget, label_widget::Widget)\n```\nInsert a page at the given position, where `child_widget` is the widget used as the page, and `label_widget` is the \nwidget displayed in the tab bar.\n\"\"\"\n\n@document insert_after! \"\"\"\n```\ninsert_after!(::Box, to_append::Widget, after::Widget) \n```\nInsert `to_append` such that it comes right after `after`.\n\"\"\"\n\n\n@document insert_column_at! \"\"\"\n```\ninsert_column_at!(grid::Grid, column_i::Signed) \n```\nInsert an empty column after the given index (may be negative).\n\n---\n\n```\ninsert_column:at!(column_view::ColumnView, index::Integer, title::String) \n```\nInsert a column at the given index. Each row of the column will be empty.\n\"\"\"\n\n@document insert_next_to! \"\"\"\n```\ninsert_next_to!(::Grid, to_insert::Widget, already_in_grid::Widget, position::RelativePosition, [n_horizontal_cells:Unsigned = 1, n_vertical_cells::Unsigned = 1]) -> Cvoid\n```\nInsert widget into a grid such that it is now next to `already_in_grid`, where `position` describes the relative position of `already_in_grid` to `to_insert`.\n\"\"\"\n\n@document insert_row_at! \"\"\"\n```\ninsert_row_at!(grid::Grid, row_i::Signed) \n```\nInsert an empty row after the given index (may be negative).\n\n---\n\n```\ninsert_row_at!(::ColumnView, index::Integer, widgets::Widget...)\n```\nInsert several widgets as a row, inserting them into the corresponding column. If the number of widgets is\nlower than the number of columns, the left-over columns will contain an empty cell in that row.\n\"\"\"\n\n@document is_cancelled \"\"\"\n```\nis_cancelled(::FileMonitor) -> Bool\n```\nCheck whether the file monitor has been cancelled.\n\"\"\"\n\n@document is_file \"\"\"\n```\nis_file(::FileDescriptor) -> Bool\n```\nCheck whether the location on disk contains points to a valid file (not folder).\n\"\"\"\n\n@document is_folder \"\"\"\n```\nis_folder(::FileDescriptor) -> Bool\n```\nCheck whether the location on disk contains points to a valid folder (not file).\n\"\"\"\n\n@document get_is_local \"\"\"\n```\nget_is_local(::Clipboard) -> Bool\n```\nCheck whether the content of the clipboard was set from within the currently active Mousetrap application.\n\"\"\"\n\n@document is_symlink \"\"\"\n```\nis_symlink(::FileDescriptor) -> Bool\n```\nGet whether the location on disk is a valid symbolic link.\n\"\"\"\n\n@document is_valid_html_code \"\"\"\n```\nis_valid_html_code(code::String) -> Bool\n```\nCheck whether `code` is a string that can be converted to a color using [`html_code_to_rgba`](@ref).\n\"\"\"\n\n@document main \"\"\"\n```\nmain(f; application_id::ApplicationID) \n```\nRun `f`, which is required to be invocable as a function with signature\n```\n(::Application, [::Data_t]) -> Cvoid\n```\n\nThis function automatically creates an application with given ID and starts the main loop. If an error occurs\nduring `f`, the application safely exits.\n\n## Example\n\n```julia\nusing Mousetrap\nmain() do app::Application\n    window = Window(app)\n    present!(window)\nend\n```\n\"\"\"\n\n@document log_debug \"\"\"\n```\nlog_debug(::LogDomain, message::Sting)\n@log_debug(::LogDomain, message::Sting)\n```\nDisplay a log message with level `DEBUG`. Messages of\nthis level will only be displayed once `set_surpress_debug!`\nis set to `false` for this log domain.\n    \nIf [`set_log_file!`](@ref) was called before, the message will also be appended to that file.\n\"\"\"\n\n@document log_info \"\"\"\n```\nlog_info(::LogDomain, message::Sting)\n@log_info(::LogDomain, message::Sting)\n```\nDisplay a log message with level `INFO`. Messages of this level can be surpressed by setting [`set_surpress_info!`](@ref) for this log domain to `true`. \n\nIf [`set_log_file!`](@ref) was called before, the message will also be appended to that file.\n\"\"\"\n\n@document log_warning \"\"\"\n```\nlog_warning(::LogDomain, message::Sting)\n@log_warning(::LogDomain, message::Sting)\n```\nDisplay a log message with level `WARNING`. If [`set_log_file!`](@ref) was called before, the message will also be appended to that file.\n\"\"\"\n\n@document log_critical \"\"\"\n```\nlog_critical(::LogDomain, message::Sting)\n@log_critical(::LogDomain, message::Sting)\n```\nDisplay a log message with level `CRITICAL`. If [`set_log_file!`](@ref) was called before, the message will also be appended to that file.\n\"\"\"\n\n@document log_fatal \"\"\"\n```\nlog_fatal(::LogDomain, message::Sting)\n@log_fatal(::LogDomain, message::Sting)\n```\nDisplay a log message with level `FATAL`. Immediately after, runtime ends. If [`set_log_file!`](@ref) was called before, the message will also be appended to that file.\n\"\"\"\n\n@document make_current \"\"\"\n```\nmake_current(::RenderArea) \nmake_current(::GLArea)\n```\nBind the associated frame buffer as the one currently being rendered to. This is usually not necessary.\n\"\"\"\n\n@document mark_as_busy! \"\"\"\n```\nmark_as_busy!(::Application) \n```\nMark the application as busy, this will tell the OS that the application is currently processing something. The caller of this function is reponsible for calling [`unmark_as_busy!`](@ref) to undo this action.\n\"\"\"\n\n@document microseconds \"\"\"\n```\nmicroseconds(n::Number) -> Time\n```\nCreate time from number of microseconds.\n\"\"\"\n\n@document milliseconds \"\"\"\n```\nmilliseconds(n::Number) -> Time\n```\nCreate time from number of milliseconds.\n\"\"\"\n\n@document minutes \"\"\"\n```\nminutes(n::Number) -> Time\n```\nCreate time from number of minutes.\n\"\"\"\n\n@document mouse_button_01_pressed \"\"\"\n```\nmouse_button_01_pressed(::ModifierState) -> Bool\n```\nCheck whether the modifier state indicates that the left mouse button is currently pressed.\n\"\"\"\n\n@document mouse_button_02_pressed \"\"\"\n```\nmouse_button_02_pressed(::ModifierState) -> Bool\n```\nCheck whether the modifier state indicates that the right mouse button is currently pressed.\n\"\"\"\n\n@document move! \"\"\"\n```\nmove!(from::FileDescriptor, to::FileDescriptor, allow_overwrite::Bool ; [make_backup::Bool = false, follow_symlink::Bool = true]) -> Bool \n```\nMove a file to a different location. Returns `true` if the operation was successful.\n\"\"\"\n\n@document move_page_to! \"\"\"\n```\nmove_page_to!(::Notebook, current_index::Integer, new_index::Integer)\n```\nMove notebook page at position `current_index` to position `new_index`, 1-based. This will emit signal `page_reordered`.\n\"\"\"\n\n@document move_to_trash! \"\"\"\n```\nmove_to_trash!(file::FileDescriptor) ::Bool\n```\nSafely move the file to the operating system garbage bin. This operation can be undone by the user. Returns `true` if the operation was successful.\n\"\"\"\n\n@document nanoseconds \"\"\"\n```\nnanoseconds(n::Integer) -> Time\n```\nCreate time from number of nanoseconds.\n\"\"\"\n\n@document next_page! \"\"\"\n```\nnext_page!(::Notebook) \n```\nGo to the next page, if possible.\n\"\"\"\n\n@document on_accept! \"\"\"\n```\non_accept!(f, chooser::FileChooser, [::Data_t]) \n```\nRegister a callback to be called when the user clicks the \"accept\" button.\n\n`f` is required to be invocable as a function with signature\n```\n(::FileChooser, ::Vector{FileDescriptor}, [::Data_t]) -> Cvoid\n```\n\n## Example\n```julia\nfile_chooser = FileChooser(FILE_CHOOSER_ACTION_OPEN_FILE)\non_accept!(file_chooser) do x::FileChooser, files::Vector{FileDescriptor}\n   # use `files` here\nend\n````\n---\n\n```\non_accept!(f, chooser::ColorChooser, [::Data_t]) \n```\nRegister a callback to be called when the user makes a color selection. `f` is required to be invocable as a function with signature:\n```\n(::FileChooser, ::RGBA, [::Data_t]) -> Cvoid\n```\n\n## Example\n```julia\ncolor_chooser = ColorChooser()\non_accept!(color_chooser) do self::ColorChooser, color::RGBA\n   # use `color` here\nend\n```\n\"\"\"\n\n@document on_cancel! \"\"\"\n```\non_cancel!(f, chooser::FileChooser, [::Data_t]) \n```\nRegister a callback to be called when the user clicks the \"cancel\" button of the file chooser. \n`f` is required to be invocable as a function with signature\n```\n(::FileChooser, [::Data_t]) -> Cvoid\n```\n\n## Example\n```julia\nfile_chooser = FileChooser(FILE_CHOOSER_ACTION_OPEN_FILE)\non_cancel!(file_chooser) do x::FileChooser\n    println(\"file selection cancelled.\")\nend\n```\n\n---\n\n```\non_cancel!(f, chooser::ColorChooser, [::Data_t]) \n```\nRegister a callback to be called when the user cancels color selection or otherwise closes the dialog. `f` is required to be invocable as a function with signature:\n```\n(::FileChooser, [::Data_t]) -> Cvoid\n```\n\n## Example\n```julia\ncolor_chooser = ColorChooser()\non_cancel!(color_chooser) do self::ColorChooser\n    println(\"color selection cancelled\")\nend\n````\n\"\"\"\n\n@document on_done! \"\"\"\n```\non_done!(f, ::Animation, [::Data_t])\n```\nRegister a callback called when the animations state changes from `ANIMATION_STATE_PLAYING` to `ANIMATION_STATE_DONE`.\n\n`f` is required to be invocable as a function with signature \n```\n(::Animation, [::Data_t]) -> Cvoid\n```\n\n## Example\n```julia\nanimation = Animation(widget, seconds(1))\non_done!(animation) do self::Animation\n    println(\"done\")\nend\nplay!(animation)\n```\n\"\"\"\n\n@document on_file_changed! \"\"\"\n```\non_file_changed!(f, monitor::FileMonitor, [::Data_t]) \n```\nRegister a callback to be called when the monitored file is modified. `f` is required to be \ninvocable as a function with signature \n```\n(::FileMonitor, ::FileMonitorEvent, self::FileDescriptor, other::FileDescriptor) -> Cvoid\n```\nWhere `other` may not point to a valid file, depending on the event type.\n\n## Example\n```julia\nfile = FileDescriptor(\"path/to/file.jl\")\n@assert(exists(file))\nmonitor = create_monitor(file)\non_file_changed!(monitor) do x::FileMonitor, event_type::FileMonitorEvent, self::FileDescriptor, other::FileDescriptor\n    if event_type == FILE_MONITOR_EVENT_CHANGED\n        println(\"File at \" * get_path(self) * \" was modified.\")\n    end\nend\n```\n\"\"\"\n\n@document on_selection! \"\"\"\n```\non_selection!(f, ::AlertDialog, [::Data_t])\n```\nRegister a callback to be called when the user clicks one of the dialog's buttons or dismisses the dialog. `f` is required to be invocable as a function with signature\n```\n(::AlertDialog, button_index::Signed, [::Data_t]) -> Cvoid\n```\nWhere `button_index` is the index of the current button (1-based), or `0` if the dialog was dismissed.\n\n## Example\n```julia\nalert_dialog = AlertDialog([\"Yes\", \"No\"], \"Is this a dialog?\")\non_selection!(alert_dialog) do self::AlertDialog, button_index::Signed\n    if button_index == 0\n        println(\"User dismissed the dialog\")\n    else\n        println(\"User chose \\$(get_button_label(self, button_index))\")\n    end\nend\npresent!(alert_dialog)\n```\n\"\"\"\n\n@document on_tick! \"\"\"\n```\non_tick!(f, ::Animation, [::Data_t])\n```\nRegister a callback called every frame while the animation is active. `f` is required to be invocable as a function with signature\n```\n(::Animation, value::AbstractFloat, [::Data_t]) -> Cvoid\n```\nWhere `value` is the currently interpolated value.\n\n## Example\n```julia\nanimation = Animation(widget, seconds(1))\non_tick!(animation) do self::Animation, value::AbstractFloat\n    # use `value` here\nend\nplay!(animation)\n```\n\"\"\"\n\n@document open_file \"\"\"\n```\nopen_file(::FileDescriptor) -> Cvoid\n```\nAsynchronously launch the default application to open the file or folder. May present the users with a list of applications they can choose from.\n\"\"\"\n\n@document open_url \"\"\"\n```\nopen_url(uri::String) -> Cvoid\n```\nAsynchronously launch the default application to open the URI. This will usually be the user's web browser\n\"\"\"\n\n@document pause! \"\"\"\n```\npause!(::Animation)\n```\nIf the animation is playing, pause it, otherwise does nothing.\n\"\"\"\n\n@document play! \"\"\"\n```\nplay!(::Animation)\n```\nIf the animation is currently paused, resume playing, otherwise restart the animation from the beginning.\n\"\"\"\n\n@document popdown! \"\"\"\n```\npopdown!(::Popover) \npopdown!(::PopoverButton) \n```\nClose the popover window.\n\"\"\"\n\n@document popup! \"\"\"\n```\npopup!(::Popover) \npopup!(::PopoverButton) \n```\nPresent the popover window.\n\"\"\"\n\n@document present! \"\"\"\n```\npresent!(::Window) \npresent!(::Popover) \npresent!(::FileChooser) \npresent!(::ColorChooser)\npresent!(::AlertDialog)\n```\nShow the window to the user.\n\"\"\"\n\n@document previous_page! \"\"\"\n```\nprevious_page!(::Notebook) \n```\nGo to the previous page, if possible.\n\"\"\"\n\n@document pulse \"\"\"\n```\npulse(::ProgressBar) \n```\nTrigger an animation that signifies to the user that progress has been made. This does not increase the displayed ratio of the progress bar.\n\"\"\"\n\n@document push_back! \"\"\"\n```\npush_back!(::Box, ::Widget) -> Cvoid\npush_back!(::FlowBox, ::Widget) -> Cvoid\npush_back!(::ListView, ::Widget, [::ListViewIterator]) -> ListViewIterator \npush_back!(::GridView, ::Widget) -> Cvoid\npush_back!(::HeaderBar, ::Widget) -> Cvoid\npush_back!(::ActionBar, ::Widget) -> Cvoid\n```\nAdd a widget to the end of the container.\n\n---\n\n```\npush_back!(::DropDown, label_for_both::String) -> DropDownItemID\npush_back!(::DropDown, list_widget::Widget, label_widget::Widget) -> DropDownItemID\npush_back!(f, drop_down::DropDown, list_widget::Widget, label_widget::Widget, [::Data_t]) -> DropDownItemID\npush_back!(f, drop_down::DropDown, label_for_both::String, [::Data_t]) -> DropDownItemID\n```\nAdd an item to the end of the `DropDown`. When it is selected `label_widget` will appear as the \nchild of the `DropDown`, while `list_widget` will be used as the widget displayed when the `DropDown` menu is open.\n\n`f` is called when the corresponding item is selected. `f` is required to be invocable as a function with the signature\n```\n(::DropDown, [::Data_t]) -> Cvoid\n```\nReturns a unique ID identifying the inserted item.\n\nSee the manual section on `DropDown` in the chapter on widgets for more information.\n\n---\n\n```\npush_back!(::Notebook, inde::Integer, child_widget::Widget, label_widget::Widget) \n```\nInsert a page at the end of the notebook, where `child_widget` is the widget used as the page, and `label_widget` is the \nwidget displayed in the tab bar.\n\"\"\"\n\n@document push_back_column! \"\"\"\n```\npush_back_column!(::ColumnView, title::String) -> ColumnViewColumn\n```\nAdd a column to the end of the column view.\n\"\"\"\n\n@document push_back_row! \"\"\"\n```\npush_back_row!(column_view::ColumnView, widgets::Widget...) -> Cvoid\n```\nAdd widgets to the end of the rows, inserting them into the corresponding column. If the number of widgets is\nlower than the number of columns, the left-over columns will contain an empty cell in that row. \n\"\"\"\n\n@document push_front! \"\"\"\n```\npush_front!(::Box, ::Widget) -> Cvoid\npush_front!(::FlowBox, ::Widget) -> Cvoid\npush_front!(::ListView, ::Widget, [::ListViewIterator]) -> ListViewIterator \npush_front!(::GridView, ::Widget) -> Cvoid\npush_front!(::HeaderBar, ::Widget) -> Cvoid\npush_front!(::ActionBar, ::Widget) -> Cvoid\n```\nAdd a widget to the start of the container.\n\n---\n\n```\npush_front!(::DropDown, label_for_both::String) -> DropDownItemID\npush_front!(::DropDown, list_widget::Widget, label_widget::Widget) -> DropDownItemID\npush_front!(f, drop_down::DropDown, list_widget::Widget, label_widget::Widget, [::Data_t]) -> DropDownItemID\npush_front!(f, drop_down::DropDown, label_for_both::String, [::Data_t]) -> DropDownItemID\n```\nAdd an item to the start of the `DropDown`. When it is selected `label_widget` will appear as the \nchild of the `DropDown`, while `list_widget` will be used as the widget displayed when the `DropDown` menu is open.\n\n`f` is called when the corresponding item is selected. `f` is required to be invocable as a function with the signature\n```\n(::DropDown, [::Data_t]) -> Cvoid\n```\nReturns a unique ID identifying the inserted item.\n\nSee the manual section on `DropDown` in the chapter on widgets for more information.\n\n---\n\n```\npush_front!(::Notebook, inde::Integer, child_widget::Widget, label_widget::Widget) \n```\nInsert a page at the start of the notebook, where `child_widget` is the widget used as the page, and `label_widget` is the \nwidget displayed in the tab bar.\n\"\"\"\n\n@document push_front_column! \"\"\"\n```\npush_front_column!(column_view::ColumnView, title::String) -> ColumnViewColumn\n```\nAdd a column to the start of the column view.\n\"\"\"\n\n@document push_front_row! \"\"\"\n```\npush_front_row!(column_view::ColumnView, widgets::Widget...) -> Cvoid\n```\n\n!!! compat\n    This function was deprecated in v0.3.2, use [`insert_row_at!`](@ref) instead\n\nAdd widgets to the start of the rows, inserting them into the corresponding column. If the number of widgets is\nlower than the number of columns, the leftover columns will contain an empty cell in that row. \n\"\"\"\n\n@document query_info \"\"\"\n```\nquery_info(::FileDescriptor, attribute_id::String) -> String\n```\nAccess metadata info about a file. A list of attribute IDs can be found [here](https://gitlab.gnome.org/GNOME/glib/-/blob/main/gio/gfileinfo.h#L46).\nNote that there is no guarantee that a file will contain a value for any of these attributes.\n\"\"\"\n\n@document queue_render \"\"\"\n```\nqueue_render(::RenderArea) \nqueue_render(::GLArea)\n```\nRequest for the `RendeArea` to performa a render cycle and flush the current framebuffer to the screen. There is no guarantee that this \nwill happen immediately.\n\"\"\"\n\n@document quit! \"\"\"\n```\nquit!(::Application) \n```\nExit the application.\n\"\"\"\n\n@document radians \"\"\"\n```\nradians(::Number) -> Angle\n```\nConstruct angle from radians.\n\"\"\"\n\n@document read_symlink \"\"\"\n```\nread_symlink(self::FileDescriptor) -> FileDescriptor\n```\nIf the file location is a valid symlink, follow that symlink and return the resulting file.\n\"\"\"\n\n@document redo! \"\"\"\n```\nredo!(::TextView) \n```\nInvoke the \"redo!\" keybinding signal. If there is no action on the undo stack, this function does nothing.\n\"\"\"\n\n@document release! \"\"\"\n```\nrelease!(::Application) \n```\nRelease an application that is currently being prevented from exiting because [`hold!`](@ref) was called before.\n\"\"\"\n\n@document remove! \"\"\"\n```\nremove!(::Box, ::Widget) -> Cvoid\nremove!(::FlowBox, ::WIdget) -> Cvoid\nremove!(::ActionBar, ::Widget) -> Cvoid\nremove!(::ListView, index::Integer, [::ListViewIterator]) -> Cvoid \nremove!(::GridView, ::Widget) -> Cvoid\nremove!(::Grid, ::Widget) -> Cvoid\nremove!(::HeaderBar, ::Widget) -> Cvoid\nremove!(::DropDown, item_id::DropDownItemID) -> Cvoid\nremove!(::Notebook, position::Integer) -> Cvoid\n```\nRemove an item from the container.\n\"\"\"\n\n@document remove_action! \"\"\"\n```\nremove_action!(::Application, id::String) \n```\nUnregister an action from the application. Any connected widgets such as `Button` or `MenuModel` \nwill be disabled.\n\n---\n\n```\nremove_action!(::ShortcutEventController, ::Action) \n```\nRemove an action, such that the controller will not longer trigger it if any of its shortcuts are recognized.\n\"\"\"\n\n@document remove_button! \"\"\"\n```\nremove_button!(::AlertDialog, index::Signed) \n```\nRemove the button at given position (1-based, left-to-right), this means all buttons after it have their index shifted by 1. \n\"\"\"\n\n@document remove_center_child! \"\"\"\n```\nremove_center_child!(::CenterBox) \nremove_center_child!(::ActionBar) \n```\nRemove the middle child of the center box.\n\"\"\"\n\n@document remove_child! \"\"\"\n```\nremove_child!(::Fixed, child::Widget) \nremove_child!(::Window) \nremove_child!(::AspectFrame) \nremove_child!(::Button) \nremove_child!(::CheckButton) \nremove_child!(::ToggleButton) \nremove_child!(::Expander) \nremove_child!(::Frame) \nremove_child!(::Overlay) \nremove_child!(::PopupMessageOverlay)\nremove_child!(::Popover) \nremove_child!(::PopoverButton) \nremove_child!(::Stack, id::String) \nremove_child!(::Revealer) \nremove_child!(::Viewport) \nremove_child!(::TransformBin)\n```\nRemove the widget's singular child, such that it is now empty.\n\"\"\"\n\n@document remove_css_class! \"\"\"\n```\nremove_css_class!(::Widget, class::String)\n```\nUndo the effects of `add_css_class!`.\n\"\"\"\n\n@document remove_column! \"\"\"\n```\nremove_column!(::ColumnView, column::ColumnViewColumn) \n```\nRemove a column from the column view, this also frees all of its rows.\n\"\"\"\n\n@document remove_column_at! \"\"\"\n```\nremove_column_at!(::Grid, column_i::Signed)\n```\nRemove column at given index (1-based, may be negative). Any following columns will be shifted to the left.\n\"\"\"\n\n@document remove_controller! \"\"\"\n```\nremove_controller!(::Widget, controller::EventController) \n```\nDisconnect an event controller.\n\"\"\"\n\n@document remove_end_child! \"\"\"\n```\nremove_end_child!(::CenterBox) \nremove_end_child!(::Paned) \n```\nRemove the latter child of the widget.\n\"\"\"\n\n@document remove_extra_widget! \"\"\"\n```\nremove_extra_widget!(::AlertDialog)\n```\nRemove the widget set using `set_extra_widget!`.\n\"\"\"\n\n@document remove_label_widget! \"\"\"\n```\nremove_label_widget!(::Expander) \nremove_label_widget!(::Frame) \n```\nRemove the label widget of the widget, such that it now has no label widget.\n\"\"\"\n\n@document remove_marker! \"\"\"\n```\nremove_marker!(::LevelBar, name::String) \n```\nRemove a marker with the given label from the level bar.\n\"\"\"\n\n@document remove_overlay! \"\"\"\n```\nremove_overlay!(overlay::Overlay, overlayed::Widget) \n```\nRemove an overlaid widget added via [`add_overlay!`](@ref).\n\"\"\"\n\n@document remove_popover! \"\"\"\n```\nremove_popover!(::PopoverButton) \n```\nRemove the connected `Popover` or `PopoverMenu`.\n\"\"\"\n\n@document remove_primary_icon! \"\"\"\n```\nremove_primary_icon!(::Entry) \n```\nRemove the left icon of the entry.\n\"\"\"\n\n@document remove_row_at! \"\"\"\n```\nremove_row_at!(grid::Grid, row_i::Signed) \n```\nRemove a row at specified position (1-based).\n\"\"\"\n\n@document remove_secondary_icon! \"\"\"\n```\nremove_secondary_icon!(::Entry) \n```\nRemove the right icon of the entry.\n\"\"\"\n\n@document remove_start_child! \"\"\"\n```\nremove_start_child!(::Paned) \nremove_start_child!(::CenterBox) \n```\nRemove the start child such that the widget is now empty at that position\n\"\"\"\n\n@document remove_texture! \"\"\"\n```\nremove_texture!(::Shape) \n```\nMake it such that shape no longer has a texture, meaning it will be rendered as a solid color.\n\"\"\"\n\n@document remove_tick_callback! \"\"\"\n```\nremove_tick_callback!(::Widget) \n```\nRemove a registered tick callback. Usually, this should be done by returning `TICK_CALLBACK_RESULT_DISCONTINUE` from\nwithin the tick callback function.\n\"\"\"\n\n@document remove_title_widget! \"\"\"\n```\nremove_title_widget!(::HeaderBar)\n```\nRemove widget that is currently used as the title element, and instead use the default window title. To completely hide the title, use `set_title!(window, \"\")` on the associated window instance.\n\"\"\"\n\n@document remove_tooltip_widget! \"\"\"\n```\nremove_tooltip_widget!(::Widget) \n```\nRemove the tooltip widget. The widget will no longer display a tooltip.\n\"\"\"\n\n@document render \"\"\"\n```\nrender(::RenderTask) \nrender(shape::Shape, shader::Shader, transform::GLTransform) \n```\nDraw a shape to the currently bound framebuffer, forwarding the transform to the vertex shader and applying the\nfragment shader to all fragments of the shape.\n\nNote that calling this function is usually not necessary, instead, register a `RenderTask` with a `RenderArea` \nusing `add_render_task!`, after which the task will be automatically rendered every frame, unless a custom\nsignal handler was connected to `RenderArea`s signal `render`.\n\"\"\"\n\n@document render_render_tasks \"\"\"\n```\nrender_render_tasks(::RenderArea) \n```\nRender all registered render tasks. This is only necessary when a custom signal handler is connected to the areas\nsignal `render`.\n\"\"\"\n\n@document reset! \"\"\"\n```\nreset!(::Animation)\n```\nReset animation's state to idle.\n\n---\n\n```\nreset!(::GLTransform) \nreset!(::TransformBin)\n```\nOverride the transform such that it is now the identity transform.\n\"\"\"\n\n@document reset_style! \"\"\"\n```\nreset_style!(::Widget)\n```\nRemove all style classes from a widget.\n\"\"\"\n\n@document reset_text_to_value_function! \"\"\"\n```\nreset_text_to_value_function!(::SpinButton) \n```\nReset the function registered using `set_text_to_value_function!`, using the default handler instead.\n\"\"\"\n\n@document reset_value_to_text_function! \"\"\"\n```\nreset_value_to_text_function!(::SpinButton) \n```\nReset the function registered using `set_value_to_text_function!`, using the default handler instead.\n\"\"\"\n\n@document restart! \"\"\"\n```\nrestart!(clock::Clock) -> Time\n```\nRestart the clock and return the elapsed time since the last restart.\n\"\"\"\n\n@document rgba_to_hsva \"\"\"\n```\nrgba_to_hsva(rgba::RGBA) -> HSVA\n```\nConvert RGBA to HSVA.\n\"\"\"\n\n@document rgba_to_html_code \"\"\"\n```\nrgba_to_html_code(rgba::RGBA) -> String\n```\nConvert the color to an html-style hexadecimal string of the form `#RRGGBB`. The alpha component is ignored.\n\"\"\"\n\n@document rotate! \"\"\"\n```\nrotate!(::TransformBin, angle::Angle)\n```\nRotate child widget around its center.\n\n---\n\n```\nrotate!(::GLTransform, angle::Angle, [origin::Vector2f]) \nrotate!(::Shape, angle::Angle, [origin::Vector2f]) \n```\nRotate around a point, in OpenGL coordinates.\n\"\"\"\n\n@document run! \"\"\"\n```\nrun!(app::Application) -> Cint\n```\n\nStart the main loop, initializing the internal state and triggering `Application`s signal `activate`. Note that \nno part of Mousetrap should be used or initialized before this function is called.\n\nUsually, users are encouraged to use [`main`](@ref) instead, which does this automatically.\n\n## Example\n```julia\napp = Application(\"example.app\")\nconnect_signal_activate!(app) app::app\n    # all initialization should happen here\nend\nrun(app) # start the main loop\n```\n\"\"\"\n\n@document save_to_file \"\"\"\n```\nsave_to_file(::Image, path::String) -> Bool\n```\nSave the image to a file, the file format is automatically determined based on the extension of the given path.\n\nReturns `true` if the operation was successful.\n\n---\n\n```\nsave_to_file(::KeyFile, path::String) -> Bool\n```\nSerialize the key file to a string and save that string to a file. Usually, the extension for this file should be `.ini`.\n\nReturns `true` if the operation was successful\n\"\"\"\n\n@document scale! \"\"\"\n```\nscale!(::TransformBin, x_scale::Number, y_scale::Number)\nscale!(::GLTransform, x_scale::AbstractFloat, y_scale::AbstractFloat) \n```\nCombine the transform with a scale transform. To scale around a point, first `translate!` the transform to that point, \nthen apply `scale!`, then `translate!` the transform back to origin.\n\nUses relative scale, where `2` is twice as big, `0.5` is half as big, `1` is no change compared to the original size.\n\"\"\"\n\n@document seconds \"\"\"\n```\nseconds(n::Number) -> Time\n```\nCreate from number of seconds.\n\"\"\"\n\n@document select! \"\"\"\n```\nselect!(::SelectionModel, i::Integer, [unselect_others::Bool = true]) \n```\nSelect item at given position, this will emit signal `selection_changed`.\n\"\"\"\n\n@document select_all! \"\"\"\n```\nselect_all!(::SelectionModel) \n```\nSelect all items, triggering emission of signal `selection_changed`. This is only possible if the selection mode is `SELECTION_MODE_MULTIPLE`.\n\"\"\"\n\n@document self_is_focused \"\"\"\n```\nself_is_focused(::FocusEventController) -> Bool\n```\nCheck if the widget the controller was added to currently holds focus. \n\"\"\"\n\n@document self_or_child_is_focused \"\"\"\n```\nself_or_child_is_focused(::FocusEventController) -> Bool\n```\nCheck if the widget the controller was added to, or any of the widgets children currently hold focus.\n\"\"\"\n\n@document serialize \"\"\"\n```\nserialize(::RGBA) -> String\nserialize(::HSVA) -> String\n```\nConvert the object to its CSS representation.\n\"\"\"\n\n@document set_acceleration_rate! \"\"\"\n```\nset_acceleration_rate!(::SpinButton, factor::AbstractFloat) \n```\nSet the rate at which the `SpinButton`'s value accelerates when a button is held down, where `0.0` is the default rate, `0.1` is 10%, `1.0` is 100%, etc. May not be negative.\n\"\"\"\n\n@document set_accept_label! \"\"\"\n```\nset_accept_label!(::FileChooser, label::String) \n```\nSet the label used for the \"accept\" button of the file chooser.\n\"\"\"\n\n@document set_action! \"\"\"\n```\nset_action!(::Button, ::Action) \n```\nConnect an action to the button. When the button is clicked, the action is activated. When the action is disabled, the button is also disabled.\n\nNote that a button can have both an action and a signal handler connected. If this is the case and the button is clicked, both \nare triggered.\n\"\"\"\n\n@document set_active! \"\"\"\n```\nset_active!(::CheckButton, ::Bool)\n```\nIf `true`, set the check button state to [`CHECK_BUTTON_STATE_ACTIVE`](@ref), otherwise set to [`CHECK_BUTTON_STATE_INACTIVE`](@ref).\n\"\"\"\n\n@document set_alignment! \"\"\"\n```\nset_alignment!(::Widget, both::Alignment) \n```\nSet both the horizontal and vertical alignment of a widget at the same time.\n\"\"\"\n\n@document set_allow_only_numeric! \"\"\"\n```\nset_allow_only_numeric!(::SpinButton, ::Bool) \n```\nSet whether the spin button should only accept numeric text entry, as opposed to alphanumeric.\n\"\"\"\n\n@document set_always_show_arrow! \"\"\"\n```\nset_always_show_arrow!(::DropDown, ::Bool) \nset_always_show_arrow!(::PopoverButton, ::Bool) \n```\nSet whether an arrow should be drawn next to the label.\n\"\"\"\n\n@document set_application! \"\"\"\n```\nset_application!(window::Window, app::Application) \n```\nRegister the window with the application. This is usually done automatically.\n\"\"\"\n\n@document set_autohide! \"\"\"\n```\nset_autohide!(::Popover, ::Bool) \n```\nSet whether the popover should hide itself when the attached widget loses focus.\n\"\"\"\n\n@document set_auto_render! \"\"\"\n```\nset_auto_render(::GLArea, ::Bool)\n```\nSet whether the `render` signal should be emitted anytime the widget is drawn to the screen, `true` by default.\n\"\"\"\n\n@document set_button_action! \"\"\"\n```\nset_button_action!(::PopupMessage, ::Action)\n```\nConnect an action to the singular button of the message. Note that the messages button label has to be set to anything other than `\"\"` for the button to be visible.\n\"\"\"\n\n@document set_button_label! \"\"\"\n```\nset_button_label!(::AlertDialog, id::Integer, label::String)\n```\nReplace the label of the button with given ID, obtained when calling `add_button!`.\n\n---\n\n```\nset_button_label!(::PopupMessage, label::String)\n```\nIf `label` is not empty, add a singular button to the message with the given label. When that button is clicked, the `PopupMessage` will emit signal `button_clicked`.\n\"\"\"\n\n@document set_bottom_margin! \"\"\"\n```\nset_bottom_margin!(::TextView, margin::AbstractFloat) \n```\nSet margin between the bottom of the text and the `TextView`'s frame.\n\"\"\"\n\n@document set_can_respond_to_input! \"\"\"\n```\nset_can_respond_to_input!(::Widget, ::Bool) \n```\nSet whether the widget can receive input events. If set to `false`, the widget may appear \"greyed out\", signaling to the user that it is inactive.\n\"\"\"\n\n@document set_center_child! \"\"\"\n```\nset_center_child!(::CenterBox, ::Widget) \nset_center_child!(::ActionBar, ::Widget) \n```\nSet the middle child of the center box.\n\"\"\"\n\n@document set_centroid! \"\"\"\n```\nset_centroid!(::Shape, centroid::Vector2f) \n```\nMove the shape such that its centroid is now at given position, in OpenGL coordinates.\n\"\"\"\n\n@document set_child! \"\"\"\n```\nset_child!(::Window, child::Widget) \nset_child!(::AspectFrame, child::Widget) \nset_child!(::Button, child::Widget) \nset_child!(::CheckButton, child::Widget) \nset_child!(::ToggleButton, child::Widget) \nset_child!(::Viewport, child::Widget) \nset_child!(::Expander, child::Widget) \nset_child!(::Frame, child::Widget) \nset_child!(::Overlay, child::Widget) \nset_child!(::PopupMessageOverlay, child::Widget)\nset_child!(::Popover, child::Widget) \nset_child!(::PopoverButton, child::Widget) \nset_child!(::Revealer, child::Widget) \nset_child!(::TransformBin, child::Widget)\n```\nSet the widget's singular child.\n\"\"\"\n\n@document set_child_position! \"\"\"\n```\nset_child_position!(::Fixed, child::Widget, position::Vector2f) \n```\nSet fixed position of the child, in absolute widget-space coordinates.\n\"\"\"\n\n@document set_child_x_alignment! \"\"\"\n```\nset_child_x_alignment!(::AspectFrame, alignment::AbstractFloat) \n```\nSet horizontal alignment of the `AspectFrame`'s child. `0.5` by default.\n\"\"\"\n\n@document set_child_y_alignment! \"\"\"\n```\nset_child_y_alignment!(::AspectFrame, alignment::AbstractFloat) \n```\nSet vertical alignment of the `AspectFrame`'s child. `0.5` by default.\n\"\"\"\n\n@document set_color! \"\"\"\n```\nset_color!(::Shape, ::RGBA)\nset_color!(::Shape, ::HSVA)\n```\nSet the color of all vertices of the shape.\n\"\"\"\n\n@document set_column_spacing! \"\"\"\n```\nset_column_spacing!(::FlowBox, spacing::AbstractFloat)\nset_column_spacing!(::Grid, spacing::AbstractFloat) \n```\nSet spacing between two columns of the grid, in pixels.\n\"\"\"\n\n@document set_columns_homogeneous! \"\"\"\n```\nset_columns_homogeneous!(::Grid, ::Bool) \n```\nSet whether all columns of the grid should be the same width.\n\"\"\"\n\n@document set_comment_above! \"\"\"\n```\nset_comment_above!(::KeyFile, ::GroupID, comment::String) \nset_comment_above!(::KeyFile, ::GroupID, ::KeyID, comment::String) \n```\nSet the singular comment above a group or key-value pair in the file.\n\"\"\"\n\n@document set_current_blend_mode \"\"\"\n```\nset_current_blend_mode(::BlendMode; allow_alpha_blending::Bool = true) \n```\nEnable GPU-side blending and set the current OpenGL blend mode. If `allow_alpha_blending` is set to `false`, \nonly the RGB components of a fragment's color will participate in blending.\n\"\"\"\n\n@document set_current_theme! \"\"\"\n```\nset_current_theme!(::Application, ::Theme)\n```\nSwap the global theme used to determine the look and color of all widgets. This function is only available after the backend has been initialized.\n\"\"\"\n\n@document set_cursor! \"\"\"\n```\nset_cursor!(::Widget, cursor::CursorType) \n```\nSet which cursor shape should be used when the cursor is over the allocated area of the widget.\n\"\"\"\n\n@document set_cursor_from_image! \"\"\"\n```\nset_cursor_from_image!(::Widget, image::Image, [offset::Vector2i = Vector2i(0, 0)]) \n```\nSet which image should be displayed when the cursor is over the allocated area of the widget. `offset` \ndetermines the vertical and horizontal offset of the center of the image, relative to the cursor position, in pixels.\n\"\"\"\n\n@document set_cursor_visible! \"\"\"\n```\nset_cursor_visible!(::TextView, ::Bool) \n```\nSet whether the caret is visible.\n\"\"\"\n\n@document set_default_button! \"\"\"\n```\nset_default_button!(::AlertDialog, id::Integer)\n```\nMark a button as the default response using the id obtained when calling `add_button!`. This will change the buttons look and make it such that that button becomes the default widget of the dialogs window.\n\"\"\"\n\n@document set_default_widget! \"\"\"\n```\nset_default_widget!(window::Window, ::Widget) \n```\nDesignate a widget as the default widget. When the window is activated, for example by the user \npressing the enter key, the default widget is activated.\n\"\"\"\n\n@document set_delay_factor! \"\"\"\n```\nset_delay_factor!(::LongPressEventController, factor::AbstractFloat) \n```\nSet a factor that multiplies the default delay after which a longpress gesture is recognized.\n\"\"\"\n\n@document set_destroy_with_parent! \"\"\"\n```\nset_destroy_with_parent!(::Window, ::Bool) \n```\nSet whether the window should close and be destroyed when the top-level window is closed.\n\"\"\"\n\n@document set_detailed_description! \"\"\"\n```\nset_detailed_description(::AlertDialog, message::String)\n```\nSet the detailed message, this is the text shown below the dialog's title.\n\"\"\"\n\n@document set_duration! \"\"\"\n```\nset_duration!(::Animation, ::Time)\n```\nSet the duration of the animation, microseconds precision.\n\"\"\"\n\n@document set_editable! \"\"\"\n```\nset_editable!(::TextView, ::Bool) \n```\nSet whether the user can edit the text in the text view.\n\"\"\"\n\n@document set_ellipsize_mode! \"\"\"\n```\nset_ellipsize_mode!(::Label, mode::EllipsizeMode) \n```\nSet the ellipsize mode of a label, `ELLIPSIZE_MODE_NONE` by default.\n\"\"\"\n\n@document set_enable_rubberband_selection! \"\"\"\n```\nset_enable_rubberband_selection!(::ListView, ::Bool) \nset_enable_rubberband_selection!(::GridView, ::Bool) \nset_enable_rubberband_selection!(::ColumnView, ::Bool) \n```\nSet whether the user can select multiple children by holding down the mouse button and click-dragging. The selectable widgets\nselection mode has to be `SELECTION_MODE_MULTIPLE` in order for this to be possible.\n\"\"\"\n\n@document set_enabled! \"\"\"\n```\nset_enabled!(::Action, ::Bool) \n```\nSet whether the action is enabled. If set to `false`, all connected buttons and menu items \nwill be disabled as well.\n\"\"\"\n\n@document set_end_child! \"\"\"\n```\nset_end_child!(::CenterBox, child::Widget) \nset_end_child!(::Paned, child::Widget) \n```\nSet the latter child of the widget.\n\"\"\"\n\n@document set_end_child_resizable! \"\"\"\n```\nset_end_child_resizable!(::Paned, ::Bool) \n```\nSet whether the end child should resize when the `Paned` is resized.\n\"\"\"\n\n@document set_end_child_shrinkable! \"\"\"\n```\nset_end_child_shrinkable!(::Paned, ::Bool)\n```\nSet whether the user can resize the end child such that its allocated area inside the paned is smaller than the natural size of the child.\n\"\"\"\n\n@document set_expand! \"\"\"\n```\nset_expand!(::Widget, ::Bool) \n```\nSet whether the widget should expand along both the horizontal and vertical axis.\n\"\"\"\n\n@document set_is_expanded! \"\"\"\n```\nset_is_expanded(::Expander, ::Bool) \n```\nAutomatically expand or hide the `Expander`'s child.\n\"\"\"\n\n@document set_expand_horizontally! \"\"\"\n```\nset_expand_horizontally!(::Widget, ::Bool) \n```\nSet whether the widget should expand along the x-axis.\n\"\"\"\n\n@document set_expand_vertically! \"\"\"\n```\nset_expand_vertically!(::Widget, ::Bool) \n```\nSet whether the widget should expand along the y-axis.\n\"\"\"\n\n@document set_extra_widget! \"\"\"\n```\nset_extra_widget!(::AlertDialog, ::Widget)\n```\nInsert a widget into the dialog's content area, it will be displayed underneath the detailed message.\n\"\"\"\n\n@document set_file! \"\"\"\n```\nset_file!(::Clipboard, file::FileDescriptor) \n```\nOverride the content of the clipboard with a path to a file. Use [`get_string`](@ref) to retrieve it.\n\"\"\"\n\n@document set_file_chooser_action! \"\"\"\n```\nset_file_chooser_action!(::FileChooser, ::FileChooserAction)\n```\nOverride the current file chooser action type, this may not change the layout until the dialog is hidden, then shown again.\n\"\"\"\n\n@document set_fixed_width! \"\"\"\n```\nset_fixed_width!(::ColumnViewColumn, width::AbstractFloat)\n```\nSet the fixed width of the column, in pixels, or `-1` if unlimited.\n\"\"\"\n\n@document set_focus_on_click! \"\"\"\n```\nset_focus_on_click!(::Widget, ::Bool) \n```\nSet whether the widget should attempt to grap focus when it is clicked.\n\"\"\"\n\n@document set_focus_visible! \"\"\"\n```\nset_focus_visible!(::Window, ::Bool) \n```\nSet whether which widget currently holds input focus should be highlighted using a border.\n\"\"\"\n\n@document set_fraction! \"\"\"\n```\nset_fraction!(::ProgressBar, zero_to_one::AbstractFloat) \n```\nSet the currently displayed fraction of the `ProgressBar`, in `[0, 1]`.\n\"\"\"\n\n@document set_fullscreen! \"\"\"\n```\nset_fullscreen!(::Window) \n```\nRequest for the window manager to enter fullscreen mode. This may fail.\n\"\"\"\n\n@document set_function! \"\"\"\n```\nset_function!(f, action::Action, [::Data_t]) \n```\nRegister a callback that should be called when the action is activated.\n\n## Example\n```julia\naction = Action(\"example.action\")\nset_function!(action) do x::Action\n    println(get_id(x) * \" called\")\nend\n```\n\"\"\"\n\n@document set_has_base_arrow! \"\"\"\n```\nset_has_base_arrow!(::Popover, ::Bool) \n```\nSet whether the \"tail\" of the popover pointing to widget it is attached to should be visible.\n\"\"\"\n\n@document set_has_border! \"\"\"\n```\nset_has_border!(::Notebook, ::Bool) \n```\nSet whether a border should be drawn around the notebook's perimeter.\n\"\"\"\n\n@document set_has_close_button! \"\"\"\n```\nset_has_close_button!(::Window, ::Bool) \n```\nSet whether the \"x\" button is present.\n\"\"\"\n\n@document set_has_frame! \"\"\"\n```\nset_has_frame!(::Button, ::Bool) \nset_has_frame!(::Viewport, ::Bool) \nset_has_frame!(::Entry, ::Bool) \nset_has_frame!(::PopoverButton, ::Bool) \n```\nSet whether the widget's outline should be displayed. This does not impact the widget's interactability.\n\"\"\"\n\n@document set_has_origin! \"\"\"\n```\nset_has_origin!(::Scale, ::Bool)\n```\nSet whether the area of the slider between the start of the range and the current value should be filled with a color.\n\"\"\"\n\n@document set_has_wide_handle! \"\"\"\n```\nset_has_wide_handle!(::Paned, ::Bool) \n```\nSet whether the barrier in-between the `Paned`s two children is wide or thin, wide by default.\n\"\"\"\n\n@document set_header_menu! \"\"\"\n```\nset_header_menu!(::ColumnViewColumn, model::MenuModel) \n```\nAdd a menu model to be used as the column's header menu, which the user can access by \nclicking the column's title.\n\"\"\"\n\n@document set_hide_on_close! \"\"\"\n```\nset_hide_on_close!(::Window, ::Bool) \n```\nIf set to to `true`, the window will be hidden when it is closed, if set to `false`, the window \nis destroyed when closed. `false` by default.\n\nIf set to `true`, the caller of this function is responsible for deallocating the window by calling [`destroy!`](@ref).\n\"\"\"\n\n@document set_hide_on_overflow! \"\"\"\n```\nset_hide_on_overflow!(::Widget, ::Bool) \n```\nSet whether the entire widget should be hidden if its allocated area is smaller than its natural size. If `false`, the overflow part of the widget will be truncated.\n\"\"\"\n\n@document set_homogeneous! \"\"\"\n```\nset_homogeneous!(::Box, ::Bool) \n```\nSet whether the box allocates the same space for all of its children.\n\"\"\"\n\n@document set_horizontal_alignment! \"\"\"\n```\nset_horizontal_alignment!(::Widget, ::Alignment) \n```\nSet alignment along the x-axis.\n\"\"\"\n\n@document set_horizontal_scrollbar_policy! \"\"\"\n```\nset_horizontal_scrollbar_policy!(::Viewport, policy::ScrollbarVisibilityPolicy) \n```\nSet the policy governing when the horizontal scrollbar is revealed / hidden.\n\"\"\"\n\n@document set_icon! \"\"\"\n```\nset_icon!(::Button, ::Icon) \nset_icon!(::ToggleButton, ::Icon)\nset_icon!(::PopoverButton, ::Icon)\n```\nReplace the button's label with an icon.\n\"\"\"\n\n@document set_image! \"\"\"\n```\nset_image!(::Clipboard, image::Image) \n```\nOverride the clipboard's content with an image. Use [`get_image`](@ref) to retrieve it.\n\"\"\"\n\n@document set_initial_file! \"\"\"\n```\nset_initial_file!(::FileChooser, ::FileDescriptor)\n```\nFor `FILE_CHOOSER_ACTION_OPEN_FILE` or `FILE_CHOOSER_ACTION_OPEN_MULTIPLE_FILES`, choose the file that is selected when the dialog is first shown.\n\"\"\"\n\n@document set_initial_filter! \"\"\"\n```\nset_initial_filter!(::FileChooser, ::FileFilter)\n```\nSet currently selected filter. If the filter was not yet added with [`add_filter!`](@ref), it will \nstill be made the active filter, but the user will be unable to change the filter selection.\n\"\"\"\n\n@document set_initial_folder! \"\"\"\n```\nset_initial_folder(::FileChooser, ::FileDescriptor)\n```\nFor `FILE_CHOOSER_ACTION_SELECT_FOLDER` or `FILE_CHOOSER_ACTION_OPEN_MULTIPLE_FOLDERS`, choose the folder that is selected when the dialog is first shown.\n\"\"\"\n\n@document set_initial_name! \"\"\"\n```\nset_initial_name!(::FileChooser, ::String)\n```\nFor `FILE_CHOOSER_ACTION_SAVE`, set the name field that will be used to determine the saved file's name.\n\"\"\"\n\n@document set_inverted! \"\"\"\n```\nset_inverted!(::LevelBar, ::Bool) \n```\nSet whether the level bar should be mirrored along the horizontal or vertical axis, depending on orientation.\n\"\"\"\n\n@document set_is_active! \"\"\"\n```\nset_is_active!(::Switch, ::Bool) \nset_is_active!(::ToggleButton, ::Bool) \nset_is_active!(::CheckMark, ::Bool)\n```\nSet the internal state of the widget.\n\"\"\"\n\n@document set_is_circular! \"\"\"\n```\nset_is_circular!(::Button, ::Bool) \nset_is_circular!(::ToggleButton, ::Bool) \nset_is_circular!(::PopoverButton, ::Bool) \n```\nSet whether the button should be circular, as opposed to rectangular.\n\"\"\"\n\n@document set_is_decorated! \"\"\"\n```\nset_is_decorated!(::Window, ::Bool) \n```\nSet whether the header bar area of the window is visible.\n\"\"\"\n\n@document set_is_focusable! \"\"\"\n```\nset_is_focusable!(::Widget, ::Bool) \n```\nSet whether the widget can retrieve input focus. Most widgets that support interaction by default are already focusable.\n\"\"\"\n\n@document set_is_high_priority! \"\"\"\n```\nset_is_high_priority!(::PopupMessage, high_priority::Bool)\n```\nSet whether this message should be shown before any other non-high-priority messages already queue with the `PopupMessageOverlay`. `false` by default.\n\"\"\"\n\n@document set_is_horizontally_homogeneous! \"\"\"\n```\nset_is_horizontally_homogeneous!(::Stack, ::Bool) \n```\nSet whether the stack should allocate the same width for all of its pages.\n\"\"\"\n\n@document set_is_inverted! \"\"\"\n```\nset_is_inverted!(::ProgressBar, ::Bool) \n```\nSet whether the `ProgressBar` should be mirrored.\n\"\"\"\n\n@document set_is_modal! \"\"\"\n```\nset_is_modal!(::Window, ::Bool) \nset_is_modal!(::FileChooser, ::Bool) \nset_is_modal!(::ColorChooser, ::Bool) \nset_is_modal!(::AlertDialog, ::Bool) \n```\nSet whether all others windows should be paused while this window is active.\n\"\"\"\n\n@document set_is_resizable! \"\"\"\n```\nset_is_resizable!(::ColumnViewColumn, ::Bool) \n```\nSet whether the column can be resized. If set to `false`, the size set via [`set_fixed_width!`](@ref) will be used.\n\"\"\"\n\n@document set_is_reversed! \"\"\"\n```\nset_is_reversed!(::Animation, is_reversed::Bool)\n```\nIf set to `false`, the animation will interpolate its value from the lower to upper bound, or the other way around if `true`. `false` by default.\n\"\"\"\n\n@document set_is_scrollable! \"\"\"\n```\nset_is_scrollable!(::Notebook, ::Bool) \n```\nSet whether the user can scroll between pages using the mouse scroll wheel or touchscreen.\n\"\"\"\n\n@document set_is_spinning! \"\"\"\n```\nset_is_spinning!(::Spinner, ::Bool) \n```\nSet whether the spinner is currently playing its animation.\n\"\"\"\n\n@document set_is_vertically_homogeneous! \"\"\"\n```\nset_is_vertically_homogeneous!(::Stack, ::Bool) \n```\nSet whether all pages of the stack should allocate the same height.\n\"\"\"\n\n@document set_is_visible! \"\"\"\n```\nset_is_visible!(::Widget, ::Bool) \n```\nSet whether the widget is hidden.\n\n---\n\n```\nset_is_visible!(::Shape, ::Bool) \n```\nSet whether the shape and any associated render tasks should be rendered.\n\n---\n\n```\nset_is_visible!(::ColumnViewColumn, ::Bool) \n```\nTemporarily remove the column and all its rows from the column view.\n\"\"\"\n\n@document set_justify_mode! \"\"\"\n```\nset_justify_mode!(::Label, mode::JustifyMode) \nset_justify_mode!(::TextView, mode:JustifyMode) \n```\nSet the text justification mode.\n\"\"\"\n\n@document set_kinetic_scrolling_enabled! \"\"\"\n```\nset_kinetic_scrolling_enabled!(::Viewport, ::Bool) \nset_kinetic_scrolling_enabled!(::ScrollEventController, ::Bool)\n```\nSet whether scrolling should continue once the user stopped operating the mouse wheel or touchscreen, simulating \"inertia\".\n\"\"\"\n\n@document set_label_widget! \"\"\"\n```\nset_label_widget!(::Expander, label::Widget) \nset_label_widget!(::Frame, label::Widget) \n```\nChoose a widget as the label.\n\"\"\"\n\n@document set_label_x_alignment! \"\"\"\n```\nset_label_x_alignment!(::Frame, ::AbstractFloat) \n```\nSet horizontal alignment of the label widget (if present), in `[0, 1]`\n\"\"\"\n\n@document set_layout! \"\"\"\n```\nset_layout!(::HeaderBar, layout::String) \n```\nSet layout string of the header bar.\n\nThis is a list of button IDs. Valid IDs are limited to:\n\n+ `maximize`: Maximize Button\n+ `minimize`: Minimize Button\n+ `close`: Close Button\n\nAny object left of `:` will be placed left of the title, any after `:` will be place right of the title. Object are delimited by `,`.\n\n## Example\n```julia\nheader_bar = HeaderBar()\nset_layout!(header_bar, \"close:maximize,minimize\")\n\"\"\"\n\n@document set_left_margin! \"\"\"\n```\nset_left_margin!(::TextView, margin::AbstractFloat) \n```\nSet distance between the left end of the text and the `TextView`'s frame.\n\"\"\"\n\n@document set_log_file! \"\"\"\n```\nset_log_file!(path::String) -> Bool\n```\nSet file at `path` as the log file. Any logging will be pushed to the file as opposed to being printed to the console. The file\nwill be created if it does not exist. If it does exist, the file will be appended to, as opposed to being overwritten.\n\nReturns `true` if the file was successfuly opened.\n\"\"\"\n\n@document set_lower! \"\"\"\n```\nset_lower!(::Adjustment, ::Number) \nset_lower!(::Scale, ::Number) \nset_lower!(::SpinButton, ::Number) \nset_lower!(::Animation, ::Number)\n```\nSet lower bound of the underlying range.\n\"\"\"\n\n@document set_listens_for_shortcut_action! \"\"\"\n```\nset_listens_for_shortcut_action!(::Widget, ::Action)\n```\nAdds the action to the widget's internal `ShortcutEventController`. While the widget holds focus,\nif the user presses the action's associated shortcut, the action will trigger.\n\"\"\"\n\n@document set_margin_bottom! \"\"\"\n```\nset_margin_bottom!(::Widget, margin::AbstractFloat) \n```\nSet distance between the bottom of the text and the `TextView`'s frame, in pixels.\n\"\"\"\n\n@document set_margin_end! \"\"\"\n```\nset_margin_end!(::Widget, margin::AbstractFloat) \n```\nSet right margin of the widget, in pixels.\n\"\"\"\n\n@document set_margin_horizontal! \"\"\"\n```\nset_margin_horizontal!(::Widget, margin::AbstractFloat) \n```\nSet both the left and right margin of the widget, in pixels.\n\"\"\"\n\n@document set_margin! \"\"\"\n```\nset_margin!(::Widget, margin::AbstractFloat) \n```\nSet both the left, right, top, and bottom margin of the widget, in pixels.\n\"\"\"\n\n@document set_margin_start! \"\"\"\n```\nset_margin_start!(::Widget, margin::AbstractFloat) \n```\nSet left margin of the widget, in pixels.\n\"\"\"\n\n@document set_margin_top! \"\"\"\n```\nset_margin_top!(::Widget, margin::AbstractFloat) \n```\nSet top margin of the widget, in pixels.\n\"\"\"\n\n@document set_margin_vertical! \"\"\"\n```\nset_margin_vertical!(::Widget, margin::AbstractFloat) \n```\nSet both the top and bottom margin of the widget, in pixels.\n\"\"\"\n\n@document set_maximum_size! \"\"\"\n```\nset_maximum_size!(::ClampFrame, size::AbstractFloat)\n```\nSet the maximum width (or height, if vertical) the frame should constrain its child to, in pixels.\n\"\"\"\n\n@document set_max_n_columns! \"\"\"\n```\nset_max_n_columns!(grid_view::GridView, n::Integer) \n```\nLimit the number of columns (or rows, if horizontal) to given number, or `-1` if unlimited.\n\"\"\"\n\n@document set_max_value! \"\"\"\n```\nset_max_value!(::LevelBar, value::AbstractFloat) \n```\nSet upper limit of the underlying range.\n\"\"\"\n\n@document set_max_width_chars! \"\"\"\n```\nset_max_width_chars!(::Label, n::Integer) \nset_max_width_chars!(::Entry, n::Integer) \n```\nSet the number of characters that the widget should make space for, or `-1` if unlimited.\n\"\"\"\n\n@document set_maximized! \"\"\"\n```\nset_maximized!(::Window, ::Bool) \n```\nAttempt to maximize or unmaximize the window.\n\"\"\"\n\n@document set_message! \"\"\"\n```\nset_message!(::AlertDialog, ::String)\n```\nSet the main message of the dialog, this will be used as the dialogs title.\n\"\"\"\n\n@document set_minimized! \"\"\"\n```\nset_minimized!(::Window, ::Bool)\n```\nAttempt to (un-)minimize the window.\n\"\"\"\n\n@document set_min_n_columns! \"\"\"\n```\nset_min_n_columns!(grid_view::GridView, n::Integer) \n```\nLimit the minimum number of columns, or unlimited if `-1`.\n\"\"\"\n\n@document set_min_value! \"\"\"\n```\nset_min_value!(::LevelBar, value::AbstractFloat) \n```\nSet the lower bound of the underlying range.\n\"\"\"\n\n@document set_mode! \"\"\"\n```\nset_mode!(::LevelBar, mode::LevelBarMode) \n```\nSet whether the level bar should display its value continuous or segmented.\n\"\"\"\n\n@document set_n_digits! \"\"\"\n```\nset_n_digits!(::SpinButton, n::Integer) \n```\nSet number of digits after the decimal point, up to a maximum of `20`. This only affects the visuals of the `SpinButton`, the internal value of the \nunderlying adjustment is unaffected.\n\"\"\"\n\n@document set_only_listens_to_button! \"\"\"\n```\nset_only_listens_to_button!(::SingleClickGesture, button::ButtonID) \n```\nSet which mouse buttons the event controller should listen to, or `BUTTON_ID_ANY` to listen to all buttons.\n\"\"\"\n\n@document set_opacity! \"\"\"\n```\nset_opacity!(::Widget, opacity::AbstractFloat) \n```\nSet the opacity of the widget, in `[0, 1]`.\n\"\"\"\n\n@document set_orientation! \"\"\"\n```\nset_orientation!(::Box, ::Orientation) \nset_orientation!(::FlowBox, ::Orientation)\nset_orientation!(::CenterBox, ::Orientation) \nset_orientation!(::ClampFrame, ::Orientation)\nset_orientation!(::LevelBar, ::Orientation) \nset_orientation!(::Grid, ::Orientation) \nset_orientation!(::ProgressBar, ::Orientation) \nset_orientation!(::Scrollbar, ::Orientation) \nset_orientation!(::Separator, ::Orientation) \nset_orientation!(::ListView, ::Orientation) \nset_orientation!(::GridView, ::Orientation) \nset_orientation!(::Paned, ::Orientation) \nset_orientation!(::SpinButton, ::Orientation)\nset_orientation!(::Scale, ::Orientation)\n```\nSet orientation of the widget, this governs along which axis it aligns itself and its children.\n\n---\n\n```\nset_orientation!(::PanEventController) \n```\nSet along which axis the event controller should listen for pan gestures.\n\"\"\"\n\n@document set_pixel! \"\"\"\n```\nset_pixel!(image::Image, x::Integer, y::Integer, color::RGBA) \nset_pixel!(image::Image, x::Integer, y::Integer, color::HSVA) \n```\nOverride the color of a pixel, 1-based indexing.\n\"\"\"\n\n@document set_popover! \"\"\"\n```\nset_popover!(::PopoverButton, popover::Popover) \n```\nAttach a [`Popover`](@ref) to the popover button. This will detach any already attached `Popover` or `PopoverMenu`.\n\"\"\"\n\n@document set_popover_menu! \"\"\"\n```\nset_popover_menu!(popover_button::PopoverButton, popover_menu::PopoverMenu) \n```\nAttach a [`PopoverMenu`](@ref) to the popover button. This will detach any already attached `Popover` or `PopoverMenu`.\n\"\"\"\n\n@document set_position! \"\"\"\n```\nset_position!(::Paned, position::Integer) \n```\nSet position of the paned handle, relative to the `Paned`'s origin, in pixels.\n\"\"\"\n\n@document set_primary_icon! \"\"\"\n```\nset_primary_icon!(::Entry, icon::Icon) \n```\nSet left icon of the entry.\n\"\"\"\n\n@document set_propagate_natural_height! \"\"\"\n```\nset_propagate_natural_height!(::Viewport, ::Bool) \n```\nSet whether the viewport should assume the width of its child. This will usually hide the vertical scrollbar.\n\"\"\"\n\n@document set_propagate_natural_width! \"\"\"\n```\nset_propagate_natural_width!(::Viewport, ::Bool) \n```\nSet whether the viewport should assume the height of its child. This will usually hide the horizontal scrollbar.\n\"\"\"\n\n@document set_propagation_phase! \"\"\"\n```\nset_propagation_phase!(controller::EventController, ::PropagationPhase) \n```\nSet the phase at which the event controller will capture events, see [here](https://developer-old.gnome.org/gtk4/stable/event-propagation.html) for more information.\n\"\"\"\n\n@document set_quick_change_menu_enabled! \"\"\"\n```\nset_quick_change_menu_enabled!(::Notebook, ::Bool) \n```\nSet whether the user can click any of the tabs to open a menu that allows them to jump to a page.\n\"\"\"\n\n@document set_ratio! \"\"\"\n```\nset_ratio!(::AspectFrame, ratio::AbstractFloat) \n```\nSet width-to-height aspect ratio.\n\"\"\"\n\n@document set_relative_position! \"\"\"\n```\nset_relative_position!(::Popover, position::RelativePosition) \nset_relative_position!(::PopoverButton, position::RelativePosition) \n```\nSet position of the popover relative to the widget it is attached to.\n\"\"\"\n\n@document set_repeat_count! \"\"\"\n```\nset_repeat_count!(::Animation, ::Unsigned)\n```\nSet the number of cycles the animation should perform, or `0` if the animation loops endlessly. `1` by default.\n\"\"\"\n\n@document set_resource_path! \"\"\"\n```\nset_resource_path!(::IconTheme, path::String) \n```\nOverride all resource paths with the given path. The pointed-to folder has to adhere to the [Freedesktop icon theme specificatins](https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html).\n\"\"\"\n\n@document set_is_revealed! \"\"\"\n```\nset_is_revealed!(::Revealer, child_visible::Bool) \nset_is_revealed!(::ActionBar, widget_visible::Bool) \n```\nSet whether widget or its children should be visible. If the visibility changes, an animation is played.\n\"\"\"\n\n@document set_right_margin! \"\"\"\n```\nset_right_margin!(::TextView, margin::AbstractFloat) \n```\nSet margin between the right end of the text and the `TextView`'s frame.\n\"\"\"\n\n@document set_row_spacing! \"\"\"\n```\nset_row_spacing!(::Grid, spacing::Number) \nset_row_spacing!(::FLowBox, spacing::Number)\n```\nSet spacing between rows of the grid, in pixels.\n\"\"\"\n\n@document set_rows_homogeneous! \"\"\"\n```\nset_rows_homogeneous!(::Grid, ::Bool) \n```\nSet whether all rows should allocate the same height.\n\"\"\"\n\n@document set_scale! \"\"\"\n```\nset_scale!(::ImageDisplay, scale::Integer) \n```\nScale image by a constant factor, in `{1, 2, 3, ...}`.\n\"\"\"\n\n@document set_scale_mode! \"\"\"\n```\nset_scale_mode!(texture:TextureObject, ::TextureScaleMode) \n```\nSet the OpenGL scale mode the texture uses, `TEXTURE_SCALE_MODE_NEAREST` by default.\n\"\"\"\n\n@document set_scope! \"\"\"\n```\nset_scope!(::ShortcutEventController, scope::ShortcutScope) \n```\nSet the scope in which the controller listens for shortcut events, see [here](https://docs.gtk.org/gtk4/method.ShortcutController.set_scope.html) for more information.\n\"\"\"\n\n@document set_scrollbar_placement! \"\"\"\n```\nset_scrollbar_placement!(::Viewport, placement::CornerPlacement) \n```\nSet placement of both scrollbars relative to the viewport's center.\n\"\"\"\n\n@document set_secondary_icon! \"\"\"\n```\nset_secondary_icon!(entry::Entry, icon::Icon) \n```\nSet the right icon of the entry.\n\"\"\"\n\n@document set_is_selectable! \"\"\"\n```\nset_is_selectable!(::Label, ::Bool) \n```\nSet whether the user can select part of the label, as would be needed to copy its text. `false` by default.\n\"\"\"\n\n@document set_selected! \"\"\"\n```\nset_selected!(::DropDown, id::DropDownItemID)\n```\nMake the item identified by the given ID the currently selected item. This will invoke its associated callback.\n\"\"\"\n\n@document set_should_draw_value! \"\"\"\n```\nset_should_draw_value!(::Scale, ::Bool)\n```\nSet whether the current value of the scales internal adjustment should be drawn next to the knob.\n\"\"\"\n\n@document set_should_interpolate_size! \"\"\"\n```\nset_should_interpolate_size!(::Stack, ::Bool) \n```\nSet whether the stack should slowly transition its size when transitioning from one page to another.\n\"\"\"\n\n@document set_should_snap_to_ticks! \"\"\"\n```\nset_should_snap_to_ticks!(::SpinButton, ::Bool) \n```\nSet whether when the user enters a value using the spin buttons text entry, that value should be clamped to the nearest tick.\n\"\"\"\n\n@document set_should_wrap! \"\"\"\n```\nset_should_wrap!(::SpinButton, ::Bool) \n```\nSet whether the spin button should over- / underflow when reaching the upper or lower end of its range.\n\"\"\"\n\n@document set_show_column_separators \"\"\"\n```\nset_show_column_separators(::ColumnView, ::Bool) \n```\nSet whether separators should be drawn between each column.\n\"\"\"\n\n@document set_show_row_separators \"\"\"\n```\nset_show_row_separators(::ColumnView, ::Bool) \n```\nSet whether separators should be drawn between each row.\n\"\"\"\n\n@document set_show_separators! \"\"\"\n```\nset_show_separators!(::ListView, ::Bool) \n```\nSet whether separators should be drawn between two items.\n\"\"\"\n\n@document set_show_text! \"\"\"\n```\nset_show_text!(::ProgressBar, ::Bool)\n```\nSet whether a percentage or custom text should be displayed above the `ProgressBar`. Use [`set_text!`](@ref) to specify the custom text.\n\"\"\"\n\n@document set_show_title_buttons! \"\"\"\n```\nset_show_title_buttons!(::HeaderBar, ::Bool) \n```\nIf set to `false`, the \"close\", \"minimize\" and \"maximize\" buttons will be hidden.\n\"\"\"\n\n@document set_single_click_activate! \"\"\"\n```\nset_single_click_activate!(::ListView, ::Bool) \nset_single_click_activate!(::ColumnView, ::Bool) \nset_single_click_activate!(::GridView, ::Bool) \n```\nSet whether simply hovering about an item selects it.\n\"\"\"\n\n@document set_size_request! \"\"\"\n```\nset_size_request!(::Widget, size::Vector2f) \n```\nSet the size request, where a `0` for either width or height indicates that no size request was made.\n\"\"\"\n\n@document set_spacing! \"\"\"\n```\nset_spacing!(::Box, spacing::Number) \n```\nSet the space between two items, in pixels.\n\"\"\"\n\n@document set_start_child! \"\"\"\n```\nset_start_child!(::CenterBox, child::Widget) \nset_start_child!(::Paned, child::Widget) \n```\nSet first child of the container.\n\"\"\"\n\n@document set_start_child_resizable! \"\"\"\n```\nset_start_child_resizable!(::Paned, ::Bool) \n```\nSet whether the first child should resize when the `Paned` is resized.\n\"\"\"\n\n@document set_start_child_shrinkable! \"\"\"\n```\nset_start_child_shrinkable!(::Paned, ::Bool)\n```\nSet whether the user can resize the first child such that its allocated area inside the paned is smaller than the natural size of the child.\n\"\"\"\n\n@document set_startup_notification_identifier! \"\"\"\n```\nset_startup_notification_identifier!(::Window, id::String) \n```\nRegister an ID to be used to send a notification when the window is first shown, which will usually be `\"\\$id is ready.\". \n\nThere is no guarantee that the users operating system supports this feature.\n\"\"\"\n\n@document set_state! \"\"\"\n```\nset_state!(::CheckButton, state::CheckButtonState) \n```\nSet the state of the check button, this will change its visual element and emit the `toggled` signal.\n\"\"\"\n\n@document set_step_increment! \"\"\"\n```\nset_step_increment!(::Adjustment, value::Number) \nset_step_increment!(::Scale, value::Number) \nset_step_increment!(::SpinButton, value::Number) \n```\nSet minimum distance between two discrete values of the underlying range.\n\"\"\"\n\n@document set_string! \"\"\"\n```\nset_string!(::Clipboard, string::String) \n```\nOverride the clipboards contents with a string. Use [`get_string`](@ref) to retrieve it.\n\"\"\"\n\n@document set_surpress_debug! \"\"\"\n```\nset_surpress_debug!(domain::String, ::Bool) \n```\nIf set to `false`, log messages with log-level `DEBUG` will now be printed to console or the log file. `true` by default.\n\"\"\"\n\n@document set_surpress_info! \"\"\"\n```\nset_surpress_info!(domain::String, ::Bool) \n```\nIf set to `false`, log message with log-level `INFO` will now be printed to console or the log file. `true` by default.\n\"\"\"\n\n@document set_tab_position! \"\"\"\n```\nset_tab_position!(::Notebook, relative_position::RelativePosition) \n```\nSet position of the tab bar, relative to the notebook's center.\n\"\"\"\n\n@document set_tabs_reorderable! \"\"\"\n```\nset_tabs_reorderable!(::Notebook, ::Bool) \n```\nSet whether the user can reorder tabs by dragging them.\n\"\"\"\n\n@document set_tabs_visible! \"\"\"\n```\nset_tabs_visible!(::Notebook, ::Bool) \n```\nSet whether the tab bar should be displayed.\n\"\"\"\n\n@document set_text! \"\"\"\n```\nset_text!(::Entry, text::String) \nset_text!(::Label, text::String) \nset_text!(::TextView, text::String) \n```\nOverride the content of the internal text buffer.\n\n---\n\n```\nset_text!(::ProgressBar, text::String) \n```\nSet text that will be displayed instead of the percentage when [`set_show_text!`](@ref) was set to true.\n\"\"\"\n\n@document set_text_to_value_function! \"\"\"\n```\nset_text_to_value_function!(f, spin_button::SpinButton, [::Data_t]) \n```\nSet function that converts the text in the `SpinButton`'s text entry to a value. `f` is required to be invocable as a function with signature\n```\n(::SpinButton, text::String) -> Float32\n```\n## Example\n```\nspin_button = SpinButton(0, 1, 0.001)\nset_text_to_value_function!(spin_button) do self::SpinButton, text::String\n    value::Float32 = 0\n    # process string here\n    return value\nend\n```\n\"\"\"\n\n@document set_text_visible! \"\"\"\n```\nset_text_visible!(::Entry, ::Bool) \n```\nSet whether the entry should enter password mode.\n\"\"\"\n\n@document set_texture! \"\"\"\n```\nset_texture!(::Shape, texture::TextureObject) \n```\nSet the texture of the shape. It will be automatically bound when rendering.\n\"\"\"\n\n@document set_tick_callback! \"\"\"\n```\nset_tick_callback!(f, ::Widget, [::Data_t]) \n```\nRegister a function that will be invoked exactly once per frame while the widget is shown. `f` is required to be invocable as a function with signature\n```\n(::FrameClock, [::Data_t]) -> TickCallbackResult\n```\nThe callback will be removed when `f` returns `TICK_CALLBACK_RESULT_DISCONTINUE`.\n\n## Example\n```julia\nset_tick_callback!(widget) do clock::FrameClock\n    frame_duration = as_seconds(get_time_since_last_frame(clock))\n    println(\"It has been \\$frame_duration seconds since widget was last rendered\")\n    return TICK_CALLBACK_RESULT_CONTINUE\nend\n```\n\"\"\"\n\n@document set_timeout! \"\"\"\n```\nset_timeout!(::PopupMessage, duration::Time)\n```\nSet the duration after which the message should hide itself, or `0` for it to never hide on its own, forcing the user to close it. Microsecond precision, `0` by default.\n\n`PopupMessage` will not hide if it is clicked or currently holds input focus.\n\"\"\"\n\n@document set_timing_function! \"\"\"\n```\nset_timing_function!(::Animation, ::AnimationTimingFunction)\n```\nSets the shape of the function used to interpolate the animations underlying value over time.\n\"\"\"\n\n@document set_title! \"\"\"\n```\nset_title!(::Window, title::String) \nset_title!(::FileChooser, title::String)\nset_title!(::ColorChooser, title::String)\n```\nSet the window's title, which will be shown in its title bar.\n\n---\n\n```\nset_title!(::ColumnViewColumn, title::String) \n```\nSet the column's title, which will uniquely identify that column.\n\n---\n\n```\nset_title!(::PopupMessage, title::String)\n```\nSet the `PopupMessage`'s text, does not support Pango markup.\n\"\"\"\n\n@document set_title_widget! \"\"\"\n```\nset_title_widget!(header_bar::HeaderBar, ::Widget) \n```\nReplace the default header bar with a custom widget.\n\"\"\"\n\n@document set_tooltip_text! \"\"\"\n```\nset_tooltip_text!(::Widget, text::String) \n```\nCreate a simple text tooltip. It will be automatically shown when the user hovers above the widget's allocated area for a certain duration.\n\"\"\"\n\n@document set_tooltip_widget! \"\"\"\n```\nset_tooltip_widget!(::Widget, tooltip::Widget) \n```\nSet a custom widget as the widget's tooltip. It will be automatically shown when the user hovers above the widget's allocated area for a certain duration.\n\nThis widget should not be interactable.\n\"\"\"\n\n@document set_top_left! \"\"\"\n```\nset_top_left!(::Shape, top_left::Vector2f) \n```\nMove the shape such that the top left corner of its axis-aligned bounding box is at given position, in OpenGL coordinates.\n\"\"\"\n\n@document set_top_margin! \"\"\"\n```\nset_top_margin!(::TextView, margin::AbstractFloat) \n```\nSet margin between the top of the text and the `TextView`'s frame, in pixels.\n\"\"\"\n\n@document set_touch_only! \"\"\"\n```\nset_touch_only!(::SingleClickGesture) \n```\nMake it such that the event controller will exclusively listen to events emitted by touch devices.\n\"\"\"\n\n@document set_transient_for! \"\"\"\n```\nset_transient_for!(self::Window, other::Window) \n```\nMake it such that if `self` and `other` overlap, `self` will always be shown on top.\n\"\"\"\n\n@document set_transition_duration! \"\"\"\n```\nset_transition_duration!(::Stack, duration::Time) \nset_transition_duration!(::Revealer, duration::Time) \n```\nChoose the duration of the transition animation.\n\"\"\"\n\n@document set_transition_type! \"\"\"\n```\nset_transition_type!(::Stack, transition::StackTransitionType) \nset_transition_type!(::Revealer, type::RevealerTransitionType) \n```\nSet the type of transition animation. \n\"\"\"\n\n@document set_uniform_float! \"\"\"\n```\nset_uniform_float!(::Shader, name::String, ::Cfloat) \nset_uniform_float!(::RenderTask, name::String, ::Cfloat) \n```\nAssign a value to a uniform in the shader program, whose variable name exactly matches `name`. \n\nThis value will be a `float` in GLSL.\n\"\"\"\n\n@document set_uniform_hsva! \"\"\"\n```\nset_uniform_hsva!(task::RenderTask, name::String, ::RGBA) \n```\nAssign a value to a uniform in the shader program, whose variable name exactly matches `name`. \n\nThis value will be a `vec4` in GLSL.\n\"\"\"\n\n@document set_uniform_int! \"\"\"\n```\nset_uniform_int!(::Shader, name::String, ::Cint) \nset_uniform_int!(::RenderTask, name::String, ::Cint) \n```\nAssign a value to a uniform in the shader program, whose variable name exactly matches `name`. \n\nThis value will be a `int` in GLSL.\n\"\"\"\n\n@document set_uniform_rgba! \"\"\"\n```\nset_uniform_rgba!(::RenderTask, name::String, ::RGBA) \n```\nAssign a value to a uniform in the shader program, whose variable name exactly matches `name`. \n\nThis value will be a `vec4` in GLSL.\n\"\"\"\n\n@document set_uniform_transform! \"\"\"\n```\nset_uniform_transform!(::Shader, name::String, ::GLTransform) \nset_uniform_transform!(::RenderTask, name::String, ::GLTransform) \n```\nAssign a value to a uniform in the shader program, whose variable name exactly matches `name`. \n\nThis value will be a `mat4x4` in GLSL.\n\"\"\"\n\n@document set_uniform_uint! \"\"\"\n```\nset_uniform_uint!(::Shader, name::String, ::Cuint) \nset_uniform_uint!(::RenderTask, name::String, ::Cuint) \n```\nAssign a value to a uniform in the shader program, whose variable name exactly matches `name`. \n\nThis value will be a `uint` in GLSL.\n\"\"\"\n\n@document set_uniform_vec2! \"\"\"\n```\nset_uniform_vec2!(::Shader, name::String, ::Vector2f) \nset_uniform_vec2!(::RenderTask, name::String, ::Vector2f) \n```\nAssign a value to a uniform in the shader program, whose variable name exactly matches `name`. \n\nThis value will be a `vec2` in GLSL.\n\"\"\"\n\n@document set_uniform_vec3! \"\"\"\n```\nset_uniform_vec3!(::Shader, name::String, ::Vector3f) \nset_uniform_vec3!(::RenderTask, name::String, ::Vector3f) \n```\nAssign a value to a uniform in the shader program, whose variable name exactly matches `name`. \n\nThis value will be a `vec3` in GLSL.\n\"\"\"\n\n@document set_uniform_vec4! \"\"\"\n```\nset_uniform_vec4!(::Shader, name::String, ::Vector4f) \nset_uniform_vec4!(::RenderTask, name::String, ::Vector4f) \n```\nAssign a value to a uniform in the shader program, whose variable name exactly matches `name`. \n\nThis value will be a `vec4` in GLSL.\n\"\"\"\n\n@document set_upper! \"\"\"\n```\nset_upper!(::Adjustment, ::Number) \nset_upper!(::Scale, ::Number) \nset_upper!(::SpinButton, ::Number) \nset_upper!(::Animation, ::Number)\n```\nSet upper bound of the underlying range.\n\"\"\"\n\n@document set_use_markup! \"\"\"\n```\nset_use_markup!(::Label, ::Bool) \n```\nSet whether the label should respect [pango markup syntax](https://docs.gtk.org/Pango/pango_markup.html). `true` by default.\n\"\"\"\n\n@document set_value! \"\"\"\n```\nset_value!(::SpinButton, value::Number) \nset_value!(::Scale, value::Number) \nset_value!(adjustment::Adjustment, ::Number) \n```\nSet the current value of the underlying range, clamped to `[lower, upper]`.\n\n---\n\n```\nset_value!(file::KeyFile, ::GroupID, ::KeyID, ::AbstractFloat) \nset_value!(file::KeyFile, ::GroupID, ::KeyID, ::Vector{<:AbstractFloat}) \nset_value!(file::KeyFile, ::GroupID, ::KeyID, ::Signed) \nset_value!(file::KeyFile, ::GroupID, ::KeyID, ::Vector{<:Signed}) \nset_value!(file::KeyFile, ::GroupID, ::KeyID, ::Unsigned) \nset_value!(file::KeyFile, ::GroupID, ::KeyID, ::Vector{<:Unsigned}) \nset_value!(file::KeyFile, ::GroupID, ::KeyID, ::Bool) \nset_value!(file::KeyFile, ::GroupID, ::KeyID, ::Vector{Bool}) \nset_value!(file::KeyFile, ::GroupID, ::KeyID, ::String) \nset_value!(file::KeyFile, ::GroupID, ::KeyID, ::Vector{String}) \nset_value!(file::KeyFile, ::GroupID, ::KeyID, ::RGBA)\nset_value!(file::KeyFile, ::GroupID, ::KeyID, ::HSVA) \nset_value!(file::KeyFile, ::GroupID, ::KeyID, ::Image) \n```\nSerialize a value and save it to a key-value pair in given group. If the group or key does not yet exist, it is created.\n\"\"\"\n\n@document set_value_to_text_function! \"\"\"\n```\nset_value_to_text_function!(f, spin_button::SpinButton) \nset_value_to_text_function!(f, spin_button::SpinButton, data::Data_t) where Data_t \n```\nRegister a function that converts the value of the underlying range to the text displayed in the text-entry area of the spin button.\n`f` is required to be invocable as a function with signature\n```\n(::SpinButton, ::AbstractFloat) -> String\n```\n\n## Example\n```julia\nspin_button = SpinButton(0, 1, 0.001)\nset_value_to_text_function!(spin_button) do self::SpinButton, value::AbstractFloat\n    result = \"\"\n    # generate string here\n    return result\nend\n```\n\"\"\"\n\n@document set_vertex_color! \"\"\"\n```\nset_vertex_color!(::Shape, index::Integer, color::RGBA) \n```\nSet the color of a specific vertex.\n\"\"\"\n\n@document set_vertex_position! \"\"\"\n```\nset_vertex_position!(::Shape, index::Integer, position::Vector3f) \n```\nSet position of a specific vertex, in 3D OpenGL coordinates.\n\"\"\"\n\n@document set_vertex_texture_coordinate! \"\"\"\n```\nset_vertex_texture_coordinate!(::Shape, inde::Integer, coordinate::Vector2f) \n```\nSet texture coordinate of a specific vertex.\n\"\"\"\n\n@document set_vertical_alignment! \"\"\"\n```\nset_vertical_alignment!(::Widget, alignment::Alignment) \n```\nSet widget alignment along the y-axis.\n\"\"\"\n\n@document set_vertical_scrollbar_policy! \"\"\"\n```\nset_vertical_scrollbar_policy!(::Viewport, policy::ScrollbarVisibilityPolicy) \n```\nSet policy of vertical scrollbar, this determines when/if the scrollbar is shown.\n\"\"\"\n\n@document set_visible_child! \"\"\"\n```\nset_visible_child!(stack::Stack, id::StackID) \n```\nMake the current page of the stack the one identified by ID.\n\"\"\"\n\n@document set_was_modified! \"\"\"\n```\nset_was_modified!(::TextView, ::Bool) \n```\nOverride the flag indicating that a text buffer was modified.\n\"\"\"\n\n@document set_widget_at! \"\"\"\n```\nset_widget_at!(::ListView, index::Integer, ::Widget, [::ListViewIterator]) \nset_widget_at!(::ColumnView, ::ColumnViewColumn, row_i::Integer, ::Widget)\n```\nReplace the widget at given position.\n\"\"\"\n\n@document set_wrap_mode! \"\"\"\n```\nset_wrap_mode!(::Label, mode::LabelWrapMode) \n```\nSet wrap mode, this determines at which point of a line a linebreak will be inserted.\n\n---\n\n```\nset_wrap_mode!(::TextureObject, mode::TextureWrapMode) \n```\nSet OpenGL texture wrap mode, `TEXTURE_WRAP_MODE_REPEAT` by default.\n\"\"\"\n\n@document set_x_alignment! \"\"\"\n```\nset_x_alignment!(::Label, ::AbstractFloat) \n```\nSet horizontal alignment of the label, in `[0, 1]`.\n\"\"\"\n\n@document set_y_alignment! \"\"\"\n```\nset_y_alignment!(::Label, ::AbstractFloat) \n```\nSet vertical alignment of the label, in `[0, 1]`.\n\"\"\"\n\n@document shift_pressed \"\"\"\n```\nshift_pressed(modifier_state::ModifierState) -> Bool\n```\nCheck whether the modifier state indicates that the shift or caps key is currently down.\n\"\"\"\n\n@document should_shortcut_trigger_trigger \"\"\"\n```\nshould_shortcut_trigger_trigger(::KeyEventController, trigger::String) -> Bool\n```\nTest whether the currently active event should trigger the shortcut trigger. This function \nshould only be called from within signal `key_pressed` or `modifiers_changed`.\n\nThis is usually not necessary, use `ShortcutEventController` to listen for shortcuts instead.\n\"\"\"\n\n@document show! \"\"\"\n```\nshow!(::Widget) \n```\nReveal the widget if it is currently hidden. This will emit signal `show`.\n\"\"\"\n\n@document show_message! \"\"\"\n```\nshow_message!(::PopupMessageOverlay, ::PopupMessage)\n```\nQueue a message to be shown above the `PopupMessageOverlay`s child. Note that only one message can be shown at a time.\n\"\"\"\n\n@document show_in_file_explorer \"\"\"\n```\nshow_in_file_explorer(::FileDescriptor) -> Cvoid\n```\nAsynchronously open the users file explorer application to show the folder containing the given file.\n\"\"\"\n\n@document skew! \"\"\"\n```\nskew!(::TransformBin, x::Number, y::Number)\n```\nApply a skew transform to the child widget, where `x` skews along the horizontal, `y` along the vertical axis.\n\"\"\"\n\n@document start! \"\"\"\n```\nstart!(::Spinner) \n```\nStart the spinning animation if it is currently stopped.\n\"\"\"\n\n@document stop! \"\"\"\n```\nstop!(::Spinner) \n```\nStop the spinning animation if it is currently playing.\n\"\"\"\n\n@document to_gl_coordinates \"\"\"\n```\nto_gl_coordinates(area::RenderArea, absolute_widget_space_coordinates::Vector2f) -> Vector2f\n```\nConvert absolute widget-space coordinates to OpenGL coordinates. This will take into account the `RenderArea`s currently allocated size on screen.\n\"\"\"\n\n@document translate! \"\"\"\n```\ntranslate!(::TransformBin, offset::Vector2f)\n```\nMove the child widget by given offset, widget space coordinates, in pixels.\n\n```\ntranslate!(transform::GLTransform, offset::Vector2f) \n```\nTranslate the transform by the given offset, in OpenGL coordinates.\n\"\"\"\n\n@document unbind \"\"\"\n```\nunbind(::TextureObject) \n```\nUnbind a texture from rendering. This is usually done automatically.\n\"\"\"\n\n@document unbind_as_render_target \"\"\"\n```\nunbind_as_render_target(render_texture::RenderTexture) \n```\nMake it such that the render texture is no longer the current render target. This will restore the framebuffer that\nwas active when `bind_as_render_target` was called.\n\"\"\"\n\n@document undo! \"\"\"\n```\nundo!(::TextView) \n```\nTrigger an undo step.\n\"\"\"\n\n@document unmark_as_busy! \"\"\"\n```\nunmark_as_busy!(::Application) \n```\nUndo the effect of [`mark_as_busy!`](@ref).\n\"\"\"\n\n@document unparent! \"\"\"\n```\nunparent!(::Widget) \n```\nRemove the widget from its parent.\n\"\"\"\n\n@document unselect! \"\"\"\n```\nunselect!(model::SelectionModel, i::Integer) \n```\nMake item at given position no longer selected.\n\"\"\"\n\n@document unselect_all! \"\"\"\n```\nunselect_all!(::SelectionModel) \n```\nMake it such that no item is selected if the selection mode allows for that. \n\"\"\"\n\n@document vbox \"\"\"\n```\nvbox(::Widget...) -> Box\n```\nConvenience function that wraps list of widgets in a vertically oriented box.\n\"\"\"\n"
  },
  {
    "path": "src/docgen/signals.jl",
    "content": "#\n# Author: C. Cords (mail@clemens-cords.com)\n# GitHub: https://github.com/clemapfel/mousetrap.jl\n# Documentation: https://clemens-cords.com/mousetrap\n#\n# Copyright © 2023, Licensed under lGPL-3.0\n#\n\nvoid_signature = \"(::T, [::Data_t]) -> Nothing\"\nconst signal_descriptors = Dict([\n    :activate => (\n        void_signature, \n        \"Emitted when an activatable widget is activated by the user, usually by pressing the enter key.\"\n    ),\n    :activate_item => (\n        \"(::T, index::Integer, [::Data_t]) -> Nothing\",\n        \"Emitted when the user presses the enter key while one or more items are selected.\"\n    ),\n    :shutdown => (\n        void_signature, \n        \"Emitted when an `Application` is exiting the main loop and attempting to shut down.\"\n    ),\n    :update => (\n        void_signature, \n        \"Emitted exactly once per frame, when the widget associated with the `FrameClock` is about to be drawn.\"\n    ),\n    :paint => (\n        void_signature, \n        \"Emitted exactly once per frame, when the widget associated with the `FrameClock` is was drawn.\"\n    ),\n    :realize => (\n        void_signature, \n        \"Emitted when the widget is shown for the first time after being initialized.\"\n    ),\n    :unrealize => (\n        void_signature, \n        \"Emitted when the widget was hidden and no longer has an allocated area on screen.\"\n    ),\n    :destroy => (\n        void_signature, \n        \"Emitted when the widgets reference count reaches 0, it will be finalized and deleted permanently.\"\n    ),\n    :hide => (\n        void_signature, \n        \"Emitted when the widget is hidden, for example by calling `hide!`, being removed from its parent, or its parent being hidden.\"\n    ),\n    :show => (\n        void_signature, \n        \"Emitted when the widget is shown, for example by calling `show!` or its parent being shown.\"\n    ),\n    :map => (\n        void_signature, \n        \"Emitted when the widget has chosen its final size allocation and is assuming its size and position on screen.\"\n    ),\n    :unmap => (\n        void_signature, \n        \"Emitted when the widget loses its current size allocation, usually in the process of being hidden or destroyed.\"\n    ),\n    :close_request => (\n        \"(::T, [::Data_t]) -> WindowCloseRequestResult\", \n        \"Emitted when the window is requested to be closed, for example by the user pressing the close button. Depending on whether the return value of the signal handler is `WINDOW_CLOSE_REQUEST_RESULT_ALLOW_CLOSE` or `WINDOW_CLOSE_REQUEST_RESULT_PREVENT_CLOSE`, closing of the window will be permitted or prevented.\"\n    ),\n    :closed => (\n        void_signature, \n        \"Emitted when a popover-window is closed, for example by calling `popdown!`, or it losing focus while `set_autohide!` is set to `true`.\"\n    ),\n    :activate_default_widget => (\n        void_signature, \n        \"Emitted when the child widget designated via `set_default_widget!` was activated.\"\n    ),\n    :activate_focused_widget => (\n        void_signature, \n        \"Emitted when the child widget that currently holds input focus is activated.\"\n    ),\n    :clicked => (\n        void_signature, \n        \"Emitted when the user clicks the widget using a mouse or touchscreen.\"\n    ),\n    :toggled => (\n        void_signature, \n        \"Emitted when the widgets internal state changes from active to inactive, or inactive to active.\"\n    ),\n    :text_changed => (\n        void_signature, \n        \"Emitted when underlying text buffer is modified in any way.\"\n    ),\n    :selection_changed => (\n        \"(::T, position::Integer, n_items::Integer, [::Data_t]) -> Nothing\", \n        \"The number or position of one or more selected items has changed, where `position` is the item index that was modified, `n_items` is the number of items currently selected.\"\n    ),\n    :key_pressed => (\n        \"(::T, code::KeyCode, modifiers::ModifierState, [::Data_t]) -> Nothing\", \n        \"Emitted when the user presses a non-modifier key (which is currently not pressed), while the controller's associated widget holds input focus.\"\n    ),\n    :key_released => (\n        \"(::T, code::KeyCode, modifiers::ModifierState, [::Data_t]) -> Nothing\", \n        \"Emitted when the user releases a non-modifier key (which is currently pressed), while the controller's associated widget holds input focus.\"\n    ),\n    :modifiers_changed => (\n        \"(::T, modifiers::ModifierState, [::Data_t]) -> Nothing\", \n        \"Emitted when the user presses or releases a modifier key, while the controller's associated widget holds input focus.\"\n    ),\n    :drag_begin => (\n        \"(::T, start_x::AbstractFloat, start_y::AbstractFloat, [::Data_t]) -> Nothing\", \n        \"Emitted exactly once, on the first frame a drag gesture is recognized, where `start_y` and `start_x` are the initial position of the cursor, in pixels.\"\n    ),\n    :drag => (\n        \"(::T, x_offset::AbstractFloat, y_offset::AbstractFloat, [::Data_t]) -> Nothing\", \n        \"Emitted once per frame while a drag gesture is active, where `x_offset` and `y_offset` are the distance between the current position of the cursor, and the position at the start of the gesture, in pixels.\"\n    ),\n    :drag_end => (\n        \"(::T, x_offset::AbstractFloat, y_offset::AbstractFloat, [::Data_t]) -> Nothing\", \n        \"Emitted exactly once, when the drag gesture seizes to be active, where `x_offset` and `y_offset` are the distance between the current position of the cursor, and the position at the start of the gesture, in pixels.\"\n    ),\n    :click_pressed => (\n        \"(::T, n_presses::Integer, x::AbstractFloat, y::AbstractFloat, [::Data_t]) -> Nothing\", \n        \"User presses a mouse button (which is currently not pressed), while the controller's associated widget holds input focus. Where `n_presses` are the current number of clicks in the sequence, `x`, `y` are the current cursor position, in pixels.\"\n    ),\n    :click_released => (\n        \"(::T, n_presses::Integer, x::AbstractFloat, y::AbstractFloat, [::Data_t]) -> Nothing\", \n        \"User releases a mouse button (which is currently pressed), while the controller's associated widget holds input focus. Where `n_presses` are the current number of clicks in the sequence, `x`, `y` are the current cursor position, in pixels.\"\n    ),\n    :click_stopped => (\n        void_signature, \n        \"Emitted exactly once to signal that a series of clicks ended.\"\n    ),\n    :focus_gained => (\n        void_signature, \n        \"Emitted when the widget that is currently not focused becomes focus.\"\n    ),\n    :focus_lost => (\n        void_signature, \n        \"Emitted when the widget that is currently focused loses focus.\"\n    ),\n    :pressed => (\n        \"(::T, x::AbstractFloat, y::AbstractFloat, [::Data_t]) -> Nothing\", \n        \"Emitted once when a long-press gesture is recognized, where `x` and `y` are the current position of the cursor, in pixels.\"\n    ),\n    :press_cancelled => (\n        void_signature, \n        \"Emitted once when the user releases the button of a long-press gesture.\"\n    ),\n    :motion_enter => (\n        \"(::T, x::AbstractFloat, y::AbstractFloat, [::Data_t]) -> Nothing\", \n        \"Emitted once when the user's cursor first enters the allocated area of the widget on screen, where `x` and `y` are the current position of the cursor, in pixels.\"\n    ),\n    :motion => (\n        \"(::T, x::AbstractFloat, y::AbstractFloat, [::Data_t]) -> Nothing\", \n        \"Emitted once per frame, while the cursor is inside the allocated area of the widget, where `x` and `y` are the current cursor position, in pixels.\"\n    ),\n    :motion_leave => (\n        void_signature, \n        \"Emitted exactly once when the cursor leaves the allocated area of the widget.\"\n    ),\n    :scale_changed => (\n        \"(::T, scale::AbstractFloat, [::Data_t]) -> Nothing\", \n        \"Emitted any time the distance between two fingers of a pinch-zoom-gesture change, where `scale` is the factor of the current distance between the two fingers, compared to the distance at the start of the gesture.\"\n    ),\n    :rotation_changed => (\n        \"(::T, angle_absolute::AbstractFloat, angle_delta::AbstractFloat, [::Data_t]) -> Nothing\", \n        \"Emitted when the angle between the two fingers of a rotate-gesture changes, where `angle_delta` is the offset, `angle_absolute`the current angle, in radians.\"\n    ),\n    :scroll_begin => (\n        void_signature, \n        \"Emitted once when a scroll gesture is first recognized.\"\n    ),\n    :scroll => (\n        \"(::T, x_delta::AbstractFloat, y_delta::AbstractFloat, [::Data_t]) -> Nothing\", \n        \"Emitted once per frame while a scroll gesture is active, where `x_delta` and `y_delta` are the offset along the horizontal and vertical axis, in pixels.\"\n    ),\n    :scroll_end => (\n        void_signature, \n        \"Emitted to signal the end of a scroll gesture.\"\n    ),\n    :kinetic_scroll_decelerate => (\n        \"(::T, x_velocity::AbstractFloat, y_velocity::AbstractFloat, [::Data_t]) -> Nothing\", \n        \"Emitted once per frame while kinetic scrolling is active, see the manual on `ScrollEventController` for more information.\"\n    ),\n    :stylus_down => (\n        \"(::T, x::AbstractFloat, y::AbstractFloat, [::Data_t]) -> Nothing\", \n        \"Emitted once when the stylus makes contact with the touchpad, where `x` and `y` are the cursor position, in pixels.\"\n    ),\n    :stylus_up => (\n        \"(::T, x::AbstractFloat, y::AbstractFloat, [::Data_t]) -> Nothing\", \n        \"Emitted once when the stylus seizes to make contact with the touchpad, where `x` and `y` is the cursor position, in pixels.\"\n    ),\n    :proximity => (\n        \"(::T, x::AbstractFloat, y::AbstractFloat, [::Data_t]) -> Nothing\", \n        \"Emitted when the stylus enters or exits the proximity distance recognized by the touchpad. This will usually precede a `stylus_down` or `stylus_up` signal.\"\n    ),\n    :swipe => (\n        \"(::T, x_velocity::AbstractFloat, y_velocity::AbstractFloat, [::Data_t]) -> Nothing\", \n        \"Emitted once per frame while a swipe gesture is active, where `x_velocity` and `y_velocity` are the current velocity of the swipe, in pixels.\"\n    ),\n    :pan => (\n        \"(::T, ::PanDirection, offset::AbstractFloat, [::Data_t]) -> Nothing\", \n        \"Emitted once per frame while a pan gesture is active, where `offset` is the horizontal (or vertical) distance between the current position of the cursor, and the position at the start of the gesture, in pixels.\"\n    ),\n    :value_changed => (\n        void_signature, \n        \"Emitted when the `value` property of the `Adjustment` changes.\"\n    ),\n    :properties_changed => (\n        void_signature, \n        \"Emitted when any property of the `Adjustment` other than `value` changes.\"\n    ),\n    :wrapped => (\n        void_signature,\n        \"Emitted when a `SpinButton`s value wraps from the minimum to the maximum, or vice-versa, while `set_should_wrap!` was set to `true`.\" \n    ),\n    :scroll_child => (\n        \"(::T, scroll_type::ScrollType, is_horizontal::Bool, [::Data_t]) -> Nothing\", \n        \"Emitted any time a user triggers a scroll action that moves one or both of the `Viewport`s scroll bars, where `scroll_type` identifies the type of action that triggered the scrolling, while `is_horizontal` determines along which axis the scrolling took place.\"\n    ),\n    :render => (\n        \"(::T, gdk_gl_context::Ptr{Cvoid}, [::Data_t]) -> Bool\", \n        \"Emitted once per frame before the GL frame buffer is flushed to the screen. The `gdk_gl_context` argument is for internal use only and can be ignored.\"\n    ),\n    :resize => (\n        \"(::T, width::Integer, height::Integer, [::Data_t]) -> Nothing\", \n        \"Emitted whenever the allocated area of a `RenderArea` changes, where `width` and `height` are the new size, in pixels.\"\n    ),\n    :activated => (\n        void_signature, \n        \"Emitted when `activate!` is called, or the `Action` is otherwise activated.\"\n    ),\n    :revealed => (\n        void_signature, \n        \"Emitted once when a `Revealer`s child goes from hidden to shown (or shown to hidden) and the corresponding animation has finished playing.\"\n    ),\n    :switched => (\n        void_signature,\n        \"Emitted whenever the internal state of a `Switch` changes, for example by `set_is_active!`, or by the user operating the `Switch`.\"\n    ),\n    :page_reordered => (\n        \"(::T, page_index::Integer, [::Data_t]) -> Nothing\", \n        \"Emitted when a page changes position, where `page_index` is the new position of the page.\"\n    ),\n    :page_added => (\n        \"(::T, page_index::Integer, [::Data_t]) -> Nothing\", \n        \"Emitted when the total number of pages increases, where `page_index` is the position of the page that was inserted.\"\n    ),\n    :page_removed => (\n        \"(::T, page_index::Integer, [::Data_t]) -> Nothing\", \n        \"Emitted when a page is removed, where `page_index` is the old index of the page that was removed.\"\n    ),\n    :page_selection_changed => (\n        \"(::T, page_index::Integer, [::Data_t]) -> Nothing\", \n        \"Emitted when the currently active page changes by any means, where `page_index` is the index of the now visible page.\" \n    ),\n    :items_changed => (\n        \"(::T, position::Integer, n_removed::Integer, n_added::Integer, [::Data_t]) -> Nothing\", \n        \"Emitted when the number of menu items, or any of their properties, changes.\"\n    ),\n    :dismissed => (\n        \"(::T, [::Data_t]) -> Nothing\", \n        \"Emitted when the user clicks the close button of the `PopupMessage`\"\n    ),\n    :button_clicked => (\n        \"(::T, [::Data_t]) -> Nothing\", \n        \"Emitted when the user clicks the button of a `PopupMessage`. Note that the button is only visible if `set_button_label!` was set to anything but `\\\"\\\"`\"\n    )\n])\n\nimport Latexify\n\nmacro signal_table(T, signals...)\n\n    ids = [\"ID\"]\n    signatures = [\"Signature\"]\n    descriptions = [\"Description\"]\n\n    for signal_id in signals\n        push!(ids, string(signal_id))\n\n        signature = signal_descriptors[signal_id][1]\n        push!(signatures, replace(signature, \"T\" => string(T)))\n\n        push!(descriptions, signal_descriptors[signal_id][2])\n    end\n\n    return Latexify.mdtable(ids, signatures, descriptions; latex=false)\nend\n\nmacro type_signals(T)\n   return \"\"\"\n   ## Signals\n   (no unique signals)\n   \"\"\" \nend\n\nmacro type_signals(T, signals...)\n\n    out = String[\"## Signals\\n\"]\n    n_signals = length(signals)\n    for i in 1:n_signals\n\n        id = signals[i]\n        signature = replace(signal_descriptors[id][1], \"::T\" => \"::\" * string(T))\n        description = signal_descriptors[id][2]\n\n        push!(out, \"\"\"\n        > #### `$id`\n        > > ```\n        > > $signature\n        > > ```\n        > $description\n\n        \"\"\")\n    end\n    return join(out)\nend"
  },
  {
    "path": "src/docgen/types.jl",
    "content": "#\n# Author: C. Cords (mail@clemens-cords.com)\n# GitHub: https://github.com/clemapfel/mousetrap.jl\n# Documentation: https://clemens-cords.com/mousetrap\n#\n# Copyright © 2023, Licensed under lGPL-3.0\n#\n\n@document Action \"\"\"\n# Action <: SignalEmitter\n\nMemory-managed object that wraps a function. Each action has a unique ID and is registered with \nthe [`Application`](@ref). It can furthermore have any number of shortcut triggers.\n\nUse `set_function!` to register a callback to be called when the action is activated in any way.\nThis function is required to have the signature:\n```\n(::Action, [::Data_t]) -> Nothing\n```\nEach action can be enabled or disabled. If an action is disabled, all associated widgets, keybindings \nand menu items will be disabled automatically.\n\nSee the manual chapter on actions for more information.\n\n$(@type_constructors(\n    Action(::ActionID, ::Application),\n    Action(stateless_f, ::ActionID, ::Application)\n))\n\n$(@type_signals(Action, \nactivated\n))\n\n$(@type_fields())\n\n## Example\n```julia\naction = Action(\"example.action\", app)\nset_function!(action) do self::Action\n    println(get_id(self) * \" activated.\")\nend\nactivate!(action)\n```\n\"\"\"\n\n@document Adjustment \"\"\"\n# Adjustment <: SignalEmitter\n\nObject that represents a range of discrete values. Modifying the underlying adjustment \nof a widget will modify the widget, and vice-versa. \n\nSee [`get_adjustment`](@ref) to see which widgets are available to be controlled this way.\n\n$(@type_constructors(\n    Adjustment(value::Number, lower::Number, upper::Number, increment::Number)\n))\n\n$(@type_signals(Adjustment,\n    value_changed,\n    properties_changed\n))\n\n$(@type_fields())\n\"\"\"\n\n@document AlertDialog \"\"\"\n# AlertDialog <: SignalEmitter\n\nSimple dialog with a message, detailed description, space for a single widget, and one or more labeled buttons. \n\nUse `on_selection!` to register a function with the signature\n```\n(::AlertDialog, button_index::Integer, [::Data_t]) -> Nothing\n```\nwhich is called when the user makes a selection or closes the dialog.\n\n$(@type_constructors(\n    AlertDialog(message::String, [detailed_message::String])\n))\n\n$(@type_signals(AlertDialog, \n))\n\n$(@type_fields())\n\n## Example\n```julia\nalert_dialog = AlertDialog(\"Is this is a dialog?\")\nadd_button!(alert_dialog, \"yes\")\nadd_button!(alert_dialog, \"no\")\non_selection!(alert_dialog) do self::AlertDialog, button_index::Signed\n    if button_index == 1\n        println(\"User chose `Yes`\")\n    elseif button_index == 2\n        println(\"User chose `No`\")\n    elseif button_index == 0\n        println(\"User dismissed the dialog\")\n    end\nend\npresent!(alert_dialog)\n```\n\"\"\"\n\n@document ActionBar \"\"\"\n# ActionBar <: Widget\n\nHorizontal bar, has an area for widgets at the start and end, along with a singular centered widget, set via `set_center_widget!`.\n`ActionBar` can be hidden / shown using `set_is_revealed!`. It is always horizontal.\n\n$(@type_constructors(\n    ActionBar()\n))\n\n$(@type_signals(ActionBar, \n))\n\n$(@type_fields())\n\"\"\"\n\n@document Angle \"\"\"\n# Angle\n\nRepresents an angle in a unit-agnostic way.\n\nUse [`radians`](@ref) or [`degrees`](@ref) to construct\nan object of this type.\n\n[`as_radians`](@ref) and [`as_degrees`](@ref) allow for \nconverting an angle to the respective unit.\n\n$(@type_constructors())\n\n$(@type_fields())\n\"\"\"\n\n@document Application \"\"\"\n# Application <: SignalEmitter\n\nUsed to register an application with the user's OS.\n\nThe application's ID is required to contain at least one `.`, and it should be unique, meaning no\nother application on the user's operating system shares this ID.\n\nWhen all windows of an application are closed, or [`quit!`](@ref) is called,\nthe application exits. This can be prevented with [`hold!`](@ref), \nwhich has to be undone later by calling [`release!`](@ref). When exiting, the \napplication will emit signal `shutdown`, which should be used to safely free resources.\n\nWhen creating a new application, `allow_multiple_instances` governs whether resources are\nshared between two instances with the same ID. If set to `false`, the most recently \ncreated instance will be the primary instance. If set to `true` (default) the instance \ncreated **first** will be the primary instance. See [here](https://docs.gtk.org/gio/class.Application.html)\nfor more information.\n\n$(@type_constructors(\n    Application(::ApplicationID ; [allow_multiple_instances::Bool = true])\n))\n\n$(@type_signals(Application, \n    activate,\n    shutdown\n))\n\n$(@type_fields())\n\n## Example\n```julia\napp = Application(\"example.app\")\nconnect_signal_activate!(app) app::Application\n    # all initialization has to happen here\nend\nconnect_signal_shutdown!(app) app::Application\n    # safely free all resources\nend\nrun!(app)\n```\n\"\"\"\n\n@document ApplicationID \"\"\"\n# ApplicationID\n\nApplication name as a string, in reverse domain name syntax. For example, if the app's homepage is `Foo.julia.org`, an appropriate application ID would be `\"org.julia.foo\"`\n\"\"\"\n\n@document Animation \"\"\"\n# Animation <: SignalEmitter\n\nObject that provides a steady  timing function which is synched to a widget's render cycle. It can be used as the basis of implementing animations.\n\nUse `on_tick!` to register a callback with the signature\n```\n(::Animation, value::Float64, [::Data_t]) -> Nothing\n```\nWhich will be called once per frame while the widget is visible.\n\nBy default, the animation's `value` will be in [0, 1], this can be changed with `set_lower!` and `set_upper!`. The shape of the function \ninterpolating the value over time can be set using `set_timing_function!`.\n\n$(@type_constructors(\n    Animation(target::Widget, duration::Time)\n))\n\n$(@type_signals(Application, \n))\n\n$(@type_fields())\n\n## Example\n```julia\n# animate a gradual fade-out\nto_animate = Button(Label(\"Click Me\"))\n\nanimation = Animation(to_animate, seconds(1))\n\non_tick!(animation, to_animate) do self::Animation, value::AbstractFloat, target::Widget\n    # value will be in [0, 1]\n    set_opacity!(target, 1 - value)\nend\n\non_done!(animation, to_animate) do self::Animation, target::Widget\n    set_is_visible!(target, false)\nend\n\n# start animation when button is clicked\nconnect_signal_clicked!(to_animate, animation) do self::Button, animation::Animation\n    play!(animation)\nend\n\nset_child!(window, to_animate)\n```\n\"\"\"\n\n@document AspectFrame \"\"\"\n# AspectFrame <: Widget\n\nContainer widget with a single child, makes sure that \nthe size of its child will always be at the specified width-to-height ratio.\n\n$(@type_constructors(\n    AspectFrame(width_to_height::AbstractFloat; [child_x_alignment::AbstractFloat, child_y_alignment::AbstractFloat]),\n    AspectFrame(Width_to_height::AbstractFloat, child::Widget)\n))\n\n$(@type_signals(AspectFrame, \n))\n\n$(@type_fields())\n\"\"\"\n\n@document AxisAlignedRectangle \"\"\"\n# AxisAlignedRectangle\n\nAxis-aligned bounding box. Defined by its top-left \ncorner and size.\n\n$(@type_constructors(\n    AxisAlignedRectangle(top_left::Vector2f, size::Vector2f)\n))\n\n$(@type_fields(\n    top_left::Vectorf,\n    size::Vector2f\n))\n\n\"\"\"\n\n@document Box \"\"\"\n# Box <: Widget\n\nWidget that aligns its children in a row or column, depending on orientation.\n\n$(@type_constructors(\n    Box(::Orientation)\n))\n\n$(@type_signals(Box, \n))\n\n$(@type_fields())\n\n## Example\n```julia\nbox = Box(ORIENTATION_HORIZONTAL)\npush_back!(box, Label(\"01\"))\npush_back!(box, Button())\npush_back!(box, Label(\"03\"))\n\n# equivalent to\nbox = hbox(Label(\"01\"), Button(), Label(\"02\"))\n```\n\"\"\"\n\n@document Button \"\"\"\n# Button <: Widget\n\nButton with a label. Connect to signal `clicked` or specify an action via [`set_action!`](@ref) to react to the user clicking the button.\n\n$(@type_constructors(\n    Button(),\n    Button(label::Widget),\n    Button(::Icon)\n))\n\n$(@type_signals(Button, \n    clicked\n))\n\n$(@type_fields())\n\n## Example\n```julia\nbutton = Button()\nset_child!(button, Label(\"Click Me\"))\nconnect_signal_clicked!(button) do x::Button\n    println(\"clicked!\")\nend\nset_child!(window, button)\n```\n\"\"\"\n\n@document CenterBox \"\"\"\n# CenterBox <: Widget\n\nWidget that aligns exactly 3 widgets in a row (or column),\nprioritizing keeping the middle widget centered at all\ntimes.\n\n$(@type_constructors(\n    CenterBox(::Orientation),\n    CenterBox(::Orientation, left::Widget, center::Widget, right::Widget)\n))\n\n$(@type_signals(CenterBox, \n))\n\n$(@type_fields())\n\n## Example\n```julia\ncenter_box = CenterBox(ORIENTATION_HORIZONTAL)\nset_start_child!(center_box, Label(\"Left\"))\nset_center_child!(center_box, Button())\nset_end_child!(center_box, Label(\"Right\"))\n```\n\"\"\"\n\n@document CheckButton \"\"\"\n# CheckButton <: Widget\n\nRectangle that displays a checkmark and an optional label. Connect to signal `toggled` to react to the user changing the `CheckButton`'s state by clicking it.\n\n$(@type_constructors(\n    CheckButton()\n))\n\n$(@type_signals(CheckButton, \n    toggled\n))\n\n$(@type_fields())\n\n## Example\n```julia\ncheck_button = CheckButton()\nset_child!(check_button, Label(\"Click Me\"))\nconnect_signal_toggled!(check_button) do self::CheckButton\n    state = get_state(self)\n    print(\"CheckButton is now: \") \n    if state == CHECK_BUTTON_STATE_ACTIVE\n        println(\"active!\")\n    elseif state == CHECK_BUTTON_STATE_INACTIVE\n        println(\"inactive\")\n    else # state == CHECK_BUTTON_STATE_INCONSISTENT\n        println(\"inconsistent\")\n    end\nend\nset_child!(window, check_button)\n```\n\"\"\"\n\n@document ClampFrame \"\"\"\n# ClampFrame <: Widget\n\nConstrains its single child such that the child's width (or height, if vertically orientated) cannot\nexceed the size set using `set_maximum_size!`. \n\n$(@type_constructors(\n    ClampFrame(size_px::AbstractFloat, [::Orientation = ORIENTATION_HORIZONTAL])\n))\n\n$(@type_signals(ClampFrame))\n\n$(@type_fields())\n\n\n\"\"\"\n\n@document ClickEventController \"\"\"\n# ClickEventController <: SingleClickGesture <: EventController\n\nEvent controller that reacts to a series of one or more mouse-button or touchscreen presses.\n\n$(@type_constructors(\n    ClickEventController()\n))\n\n$(@type_signals(ClickEventController, \n    click_pressed,\n    click_released,\n    click_stopped\n))\n\n$(@type_fields())\n\n## Example\n```julia\nclick_controller = ClickEventController()\nconnect_signal_click_pressed!(click_controller) do self::ClickEventController, n_presses::Integer, x::Float64, y::Float64\n    if n_presses == 2\n        println(\"double click registered at position (\\$(Int64(x)), \\$(Int64(y)))\")\n    end\nend\nadd_controller!(window, click_controller)\n```\n\"\"\"\n\n@document Clipboard \"\"\"\n# Clipboard <: SignalEmitter\n\nAllows for accessing and overwriting the data in the user's OS-wide clipboard.\nConstruct an instance of this type by calling [`get_clipboard`](@ref) on the\ntop-level window.\n\nIf the clipboard contains an image, use [`get_image`](@ref) to access it,\nany other kind of data needs to be accessed with [`get_string`](@ref).\n\n$(@type_constructors(\n))\n\n$(@type_signals(Clipboard, \n))\n\n$(@type_fields())\n\n## Example\n```julia\nclipboard = get_clipboard(window)\nget_string(clipboard) do self::Clipboard, value::String\n    println(\"Value in clipboard: \" * value)\nend\n```\n\"\"\"\n\n@document Clock \"\"\"\n# Clock <: SignalEmitter\n\nObject used to keep track of time. Nanosecond precision.\n\n$(@type_constructors(\n    Clock()\n))\n\n$(@type_signals(Clock, \n))\n\n$(@type_fields())\n\n## Example\n```julia\nclock = Clock()\ncurrent = restart!(clock)\nsleep(1)\nnow = elapsed(clock)\nprintln(\"time delta: \\$(as_seconds(now - current))\")\n```\n\"\"\"\n\n@document ColorChooser \"\"\"\n# ColorChooser <: SignalEmitter\n\nDialog that allows a user to choose a color.\n\n$(@type_constructors(\n    ColorChooser([title::String, modal::Bool])\n))\n\n$(@type_signals(ColorChooser, \n))\n\n$(@type_fields())\n\n## Example\n```julia\ncolor_chooser = ColorChooser()\non_accept!(color_chooser) do self::ColorChooser, color::RGBA\n    println(\"Selected \\$color\")\nend\non_cancel!(color_chooser) do self::ColorChooser\n    println(\"color selection cancelled\")\nend\npresent!(color_chooser)\n```\n\"\"\"\n\n@document ColumnView \"\"\"\n# ColumnView <: Widget\n\nSelectable widget that arranges its children as a table with rows and named columns.\n\n$(@type_constructors(\n    ColumnView([::SelectionMode])\n))\n\n$(@type_signals(ColumnView, \n    activate\n))\n\n$(@type_fields())\n\n## Example\n```julia\ncolumn_view = ColumnView(SELECTION_MODE_NONE)\n\nfor column_i in 1:4\n    column = push_back_column!(column_view, \"Column #\\$column_i\")\n    for row_i in 1:3\n        set_widget!(column_view, column, row_i, Label(\"\\$row_i | \\$column_i\"))\n    end\nend\n\n# or push an entire row at once\npush_back_row!(column_view, Button(), CheckButton(), Entry(), Separator())        \nset_child!(window, column_view)\n```\n\"\"\"\n\n@document ColumnViewColumn \"\"\"\n# ColumnViewColumn <: SignalEmitter\n\nClass representing a column of [`ColumnView`](@ref). Has a label, any number of children \nwhich represented that column's rows, and an optional header menu.\n\n$(@type_constructors(\n))\n\n$(@type_signals(ColumnViewColumn, \n))\n\n$(@type_fields())\n\n## Example\n```julia\n# create a new column\ncolumn = push_back_column!(column_view)\n\n# set widget in 4th row, automatically backfills rows 1 - 3\nset_widget!(column, 4, Label(\"4\"))\n```\n\"\"\"\n\n@document DragEventController \"\"\"\n# DragEventController <: SingleClickGesture <: EventController\n\nEvent controller that recogizes drag-gestures by both a mouse or touch device.\n\n$(@type_constructors(\n    DragEventController()\n))\n\n$(@type_signals(DragEventController, \n    drag_begin, \n    drag,\n    drag_end\n))\n\n$(@type_fields())\n\n## Example\n```julia\ndrag_controller = DragEventController()\nconnect_signal_drag!(drag_controller) do self::DragEventController, x_offset, y_offset\n    start::Vector2f = get_start_position(self)\n    current = start + Vector2f(x_offset, y_offset)\n    println(\"Current cursor position: \\$current\")\nend\nadd_controller!(window, drag_controller)\n```\n\"\"\"\n\n@document DropDown \"\"\"\n# DropDown <: Widget\n\nPresents the user with a collapsible list of items. If one of its items is clicked, that item's callback will be invoked.\n\n$(@type_constructors(\n    DropDown()\n))\n\n$(@type_signals(DropDown, \n))\n\n$(@type_fields())\n\n## Example\n```julia\ndrop_down = DropDown()\npush_back!(drop_down, \"Item 01\") do x::DropDown\n    println(\"Item 01 selected\") \nend\npush_back!(drop_down, \"Item 02\") do x::DropDown\n    println(\"Item 02 selected\") \nend\nset_child!(window, drop_down)\n```\n\"\"\"\n\n@document DropDownItemID \"\"\"\n# DropDownItemID\n\nID of a dropdown item, returned when adding a new item to the drop down.\nKeep track of this in order to identify items in a position-independent manner.\n\n$(@type_constructors(\n))\n\n$(@type_fields(\n))\n\"\"\"\n\n@document Entry \"\"\"\n# Entry <: Widget\n\nSingle-line text entry, supports \"password mode\", as well as inserting an icon to the left and/or right of the text area.\n\n$(@type_constructors(\n    Entry()\n))\n\n$(@type_signals(Entry, \n    activate, \n    text_changed\n))\n\n$(@type_fields())\n\n\n## Example\n```julia\nentry = Entry()\nset_text!(entry, \"Write here\")\nconnect_signal_text_changed!(entry) do self::Entry\n    println(\"text is now: \\$(get_text(self))\")\nend\n```\n\"\"\"\n\n@document EventController abstract_type_docs(EventController, Any, \"\"\"\n# EventController <: SignalEmitter\n\nSuperclass of all event controllers. Use [`add_controller!`](@ref)\nto connect an event controller to any widget, after which it starts receiving input events.\nConnect to the unique signals of each event controller in order to react to these events.\n\"\"\")\n\n@document Expander \"\"\"\n# Expander <: Widget\n\nCollapsible item which has a label and child. If the label is clicked, \nthe child is shown (or hidden, if it is already shown).\n\n!!! note\n    This widget should not be used to create collapsible lists, \n    use [`ListView`](@ref) for this purpose instead.\n\n$(@type_constructors(\n    Expander(),\n    Expander(child::Widget, label::Widget)\n))\n\n$(@type_signals(Expander, \n    activate\n))\n\n$(@type_fields())\n\"\"\"\n\n@document FileChooser \"\"\"\n# FileChooser <: SignalEmitter\n\nPre-made dialog that allows users to pick a file or folder on the \nlocal disk. \n\nConnect a function with the signature\n```\n(::FileChooser, files::Vector{FileDescriptor}, [::Data_t]) -> Nothing\n```\nusing [`on_accept!`](@ref). When the user makes a selection, this function \nwill be invoked and `files` will contain one or more selected files.\n\nThe file choosers layout depends on the [`FileChooserAction`](@ref) specified on construction.\n\n$(@type_constructors(\n    FileChooser(::FileChooserAction, [title::String])\n))\n\n$(@type_signals(FileChooser, \n))\n\n$(@type_fields())\n\n## Example\n```julia\nfile_chooser = FileChooser(FILE_CHOOSER_ACTION_OPEN_FILE)\n\non_accept!(file_chooser) do x::FileChooser, files::Vector{FileDescriptor}\n    if !isempty(files)\n        println(\"Selected file at \", get_path(files[1]))\n    end\nend\n\non_cancel!(file_chooser) do x::FileChooser\n    println(\"Canceled.\")\nend\n\npresent!(file_chooser)\n```\n\"\"\"\n\n@document FileDescriptor \"\"\"\n# FileDescriptor <: SignalEmitter\n\nRead-only object that points to a specific location on disk. There is no\nguarantee that this location contains a valid file or folder.\n\n$(@type_constructors(\n    FileDescriptor(path::String)\n))\n\n$(@type_signals(FileDescriptor, \n))\n\n$(@type_fields())\n\n## Example\n```julia\ncurrent_dir = FileDescriptor(\".\")\nfor file in get_children(current_dir)\n    println(get_name(file) * \":\\t\" * get_content_type(file))\nend\n```\n\"\"\"\n\n@document FileFilter \"\"\"\n# FileFilter <: SignalEmitter\n\nFilter used by [`FileChooser`](@ref). Only files that \npass the filter will be available to be selected when the file chooser is active.\n\nIf multiple filters are supplied, the user can \nselect between them using a dropdown that is automatically \nadded to the `FileChooser` dialog.\n\n$(@type_constructors(\n    FileFilter(name::String)\n))\n\n$(@type_signals(FileFilter, \n))\n\n$(@type_fields())\n\n## Example\n```julia\nfilter = FileFilter()\nadd_allowed_suffix!(filter, \"jl\") # without the `.`\n````\n\"\"\"\n\n@document FileMonitor \"\"\"\n# FileMonitor <: SignalEmitter\n\nObject that monitors a location on disk. If anything about the object at that location changes, \nit invoke the callback registered using [`on_file_changed!`](@ref), which requires a function with signature\n\n```\n(::FileMonitor, event::FileMonitorEvent, self::FileDescriptor, other::FileDescriptor, [::Data_t]) -> Nothing\n```\n\nWhere `event` classifies the type of change, `self` is the file being monitored.\n\n$(@type_constructors(\n))\n\n$(@type_signals(FileMonitor, \n))\n\n$(@type_fields())\n\n## Example\n```julia\nfile = FileDescriptor(\"path/to/file.jl\")\n@assert(exists(file))\nmonitor = create_monitor(file)\non_file_changed!(monitor) do x::FileMonitor, event_type::FileMonitorEvent, self::FileDescriptor, other::FileDescriptor\n    if event_type == FILE_MONITOR_EVENT_CHANGED\n        println(\"File at \" * get_path(self) * \" was modified.\")\n    end\nend\n```\n\"\"\"\n\n@document Fixed \"\"\"\n# Fixed <: Widget\n\nContainer widget that places its children at a specified pixel position relative to the `Fixed`s top-left corner.\n\nUse of this widget is usually discouraged, it does not allow for automatic expansion or alignment.\n\n$(@type_constructors(\n    Fixed()\n))\n\n$(@type_signals(Fixed, \n))\n\n$(@type_fields())\n\"\"\"\n\n@document FlowBox \"\"\"\n# FlowBox <: Widget\n\n`Box`-like widget that dynamically rearranges its children into multiple rows (or columns), as the widget's width (or height) changes.\n\n$(@type_constructors(\n    FlowBox(Orientation)\n))\n\n$(@type_signals(Fixed, \n))\n\n$(@type_fields())\n\n\n\"\"\"\n\n@document FocusEventController \"\"\"\n# FocusEventController <: EventController\n\nReacts to a widget gaining or losing input focus.\n\n$(@type_constructors(\n    FocusEventController()\n))\n\n$(@type_signals(FocusEventController, \n    focus_gained,\n    focus_lost\n))\n\n$(@type_fields())\n\n## Example\n```julia\nfocus_controller = FocusEventController()\nconnect_signal_focus_gained!(focus_controller) do self::FocusController\n    println(\"Gained Focus\")\nend\nadd_controller!(widget, focus_controller)\n```\n\"\"\"\n\n@document Frame \"\"\"\n# Frame <: Widget\n\nWidget that draws a black outline with rounded corners around\nits singular child.\n\n$(@type_constructors(\n    Frame(),\n    Frame(child::Widget)\n))\n\n$(@type_signals(Frame, \n))\n\n$(@type_fields())\n\n\n\"\"\"\n\n@document FrameClock \"\"\"\n# FrameClock <: SignalEmitter\n\nClock that is synched with a widget's render cycle. Connect to its signals to trigger behavior once per frame.\n\n$(@type_constructors(\n))\n\n$(@type_signals(FrameClock, \n))\n\n$(@type_fields())\n\n## Example\n```julia\nframe_clock = get_frame_clock(widget)\nconnect_signal_paint!(frame_clock) do x::FrameClock\n    println(\"Widget was drawn.\")\nend\n```\n\"\"\"\n\n@document GLArea \"\"\"\n# GLArea <: Widget\n\nCanvas that can be used as an OpenGL render target. This widget is intended to be used by third libraries, \nif you want to render using OpenGL using only Mousetrap, use `RenderArea` instead.\n\n\n$(@type_constructors(\n    GLArea()\n))\n\n$(@type_signals(GLArea,\n    render,\n    resize \n))\n\n$(@type_fields())\n\n## Example\n```julia\ncanvas = GLArea()\nconnect_signal_resize!(canvas) do self::GLArea, x, y\n    # viewport was resized to x, y (in pixels)\nend \nconnect_signal_render!(canvas) do self::GLArea, gl_context::Ptr{Cvoid}\n    make_current!(canvas)\n    # do OpenGL rendering here\n    return true \nend\n```\n\"\"\"\n\n@document GLTransform \"\"\"\n# GLTransform <: SignalEmitter\n\nTransform in 3D spaces. Uses OpenGL coordinates, it should \ntherefore only be used to modify vertices of a [`Shape`](@ref).\n\nCan be indexed and modified as a 4x4 matrix of `Float32`.\n\n$(@type_constructors(\n    GLTransform()\n))\n\n$(@type_signals(GLTransform, \n))\n\n$(@type_fields(\n))\n\"\"\"\n\n@document Grid \"\"\"\n# Grid <: Widget\n\nSelectable container that arranges its children in a non-uniform grid. \nEach child has a row- and column index, as well as a width and height, measured in number of cells.\n\n$(@type_constructors(\n    Grid()  \n))\n\n$(@type_signals(Grid, \n))\n\n$(@type_fields())\n\n## Example\n```julia\ngrid = Grid()\ninsert_at!(grid, Label(\"Label\"), 1, 1, 1, 1)\ninsert_at!(grid, Button(), 1, 2, 1, 1)\ninsert_at!(grid, Separator, 2, 1, 2, 1)\n```\n\"\"\"\n\n@document GridView \"\"\"\n# GridView <: Widget\n\nSelectable widget container that arranges its children in a uniform \ngrid.\n\n$(@type_constructors(\n    GridView(::Orientation, [::SelectionMode])\n))\n\n$(@type_signals(GridView, \n    activate_item\n))\n\n$(@type_fields())\n\"\"\"\n\n@document GroupID \"\"\"\n# GroupID\n\nID of a group inside a `KeyFile`. May not start with a number, \nand can only roman letters, 0-9, `_`, `-`, and `.`.\n\nUse `.` to deliminate nested groups, as each key-value pair has to \nbelong to exactly one group.\n\n$(@type_constructors(\n))\n\n$(@type_fields(\n))\n\"\"\"\n\n@document HSVA \"\"\"\n# HSVA\n\nColor in hsva format, all components are `Float32` in `[0, 1]`.\n\n$(@type_constructors(\n    HSVA(::AbstractFloat, ::AbstractFloat, ::AbstractFloat, ::AbstractFloat)\n))\n\n$(@type_fields(\n    h::Float32,\n    s::Float32,\n    v::Float32,\n    a::Float32\n))\n\"\"\"\n\n@document HeaderBar \"\"\"\n# HeaderBar <: Widget\n\nWidget that is usually used as the title bar of a window. It contains a title, \nclose-, maximize-, minimize buttons, as well as an area for widgets on both sides of the title.\n\n$(@type_constructors(\n    HeaderBar(),\n    HeaderBar(layout::String)\n))\n\n$(@type_signals(HeaderBar, \n))\n\n$(@type_fields())\n\n\n\n## Example\n```julia\nheader_bar = HeaderBar(\"close:title,minimize,maximize\")\npush_front!(header_bar, Button())\nset_titlebar_widget!(window, header_bar)\n```\n\"\"\"\n\n@document Icon \"\"\"\n# Icon\n\nAllows loading of icons from an image file or icon theme.\n\n$(@type_constructors(\n    Icon(),\n    Icon(path::String),\n    Icon(theme::IconTheme, id::IconID, square_resolution::Integer)\n))\n\n$(@type_fields(\n))\n\"\"\"\n\n@document IconID \"\"\"\n# IconID\n\nID of an icon, used by [`IconTheme`](@ref) to refer to icons in \na file-agnostic way.\n\n$(@type_constructors(\n))\n\n$(@type_fields()))\n\"\"\"\n\n@document IconTheme \"\"\"\n# IconTheme <: Any\n\nAllows loading of items from a folder if that folder strictly adheres to \nthe [freedesktop icon theme specifications](https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html). \n\nA [`Window`](@ref) is required to construct the icon theme, at which point the default icons for that window's display are also loaded.\n\n$(@type_constructors(\nIconTheme(::Window)\n))\n\n$(@type_fields())\n\n## Example\n```julia\ntheme = IconTheme()\nadd_resource_path!(theme, \"/path/to/freedesktop_icon_theme_directory\")\n\nicon = Icon()\ncreate_from_theme!(icon, theme, \"custom-icon-id\", 64)\n```\n\"\"\"\n\n@document Image \"\"\"\n# Image <: Any\n\n2D image data, in RGBA.\n\n$(@type_constructors(\n    Image(),\n    Image(path::String),\n    Image(width::Integer, height::Integer, [::RGBA])\n))\n\n$(@type_fields())\n\"\"\"\n\n@document ImageDisplay \"\"\"\n# ImageDisplay <: Widget\n\nWidget that displays an [`Image`](@ref), [`Icon`](@ref), or image file.\n\n$(@type_constructors(\n    ImageDisplay(path::String),\n    ImageDisplay(::Image),\n    ImageDisplay(::Icon)\n))\n\n$(@type_signals(ImageDisplay, \n))\n\n$(@type_fields())\n\"\"\"\n\n@document KeyCode \"\"\"\n# KeyCode\n\nIdentifier of a key. Used for [`ShortcutTrigger`](@ref) syntax.\n\n$(@type_constructors(\n))\n\n$(@type_fields(\n))\n\"\"\"\n\n@document KeyEventController \"\"\"\n# KeyEventController <: EventController\n\nEvent controller that recognizes keyboard keystrokes.\n\n$(@type_constructors(\n    KeyEventController()\n))\n\n$(@type_signals(KeyEventController, \n    key_pressed,\n    key_released,\n    modifiers_changed\n))\n\n$(@type_fields())\n\n## Example\n```julia\nkey_controller = KeyEventController()\nconnect_signal_key_pressed!(key_controller) do self::KeyEventController, key::KeyCode, modifier::ModifierState\n    if key == KEY_space\n        println(\"space bar pressed\")\n    end\nend\nadd_controller!(window, key_controller)\n```\n\"\"\"\n\n@document KeyFile \"\"\"\n# KeyFile <: SignalEmitter\n\nGLib keyfile, ordered into groups with key-value pairs. \n\nAllows (de-)serializing of the following types:\n+ `Bool`, `Vector{Bool}`\n+ `AbstractFloat`, `Vector{AbstractFloat}`\n+ `Signed`, `Vector{Signed}`\n+ `Unsigned`, `Vector{Unsigned}`\n+ `String`, `Vector{String}`\n+ `RGBA`\n+ `HSVA`\n+ `Image`\n\nAll key-value pairs have to be in exactly one group.\n\n$(@type_constructors(\n    KeyFile(),\n    KeyFile(path::String)\n))\n\n$(@type_signals(KeyFile, \n))\n\n$(@type_fields())\n\n## Example\n```julia\nkey_file = KeyFile()\nset_value!(key_file, \"group_id\", \"key_id\", [123, 456, 789])\nset_comment_above!(key_file, \"group_id\", \"key_id\", \"An example key-value pair\")\nprintln(as_string(key_file))\n```\n```\n[group_id]\n# An example key-value pair\nkey_id=123;456;789;\n````\n\"\"\"\n\n@document KeyID \"\"\"\n# KeyID\n\nID of [`KeyFile`](@ref) key-value-pair. Contains only Roman letters, 0-9, and '_'.\n\n$(@type_constructors(\n))\n\n$(@type_fields(\n))\n\"\"\"\n\n@document Label \"\"\"\n# Label <: Widget\n\nStatic text, multi- or single-line. Use [`set_ellipsize_mode!`](@ref),\n[`set_wrap_mode!`](@ref), and [`set_justify_mode!`](@ref) to influence how text \nis displayed.\n\nSupports [pango markup](https://docs.gtk.org/Pango/pango_markup.html), see \nthe manual section on labels for more information.\n\n$(@type_constructors(\n    Label(),\n    Label(markup_string::String)\n))\n\n$(@type_signals(Label, \n))\n\n$(@type_fields())\n\"\"\"\n\n@document LevelBar \"\"\"\n# LevelBar <: Widget\n\nNon-interactive widget that displays the value of a range as a fraction.\n\n$(@type_constructors(\n    LevelBar(min::AbstractFloat, max::AbstractFloat)\n))\n\n$(@type_signals(LevelBar, \n))\n\n$(@type_fields())\n\"\"\"\n\n@document ListView \"\"\"\n# ListView <: Widget\n\nSelectable widget container that arranges its children in a (nested) list.\n\n$(@type_constructors(\n    ListView(::Orientation, [::SelectionMode])\n))\n\n$(@type_signals(ListView, \n    activate_item\n))\n\n$(@type_fields())\n\n## Example\n```julia\nlist_view = ListView(ORIENTATION_VERTICAL)\n\nitem_01_iterator = push_back!(list_view, Label(\"Item 01\"))\npush_back!(list_view, Label(\"Item 02\"))\npush_back!(list_view, Label(\"Imte 03\"))\n\npush_back!(list_view, Label(\"Nested Item 01\"), item_01_iterator)\npush_back!(list_view, Label(\"Nested Item 02\"), item_01_iterator)\n\nset_child!(window, list_view)\n```\n\"\"\"\n\n@document ListViewIterator \"\"\"\n# ListViewIterator\n\nIterator returned when inserting an item into a [`ListView`](@ref). Use this\niterator as an additional argument to `push_back!`, `push_front!`, or `insert`, in order \nto create a nested list at that item position.\n\n$(@type_constructors(\n))\n\n$(@type_fields(\n))\n\"\"\"\n\n@document LogDomain \"\"\"\n# LogDomain\n\nDomain of log messages, this will be used to associate log \nmessage with a specific application or library. May only contain roman letters, `_`, `.` and `-`.\n\n$(@type_constructors(\n    LogDomain(::String)\n))\n\n$(@type_fields(\n))\n\"\"\"\n\n@document LongPressEventController \"\"\"\n# LongPressEventController <: SingleClickGesture <: EventController\n\nEvent controller that recognizes long-press-gestures from a mouse or touch device.\n\n$(@type_constructors(\n    LongPressEventController()\n))\n\n$(@type_signals(LongPressEventController, \n    pressed,\n    press_cancelled\n))\n\n$(@type_fields())\n\n## Example\n```julia\nlong_press_controller = LongPressEventController()\nconnect_signal_pressed!(long_press_controller) do self::LongPressEventController, x::AbstractFloat, y::AbstractFloat \n    println(\"long press recognized at (\\$x, \\$y)\")\nend\nadd_controller!(window, long_press_controller)\n```\n\"\"\"\n\n@document MenuBar \"\"\"\n# MenuBar <: Widget\n\nView that displays a [`MenuModel`](@ref) as a horizontal bar. \n\nThe `MenuModel` has to have the following structure:\n+ all top-level items have to be \"submenu\"-type items\n+ no submenu or section of another submenu may have a \"widget\"-type item\n\n$(@type_constructors(\n    HeaderBar(::MenuModel)\n))\n\n$(@type_signals(MenuBar, \n))\n\n$(@type_fields())\n\n## Example\n```julia\naction = Action(\"example.action\", app)\nset_function!(action) do action::Action\n    println(get_id(action), \" activate.\")\nend\n\nouter_model = MenuModel()\ninner_model = MenuModel()\nadd_action!(inner_model, \"Trigger Action\", action)\nadd_submenu!(outer_model, \"Submenu\", inner_model)\n\nmenu_bar = MenuBar(outer_model)\nset_child!(window, menu_bar)\n```\n\"\"\"\n\n@document MenuModel \"\"\"\n# MenuModel <: SignalEmitter\n\nModel that holds information about how to \nconstruct a menu. \n\nUse [`MenuBar`](@ref) or [`PopoverMenu`](@ref) to display the\nmenu to the user.\n\nThe following types of menu items are available\n\n| Type      | Function |\n|-----------|-------------|\n| \"action\"  | [`add_action!(::MenuModel, label::String, ::Action)`](@ref) |\n| \"icon\"    | [`add_icon!(::MenuModel, ::Icon, ::Action)`](@ref) |\n| \"submenu\" | [`add_submenu!(::MenuModel, label::String, other::MenuModel)`](@ref) |\n| \"section\" | [`add_section!(::MenuModel, label::String, other::MenuModel)`](@ref) |\n| \"widget\"  | [`add_widget!(::MenuModel, ::Widget)`](@ref) |\n\nSee the manual section on menus for more information.\n\n$(@type_constructors(\n    MenuModel()\n))\n\n$(@type_signals(MenuModel, \n    items_changed\n))\n\n$(@type_fields())\n\"\"\"\n\n@document ModifierState \"\"\"\n# ModifierState\n\nHolds information about which modifiers are currently pressed\n\nSee also:\n+ [`control_pressed`](@ref)\n+ [`alt_pressed`](@ref)\n+ [`shift_pressed`](@ref)\n+ [`mouse_button_01_pressed`](@ref)\n+ [`mouse_button_02_pressed`](@ref)\n\n$(@type_constructors(\n))\n\n$(@type_fields(\n))\n\n## Example\n```julia\nkey_controller = KeyEventController()\nconnect_signal_modifiers_changed!(key_controller) do self::KeyEventController, modifiers::ModifierState\n    if shift_pressed(modifiers)\n        println(\"Shift was pressed\")\n    end\nend\nadd_controller!(window, key_controller)\n```\n\"\"\"\n\n@document MotionEventController \"\"\"\n# MotionEventController <: EventController\n\nCaptures cursor motion events while the cursor is inside the allocated area of the associated widget.\n\n$(@type_constructors(\n    MotionEventController()\n))\n\n$(@type_signals(MotionEventController, \n    motion_enter,\n    motion,\n    motion_leave\n))\n\n$(@type_fields())\n\n## Example\n```julia\nmotion_controller = MotionEventController()\nconnect_signal_motion!(motion_controller) do self::MotionEventController, x::AbstractFloat, y::AbstractFloat\n    println(\"recognized motion at (\\$(Int64(round(x))), \\$(Int64(round(y))))\")\nend\nadd_controller!(window, motion_controller)\n```\n\"\"\"\n\n@document Notebook \"\"\"\n# Notebook <: Widget\n\nWidget that arranges its children as a list of pages. Each page\nhas exactly one child widget, as well as an optional label widget. Pages\ncan be freely reordered by the user if [`set_tabs_reorderable!`](@ref) is\nset to true. It furthermore supports a quick-change menu, in which \nthe user can quickly jump to another tab. To enable this, `set_quick_change_menu_enabled!`\nneeds to be set to `true`.\n\n$(@type_constructors(\n    Notebook()\n))\n\n$(@type_signals(Notebook, \n    page_added,\n    page_removed,\n    page_reordered,\n    page_selection_changed\n))\n\n$(@type_fields())\n\n## Example\n```julia\nnotebook = Notebook()\npush_back!(notebook, Separator(), Label(\"Page 01\"))\npush_back!(notebook, Separator(), Label(\"Page 02\"))\n\nconnect_signal_page_selection_changed!(notebook) do x::Notebook, index::Integer\n    println(\"Page #\\$index is now selected\")\nend\n\nset_child!(window, notebook)\n```\n\"\"\"\n\n@document Overlay \"\"\"\n# Overlay <: Widget\n\nWidget that has exactly one \"base\" child, and any number of \"overlay\" children. If \ntwo interactable widgets overlap, only the top-most widget will be interactable. \n\n$(@type_constructors(\n    Overlay(),\n    Overlay(base::Widget, overlays::Widget...)\n))\n\n$(@type_signals(Overlay, \n))\n\n$(@type_fields())\n\n\n\n## Example\n```julia\noverlay = Overlay()\nset_child!(overlay, Separator())\nadd_overlay!(overlay, Label(\"On Top\"))    \nset_child!(window, overlay)\n```\n\"\"\"\n\n@document PanEventController \"\"\"\n# PanEventController <: SingleClickGesture <: EventController\n\nRecognizes pan gestures along exactly one axis.\n\n$(@type_constructors(\n    PanEventController(axis::Orientation)\n))\n\n$(@type_signals(PanEventController, \n    pan\n))\n\n$(@type_fields())\n\n## Example\n```julia\nconnect_signal_pan!(pan_controller) do self::PanEventController, direction::PanDirection, offset::AbstractFloat\n    if direction == PAN_DIRECTION_LEFT\n        println(\"panning left\")\n    elseif direction == PAN_DIRECTION_RIGHT\n        println(\"panning right\")\n    end\n    println(\"x-offset from start position: \\$offset\")\nend\nadd_controller!(window, pan_controller)\n```\n\"\"\"\n\n@document Paned \"\"\"\n# Paned <: Widget\n\nWidget with exactly two children. Draws a solid border between the two, which the user can drag to \none side or the other to control the size of both widgets at the same time.\n\n$(@type_constructors(\n    Paned(orientation::Orientation),\n    Paned(orientation::Orientation, start_child::Widget, end_child::Widget)\n))\n\n$(@type_signals(Paned, \n))\n\n$(@type_fields())\n\n## Example\n```julia\npaned = Paned(ORIENTATION_HORIZONTAL)\nset_start_child!(paned, Label(\"Left\"))\nset_end_child!(paned, Label(\"Right\"))\n```\n\"\"\"\n\n@document PinchZoomEventController \"\"\"\n# PinchZoomEventController <: EventController\n\nController recognizing 2-finger pinch-zoom gestures (touch-only).\n\n$(@type_constructors(\n    PinchZoomEventController()\n))\n\n$(@type_signals(PinchZoomEventController, \n    scale_changed\n))\n\n$(@type_fields())\n\n## Example\n```julia\npinch_zoom_controller = PinchZoomEventController()\nconnect_signal_scale_changed!(pinch_zoom_controller) do self::PinchZoomEventController, scale::AbstractFloat\n    println(\"current scale: \\$scale\")\nend\nadd_controller!(window, pinch_zoom_controller)\n```\n\"\"\"\n\n@document PopupMessage \"\"\"\n# PopupMessage <: SignalEmitter\n\nPopup message, always has a title and a close button. Additionally, a singular optional button can be placed next to the title. \nWhen clicked, the `PopupMessage` emits signal `button_clicked`, or calls the `Action` connected to the button using `set_button_action!`.\n\nUse `PopupMessageOverlay` to display the message above a widget.\n\n$(@type_constructors(\n    PopupMessage(title::String),\n    PopupMessage(title::String, button_label::String)\n))\n\n$(@type_signals(PopupMessage, \n    dismissed,\n    button_clicked\n))\n\n$(@type_fields())\n\n## Example\n```julia\nmessage_overlay = PopupMessageOverlay()\nset_child!(message_overlay, Separator())\n\nmessage = PopupMessage(\"Is this a message?\", \"Yes\")\nconnect_signal_button_clicked!(message) do self::PopupMessage\n    println(\"button clicked\")\nend\nconnect_signal_dismissed!(message) do self::PopupMessage\n    println(\"message closed\")\nend\n\nshow_message!(message_overlay, message)\n```\n\"\"\"\n\n@document PopupMessageOverlay \"\"\"\n# PopupMessageOverlay <: SignalEmitter\n\nWidget that can display a `PopupMessage` above the `PopupMessageOverlay`'s singular child. Only one message can be shown at a time.\n\n$(@type_constructors(\n    PopupMessageOverlay()\n))\n\n$(@type_signals(PopupMessageOverlay, \n))\n\n$(@type_fields())\n\n## Example\n```julia\noverlay = PopupMessageOverlay()\nset_child!(overlay, widget)\n\nmessage = PopupMessage(\"This example works!\", \"ok\")\nconnect_signal_button_clicked!(message) do self::PopupMessage\n    println(\"button clicked\")\nend\nconnect_signal_dismissed!(message) do self::PopupMessage\n    println(\"message closed\")\nend\n\nshow_message!(overlay, message)\n```\n\"\"\"\n\n@document Popover \"\"\"\n# Popover <: Widget\n\nWindow-type widget with exactly one child, has to be attached to another widget to be visible.\nUse [`PopoverButton`](@ref) to automatically show / hide the popover.\n\n$(@type_constructors(\n    Popover()\n))\n\n$(@type_signals(Popover, \n    closed\n))\n\n$(@type_fields())\n\n## Example\n```julia\npopover = Popover()\nset_child!(popover, Label(\"Popover!\"))\n\npopover_button = PopoverButton()\nset_popover!(popover_button, popover)\n\nset_child!(window, popover_button)\n```\n\"\"\"\n\n@document PopoverButton \"\"\"\n# PopoverButton <: Widget\n\nButton that automatically shows or hides its associated [`Popover`](@ref) or [`PopoverMenu`](@ref) when clicked.\n\n$(@type_constructors(\n    PopoverButton(::Popover),\n    PopoverButton(::PopoverMenu)\n))\n\n$(@type_signals(PopoverButton, \n))\n\n$(@type_fields())\n\n## Example\n```julia\npopover = Popover()\nset_child!(popover, Label(\"Popover!\"))\n\npopover_button = PopoverButton()\nset_popover!(popover_button, popover)\n\nset_child!(window, popover_button)\n```\n\"\"\"\n\n@document PopoverMenu \"\"\"\n# PopoverMenu <: Widget\n\nMenu view that displays a [`MenuModel`](@ref) in a popover window. \nUse [`PopoverButton`](@ref) to automatically show / hide the popover.\n\n$(@type_constructors(\n    PopoverMenu(::MenuModel)\n))\n\n$(@type_signals(PopoverMenu, \n    closed\n))\n\n$(@type_fields())\n\n## Example\n```julia\naction = Action(\"example.action\", app)\nset_function!(action) do x::Action\n    println(\"Action activated\")\nend\n\nmodel = MenuModel()\nadd_action!(model, \"Trigger Example\", action)\n\npopover_menu = PopoverMenu(model)\npopover_button = PopoverButton()\nset_popover_menu!(popover_button, popover_menu)\n\nset_child!(window, popover_button)\n```\n\"\"\"\n\n@document ProgressBar \"\"\"\n# ProgressBar <: Widget\n\nBar that displays a fraction in `[0, 1]`. Use `set_fraction!` to change the current value.\n\n$(@type_constructors(\n    ProgressBar()\n))\n\n$(@type_signals(ProgressBar, \n))\n\n$(@type_fields())\n\"\"\"\n\n@document RGBA \"\"\"\n# RGBA\n\nColor representation in rgba. All components are `Float32` in `[0, 1]`.\n\n$(@type_constructors(\n    RGBA(r::AbstractFloat, g::AbstractFloat, b::AbstractFloat, a::AbstractFloat)\n))\n\n$(@type_fields(\n    r::Float32,\n    g::Float32,\n    b::Float32,\n    a::Flota32\n))\n\"\"\"\n\n@document RenderArea \"\"\"\n# RenderArea <: Widget\n\nCanvas for rendering custom shapes.\n\nSee the manual chapter on native rendering for more\ninformation.\n\n$(@type_constructors(\n    RenderArea([AntiAliasingQuality = ANTI_ALIASING_QUALITY_OFF])\n))\n\n$(@type_signals(RenderArea,\n    #render,\n    resize \n))\n\n$(@type_fields())\n\n## Example\n```julia\nrender_area = RenderArea()\nrectangle = Rectangle(Vector2f(-0.5, -0.5), Vector2f(1, 1))\nadd_render_task!(render_area, RenderTask(rectangle))\nset_size_request!(render_area, Vector2f(150, 150))\nset_child!(window, render_area)\n```\n\"\"\"\n\n@document RenderTask \"\"\"\n# RenderTask <: SignalEmitter\n\nTask that groups a [`Shape`](@ref), [`Shader`](@ref), [`GLTransform`]@ref, and [`BlendMode`](@ref),\nallowing them to be bound for rendering. \n    \nIf no shader, transform, and/or blend mode is specified, \nthe default shader, identity transform, and [`BLEND_MODE_NORMAL`](@ref) will \nbe used, respectively.\n\nSee the manual chapter on native rendering for more\ninformation.\n\n$(@type_constructors(\n    RenderTask(::Shape ; [shader::Union{Shader, Nothing}, transform::Union{GLTransform, Nothing}, blend_mode::BlendMode])\n))\n\n$(@type_signals(RenderTask, \n))\n\n$(@type_fields())\n\n## Example\n```julia\nshape = Rectangle(Vector2f(-0.5, -0.5), Vector2f(1, 1))\ntask = RenderTask(shape)\n\n# euivalent to\n\ntask = RenderTask(shape;\n    shader = nothing,\n    transform = nothing,\n    blend_mode = BLEND_MODE_NORMAL\n)\n```\n\"\"\"\n\n@document RenderTexture \"\"\"\n# RenderTexture <: TextureObject <: SignalEmitter\n\nTexture that can be bound as a render target. This object is for internal use only.\n\n$(@type_constructors(\n    RenderTexture()\n))\n\n$(@type_signals(RenderTexture, \n))\n\n$(@type_fields())\n\"\"\"\n\n@document Revealer \"\"\"\n# Revealer <: Widget\n\nContainer that plays an animation to reveal or hide its singular child.\n\n$(@type_constructors(\n    Revealer([::RevealerTransitionType]),\n    Revealer(child::Widget, [::RevealerTransitionType])\n))\n\n$(@type_signals(Revealer, \n    revealed\n))\n\n$(@type_fields())\n\"\"\"\n\n@document RotateEventController \"\"\"\n# RotateEventController <: EventController\n\nRecognizes 2-finger rotate gestures (touch-only).\n\n$(@type_constructors(\n    RotateEventController()\n))\n\n$(@type_signals(RotateEventController, \n    rotation_changed\n))\n\n$(@type_fields())\n\n## Example\n```julia\nrotate_controller = RotateEventController()\nconnect_signal_rotation_changed!(rotate_controller) do self::RotateEventController, angle_absolute::AbstractFloat, angle_dela::AbstractFloat\n    println(\"angle is now: \" * as_degrees(radians(angle_absolute)) * \"°\")\nend\nadd_controller!(window, rotate_controller)\n```\n\"\"\"\n\n@document Scale \"\"\"\n# Scale <: Widget\n\nAllows users to select a value from a range.\n\n$(@type_constructors(\n    Scale(lower::AbstractFloat, upper::AbstractFloat, step_increment::AbstractFloat, [::Orientation])\n))\n\n$(@type_signals(Scale, \n    value_changed\n))\n\n$(@type_fields())\n\n## Example\n```julia\nscale = Scale(0, 1, 0.01)\nconnect_signal_value_changed!(scale) self::Scale\n    println(\"Current value: \\$(get_value(scale))\")\nend\n```\n\"\"\"\n\n@document Scrollbar \"\"\"\n# Scrollbar <: Widget\n\nGUI element typically used to scroll another widget. Connect to the signals of the\nunderlying adjustment to react to the user scrolling.\n\n$(@type_constructors(\n    Scrollbar(::Orientation, ::Adjustment)\n))\n\n$(@type_signals(Scrollbar,\n))\n\n$(@type_fields())\n\n## Example\n```julia\nscrollbar = Scrollbar(ORIENTATION_HORIZONTAL, Adjustment(0, 0, 1, 0.01))\nconnect_signal_value_changed!(get_adjustment(scrollbar)) do self::Adjustment\n    println(\"value is now \\$(get_value(self))\")\nend\n\"\"\"\n\n@document ScrollEventController \"\"\"\n# ScrollEventController <: EventController\n\nController able to recognize scrolling gestures by a mouse scroll wheel or touch device.\n\n$(@type_constructors(\n    ScrollEventController([kinetic_scrolling_enabled::Bool = false])\n))\n\n$(@type_signals(ScrollEventController, \n    scroll_begin,\n    scroll,\n    scroll_end,\n    kinetic_scroll_decelerate\n))\n\n$(@type_fields())\n\n## Example\n```julia\nscroll_controller = ScrollEventController()\nconnect_signal_scroll!(scroll_controller) do self::ScrollEventController, delta_x::AbstractFloat, delta_y::AbstractFloat\n    println(\"current scroll offset: (\\$delta_x, \\$delta_y)\")\nend\nadd_controller!(window, scroll_controller)\n```\n\"\"\"\n\n@document SelectionModel \"\"\"\n# SelectionModel <: SignalEmitter\n\nModel that governs the current selection of a selectable widget,\nsuch as [`GridView`](@ref), [`ListView`](@ref), or [`Stack`](@ref).\n\nOnly if the selection mode is set to anything other than [`SELECTION_MODE_NONE`](@ref)\nwill the selection model emit its signals.\n\nUse [`get_selection_model`](@ref) to retrieve the model from a selectable widget.\n\n$(@type_constructors(\n))\n\n$(@type_signals(SelectionModel, \n    selection_changed\n))\n\n$(@type_fields())\n\n## Example\n```julia\ngrid_view = GridView(SELECTION_MODE_SINGLE)\nfor i in 1:4\n    push_back!(grid_view, Label(\"0\\$i\"))\nend\n\nselection_model = get_selection_model(grid_view)\nconnect_signal_selection_changed!(selection_model) do x::SelectionModel, position::Integer, n_items::Integer\n    println(\"selected item is now: \\$position\")\nend\nset_child!(window, grid_view)\n```\n\"\"\"\n\n@document Separator \"\"\"\n# Separator <: Widget\n\nSimple spacer, fills its allocated area with a solid color.\n\n$(@type_constructors(\n    Separator([::Orientation, opacity::AbstractFloat = 1.0])\n))\n\n$(@type_signals(Separator, \n))\n\n$(@type_fields())\n\"\"\"\n\n@document Shader \"\"\"\n# Shader <: SignalEmitter\n\nOpenGL shader program, contains a fragment and vertex shader.\n\nSee the manual chapter on native rendering for more\ninformation.\n\n$(@type_constructors(\n    Shader()\n))\n\n$(@type_signals(Shader, \n))\n\n$(@type_fields())\n\"\"\"\n\n@document Shape \"\"\"\n# Shape <: SignalEmitter\n\nOpenGL vertex buffer, pre-initialized as one of various shape types.\n\nSee the manual chapter on native rendering for more\ninformation.\n\n$(@type_constructors(\n    Shape(),\n    Point(::Vector2f),\n    Points(::Vector{Vector2f}),\n    Triangle(::Vector2f, ::Vector2f, ::Vector2f),\n    Rectangle(top_left::Vecto2f, size::Vector2f),\n    Circle(center::Vector2f, radius::AbstractFloat, n_outer_vertices::Integer),\n    Ellipse(center::Vector2f, x_radius::AbstractFloat, y_radius::AbstractFloat, n_outer_vertices),\n    Line(::Vector2f, ::Vector2f),\n    Lines(::Vector{Pair{Vector2f, Vector2f}}),\n    LineStrip(::Vector2{Vector2f}),\n    Polygon(::Vector{Vector2f}),\n    RectangularFrame(top_left::Vector2f, outer_size::Vector2f, x_width::AbstractFloat, y_width::AbstractFloat),\n    CircularRing(center::Vector2f, outer_radius::AbstractFloat, thickness::AbstractFloat, n_outer_vertices::Integer),\n    EllipticalRing(center::Vector2f, outer_x_radius::AbstractFloat, outer_y_radius::AbstractFloat, x_thickness::AbstractFloat, y_thickness::AbstractFloat, n_outer_vertices::Unsigned),\n    Wireframe(::Vector{Vector2f}),\n    Outline(other_shape::Shape)\n))\n\n$(@type_signals(Shape, \n))\n\n$(@type_fields())\n\"\"\"\n\n@document ShortcutEventController \"\"\"\n# ShortcutEventController <: EventController\n\nTriggers actions if their associate shortcuts are recognized. \nCall [`add_action!`](@ref) to specify which actions the controller should manage.\n\n$(@type_constructors(\n    ShortcutEventController()\n))\n\n$(@type_signals(ShortcutEventController, \n))\n\n$(@type_fields())\n\n## Example\n```julia\naction = Action(\"example.action\", app)\nset_function!(action) do x::Action\n    println(\"example.action activated\")\nend\n\nadd_shortcut!(action, \"<Control>space\")\n# activate action when the user presses Control + Space\n\nshortcut_controller = ShortcutEventController()\nadd_action!(shortcut_controller, action)\n\nadd_controller!(window, shortcut_controller)\n```\n\"\"\"\n\n@document ShortcutTrigger \"\"\"\n# ShortcutTrigger\n\nString expressing a combination of zero or more modifier \nkeys, enclosed in `<>`, followed by exactly one non-modifier \nkey. \n\nSee the section on `ShortctuEventController` in the manual\nchapter on event handling for more information.\n\n$(@type_constructors(\n    ShortcutTrigger(::String)\n))\n\n$(@type_fields(\n))\n\"\"\"\n\n@document SignalEmitter abstract_type_docs(SignalEmitter, Any, \"\"\"\n# SignalEmitter <: Any\n\nObject that can emit signals.\n\nAny signal emitter is memory-managed independently of Julia, once its\ninternal reference counter reaches zero, it is safely deallocated. Julia\nusers do not have to worry about keeping any signal emitters in scope, it\nis kept alive automatically.\n\"\"\")\n\n@document SingleClickGesture abstract_type_docs(SingleClickGesture, Any, \"\"\"\n# SingleClickGesture <: EventController\n\nSpecialized type of `EventController` that provides the following functions:\n\n+ [`get_current_button`](@ref)\n+ [`get_only_listens_to_button`](@ref)\n+ [`set_only_listens_to_button!`](@ref)\n+ [`set_touch_only!`](@ref)\n\"\"\")\n\n@document SpinButton \"\"\"\n# SpinButton <: Widget\n\nWidget with a value-entry and two buttons.\n\n$(@type_constructors(\n    SpinButton(lower::Number, upper::Number, step_increment::Number, [orientation::Orientation])\n))\n\n$(@type_signals(SpinButton, \n    value_changed\n))\n\n$(@type_fields())\n\"\"\"\n\n@document Spinner \"\"\"\n# Spinner <: Widget\n\nGraphical widget that signifies that a process is busy. Set \n[`set_is_spinning!`](@ref)  to `true` to start the spinning animation.\n\n$(@type_constructors(\n    Spinner()\n))\n\n$(@type_signals(Spinner, \n))\n\n$(@type_fields())\n\"\"\"\n\n@document Stack \"\"\"\n# Stack <: Widget\n\nSelectable widget that always shows exactly one of its children. \nUse [`StackSwitcher`](@ref) or [`StackSidebar`](@ref) to provide a\nway for users to choose the page of the stack.\n\nConnect to the signals of the [`SelectionModel`](@ref) provided by [`get_selection_model`](@ref)\nto track which stack page is currently selected.\n\n$(@type_constructors(\n    Stack()\n))\n\n$(@type_signals(Stack, \n))\n\n$(@type_fields())\n\n## Example\n```julia\nstack = Stack()\n\nadd_child!(stack, Label(\"Page 01\"), \"Page 01\")\nadd_child!(stack, Label(\"Page 02\"), \"Page 02\")\nadd_child!(stack, Label(\"Page 03\"), \"Page 03\")\n\nstack_switcher = StackSwitcher(stack)\n\nbox = Box(ORIENTATION_VERTICAL)\npush_back!(box, stack)\npush_back!(box, stack_switcher)\nset_child!(window, box)\n```\n\"\"\"\n\n@document StackID \"\"\"\n# StackID\n\nID that uniquely identifies a page of a [`Stack`](@ref). Will be used as the page title for [`StackSwitcher`](@ref) and [`StackSidebar`](@ref).\n\n$(@type_constructors(\n))\n\n$(@type_fields(\n))\n\"\"\"\n\n@document StackSidebar \"\"\"\n# StackSidebar <: Widget\n\nWidget that allows users to select a page of a [`Stack`](@ref).\n\n$(@type_constructors(\n    StackSidebar(::Stack)\n))\n\n$(@type_signals(StackSidebar, \n))\n\n$(@type_fields())\n\"\"\"\n\n@document StackSwitcher \"\"\"\n# StackSwitcher <: Widget\n\nWidget that allows users to select a page of a [`Stack`](@ref).\n\n$(@type_constructors(\n    StackSwitcher(::Stack)\n))\n\n$(@type_signals(StackSwitcher, \n))\n\n$(@type_fields())\n\"\"\"\n\n@document StylusEventController \"\"\"\n# StylusEventController <: SingleClickGesture <: EventController\n\nController handling events from a stylus devices, such as drawing tablets.\n\nHas access to many manufacturer-specific sensors, see the section on `StylusEventController`\nin the manual chapter on event handling for more information.\n\n$(@type_constructors(\n    StylusEventController()\n))\n\n$(@type_signals(StylusEventController, \n    stylus_up,\n    stylus_down,\n    proximity,\n    motion\n))\n\n$(@type_fields())\n\n## Example\n```julia\nstylus_controller = StylusEventController()\nconnect_signal_motion!(stylus_controller) do self::StylusEventController, x::AbstractFloat, y::AbstractFloat\n    println(\"stylus position detected at (\\$x, \\$y)\")\nend\nadd_controller!(window, stylus_controller)\n```\n\"\"\"\n\n@document SwipeEventController \"\"\"\n# SwipeEventController <: SingleClickGesture <: EventController\n\nRecognizes swipe gestures (touch-only).\n\n$(@type_constructors(\n    SwipeEventController())\n))\n\n$(@type_signals(SwipeEventController, \n    swipe\n))\n\n$(@type_fields())\n\n## Example\n```julia\nswipe_controller = SwipeEventController()\nconnect_signal_swipe!(swipe_controller) do self::SwipeEventController, x_velocity::AbstractFloat, y_velocity::AbstractFloat\n    print(\"swiping \")\n    \n    if (y_velocity < 0)\n        print(\"up \")\n    elseif (y_velocity > 0)\n        print(\"down \")\n    end\n    \n    if (x_velocity < 0)\n        println(\"left\")\n    elseif (x_velocity > 0)\n        println(\"right\")\n    end\nend\nadd_controller!(window, swipe_controller)\n```\n\"\"\"\n\n@document Switch \"\"\"\n# Switch <: Widget\n\nWidget with a binary state, emits signal `switched` when triggered.\n\n$(@type_constructors(\n    Switch()\n))\n\n$(@type_signals(Switch, \n    switched\n))\n\n$(@type_fields())\n\"\"\"\n\n@document TextView \"\"\"\n# TextView <: Widget\n\nMulti-line text entry. \n\n$(@type_constructors(\n    TextVew()\n))\n\n$(@type_signals(TextView, \n    text_changed\n))\n\n$(@type_fields())\n\n## Example\n```julia\ntext_view = TextView()\nset_text!(text_view, \"Write here\")\nconnect_signal_text_changed!(text_view) do self::TextView\n    println(\"text is now: \\$(get_text(self))\")\nend\n```\n\"\"\"\n\n@document Texture \"\"\"\n# Texture <: TextureObject <: SignalEmitter\n\nOpenGL Texture. \n\nSee the manual chapter on native rendering for more\ninformation.\n\n$(@type_constructors(\n    Texture()\n))\n\n$(@type_signals(Texture, \n))\n\n$(@type_fields())\n\"\"\"\n\n@document TextureObject \"\"\"\n# TextureObject\n\nObject that can be bound as a texture. Use [`set_texture!`](@ref) to associate \nit with a [`Shape`](@ref).\n\nSee the manual chapter on native rendering for more\ninformation.\n\"\"\"\n\n@document Time \"\"\"\n# Time\n\nObject representing a duration of time, nanoseconds precision, may be negative.\n\n$(@type_constructors(\n    nanoseconds(::Int64),\n    microseconds(::Number),\n    milliseconds(::Number),\n    seconds(::Number),\n    minutes(::Number)\n))\n\n$(@type_fields(\n))\n\n## Example\n```julia\n# convert seconds to microseconds\nprintln(as_microseconds(seconds(3.14159)))\n```\n\"\"\"\n\n@document ToggleButton \"\"\"\n# ToggleButton <: Widget\n\nButton with a boolean state. Emits signal `toggled` when its state changes.\n\n$(@type_constructors(\n    ToggleButton(),\n    ToggleButton(label::Widget),\n    ToggleButton(::Icon)\n))\n\n$(@type_signals(ToggleButton, \n    toggled,\n    clicked\n))\n\n$(@type_fields())\n\n## Example\n```julia\ntoggle_button = ToggleButton()\nconnect_signal_toggled!(toggle_button) do self::ToggleButton\n    println(\"state is now: \" get_is_active(self))\nend\nset_child!(window, toggle_button)\n```\n\"\"\"\n\n@document TransformBin \"\"\"\n# TransformBin <: Widget\n\nContainer with a singular child, allows applying spatial transform operations to its child widget.\n\n$(@type_constructors(\n    TransformBin(),\n    TransformBin(child::Widget)\n))\n\n$(@type_signals(TransformBin, \n))\n\n$(@type_fields())\n\n## Example\n```julia\n# continuously rotate a widget\n\nwidget = Button()\nbin = TransformBin()\nset_child!(widget)\n\nanimation = Animation(bin, seconds(1))\nset_repeat_count!(animation, 0) # infinite repeats\non_tick!(animation, bin) do self::Animation, bin::TransformBin\n    rotate!(bin, degrees(1))\nend\nplay!(animation)\n\nset_child!(window, button)\n```\n\"\"\"\n\n@document TypedFunction \"\"\"\n# TypedFunction\n\nObject used to invoke an arbitrary function using the given signature. This wrapper\nwill automatically convert any arguments and return values to the types specified as the signature,\nunless impossible, at which point an assertion error will be thrown on instantiation.\n\nIn this way, it can be used to assert a functions signature at compile time.\n\n$(@type_constructors(\n))\n\n$(@type_fields(\n))\n\n## Example\n\n```julia\nas_typed = TypedFunction(Int64, (Integer,)) do(x::Integer)\n    return string(x)\nend\nas_typed(12) # returns 12, because \"12\" will be converted to given return type, Int64\n```\n\"\"\"\n\n@document Vector2 \"\"\"\n# Vector2{T}\n\nVector with two components, all operations are component-wise, just like in GLSL.\n\n$(@type_constructors(\n    Vector2{T}(::T, ::T),\n    Vector2{T}(both::T)\n))\n\n$(@type_fields(\n    x::T,\n    y::T\n))\n\"\"\"\n\n@document Vector3 \"\"\"\n# Vector3{T}\n\nVector with 4 components, all operations are component-wise, just like in GLSL.\n\n$(@type_constructors(\n    Vector3{T}(::T, ::T, ::T),\n    Vector3{T}(all::T)\n))\n\n$(@type_fields(\n    x::T,\n    y::T,\n    z::T\n))\n\"\"\"\n\n@document Vector4 \"\"\"\n# Vector4{T}\n\nVector with 4 components, all operations are component-wise, just like in GLSL.\n\n$(@type_constructors(\n    Vector4{T}(::T, ::T, ::T, ::T),\n    Vector4{T}(all::T)\n))\n\n$(@type_fields(\n    x::T,\n    y::T,\n    z::T,\n    w::T\n))\n\"\"\"\n\n@document Viewport \"\"\"\n# Viewport <: Widget\n\nContainer that displays part of its singular child. The \nallocated size of the `Viewport` is independent of that\nof its child. \n\nThe user can control which part is shown \nby operating two scrollbars. These will automatically hide \nor show themself when the user's cursor enters the viewport.\nThis behavior can be influenced by setting the \n[`ScrollbarVisibilityPolicy`](@ref) for one or both of the scrollbars.\n\n`Viewport` can be forced to obey the width and/or height \nof its child by setting [`set_propagate_natural_width!`](@ref) or\n[`set_propagate_natural_height!`](@ref) to `true`.\n\nThe placement of both scrollbars at the same time can be set with [`set_scrollbar_placement!`](@ref).\n\nConnect to the `value_changed` signal of each of the scrollbars [`Adjustment`](@ref)\nto react to the user scrolling the `Viewport`.\n\n$(@type_constructors(\n    Viewport()\n))\n\n$(@type_signals(Viewport, \n    scroll_child\n))\n\n$(@type_fields())\n\"\"\"\n\n@document Widget abstract_type_docs(Widget, Any, \"\"\"\n# Widget <: SignalEmitter\n\nSuperclass of all renderable entities in Mousetrap. Like all\n[`SignalEmitter`](@ref)s, a widget's lifetime is managed automatically.\n\nWidgets have a large number of properties that influence their \nsize and position on screen. See the manual chapter on widgets \nfor more information.\n\nIn order for an object to be treated as a widget, it needs to subtype \nthis abstract type and define [`Mousetrap.get_top_level_widget`](@ref). See the \nmanual section on compound widgets in the chapter on widgets.\n\nAll widgets share the following signals, where `T` is the subclass \nof `Widget`. For example, signal `realize` of class `Label` has the \nsignature `(::Label, [::Data_t]) -> Nothing`:\n\n$(@type_signals(T,\n    realize,\n    unrealize,\n    destroy,\n    hide,\n    show,\n    map,\n    unmap\n))\n\n$(@type_fields())\n\"\"\")\n\n@document Window \"\"\"\n# Window <: Widget\n\nTop-level window, associated with an [`Application`](@ref). Has exactly one child, \nas well as a titlebar widget, which will usually be a [`HeaderBar`](@ref).\n\nWhen the user's window manager requests for a window to close,\nsignal `close_request` will be emitted, whose return value can \nprevent the window from closing.\n\n$(@type_constructors(\n    Window(app::Application)\n))\n\n$(@type_signals(Window, \n    close_request,\n    activate_default_widget,\n    activate_focused_widget\n))\n\n$(@type_fields())\n\n## Example\n```julia\nmain() do app::Application\n    window = Window(app)\n    present!(window)\nend\n```\n\"\"\"\n"
  },
  {
    "path": "src/docs.jl",
    "content": "#\n# Author: C. Cords (mail@clemens-cords.com)\n# GitHub: https://github.com/clemapfel/mousetrap.jl\n# Documentation: https://clemens-cords.com/mousetrap\n#\n# Copyright © 2023, Licensed under lGPL-3.0\n#\n\n## Classification\n\nconst widgets = Symbol[]\nconst event_controllers = Symbol[]\nconst signal_emitters = Symbol[]\nconst abstract_types = Symbol[]\nconst types = Symbol[]\nconst functions = Symbol[]\nconst enums = Symbol[]\nconst enum_values = Symbol[]\nconst key_codes = Symbol[]\nconst other = Symbol[]\nconst style_properties = Symbol[]\nconst style_targets = Symbol[]\nconst style_classes = Symbol[]\n\nfor n in names(Mousetrap)\n\n    binding = getproperty(Mousetrap, n)\n\n    if binding isa Type\n        if isabstracttype(binding)\n            push!(abstract_types, n)\n        elseif binding <: Widget\n            push!(widgets, n)\n        elseif binding <: EventController\n            push!(event_controllers, n)\n        elseif binding <: SignalEmitter\n            push!(signal_emitters, n)\n        elseif binding <: Int64\n            push!(enums, n)\n        else\n            push!(types, n)\n        end\n    elseif typeof(binding) <: Function\n       # if isnothing(match(r\".*_signal_.*\", string(binding))) # filter autogenerated signal functions\n            if string(n)[1] != `@` # filter macros\n                push!(functions, n)\n            end\n        #end\n    elseif occursin(\"STYLE_PROPERTY_\", string(n))\n        push!(style_properties, n)\n    elseif occursin(\"STYLE_TARGET_\", string(n))\n        push!(style_targets, n)\n    elseif occursin(\"STYLE_CLASS_\", string(n))\n        push!(style_classes, n)    \n    elseif occursin(\"KEY_\", string(n))\n        push!(key_codes, Symbol(string(n)[5:end])) # omit `KEY_`\n    elseif typeof(binding) <: Int64\n        push!(enum_values, n)\n    else\n        push!(other, n)\n    end\nend\n\n## Docs Common\n\nmacro document(name, string)\n    :(@doc $string $name)\nend\n\nfunction enum_docs(name, brief, values)\n    out = \"# $(name)\\n\"\n    out *= \"$(brief)\\n\"\n    out *= \"## Enum Values\\n\"\n\n    for value in values\n        out *= \"+ `$value`\\n\"\n    end\n\n    return out\nend\n\nmacro type_constructors(constructors...)\n    out = \"## Constructors\\n\"\n    if !isempty(constructors)\n        out *= \"```\\n\"\n        for constructor in constructors\n            out *= string(constructor) * \"\\n\"\n        end\n        out *= \"```\\n\"\n    else\n        out *= \"(no public constructors)\\n\"\n    end\n    return out\nend\n\nmacro type_fields()\n    out = \"## Fields\\n\"\n    out *= \"(no public fields)\\n\"   \n    return out\nend\n\nmacro type_fields(fields...)\n    out = \"## Fields\\n\"\n    if !isempty(fields)\n        for field in fields\n            out *= \"+ `$field`\\n\"\n        end\n    else\n        out *= \"(no public fields)\\n\"\n    end\n    return out\nend\n\nusing InteractiveUtils\n\nfunction abstract_type_docs(type_in, super_type, brief)\n\n    type = string(type_in)\n    out = \"$brief\\n\"\n    out *= \"## Supertype\\n`$super_type`\\n\"\n    out *= \"## Subtypes\\n\"\n\n    # get all subtypes and subtypes of subtypes\n    seen = Set()\n    subtypes = []\n    function aux(type, subtypes)\n        \n        if type in seen\n            return\n        else\n            push!(seen, type)\n        end\n\n        if isabstracttype(type)\n            for t in InteractiveUtils.subtypes(type)\n                aux(t, subtypes)\n            end\n        else\n            push!(subtypes, string(type))\n        end\n    end\n    aux(type_in, subtypes)\n\n    for type in sort(subtypes)\n        to_append = \"+ [`$type`](@ref)\\n\"\n        out *= replace(to_append, \"Mousetrap.\" => \"\")\n    end\n    return out\nend\n\n## include\n\ninclude(\"docgen/signals.jl\")\ninclude(\"docgen/functions.jl\")\ninclude(\"docgen/types.jl\")\ninclude(\"docgen/enums.jl\")\n\nmacro generate_signal_function_docs(snake_case)\n\n    out = Expr(:toplevel)\n\n    id = snake_case\n\n    signature = signal_descriptors[snake_case][1]\n    connect_signal_name = :connect_signal_ * snake_case * :!\n    \n    connect_signal_string = \"\"\"\n        ```\n        $connect_signal_name(f, ::T, [::Data_t]) -> Cvoid\n        ```\n        Connect to signal `$id`, where `T` is a signal emitter instance that supports this signal\n\n        `Data_t` is an optional argument, which, if specified, will be forwarded to the signal handler.\n        \n        `f` is required to be invocable as a function with signature:\n        ```\n        $signature\n        ```\n        Where `T` is the type of the signal emitter instance.\n        \"\"\"\n\n    push!(out.args, :(@document $connect_signal_name $connect_signal_string))\n\n    ###\n\n    emit_signal_name = :emit_signal_ * snake_case\n\n    return_t = match(r\" -> .*\", signature).match\n    arg_ts = signature[(match(r\"::T, \", signature).offset + 5):(match(r\", \\[::Data_t\\]\", signature).offset - 1)]\n\n    emit_signal_string = \"\"\"\n        ```\n        $emit_signal_name(::T, $arg_ts)$return_t\n        ```\n        Manually emit signal `$id`, where `T` is a signal emitter that supports this signal.\n        The arguments will forwarded to the signal handler.\n        \"\"\"\n   \n    emit_signal_name = :emit_signal_ * snake_case\n\n    push!(out.args, :(@document $emit_signal_name $emit_signal_string))\n\n    ### \n    \n    disconnect_signal_name = :disconnect_signal_ * snake_case * :!\n\n    disconnect_signal_string = \"\"\"\n        ```\n        $disconnect_signal_name(::T)\n        ```\n        Permanently disconnect a signal, where `T` is a signal emitter that supports signal `$id`. \n        \"\"\"\n\n    push!(out.args, :(@document $disconnect_signal_name $disconnect_signal_string))\n\n    ###\n\n    set_signal_blocked_name = :set_signal_ * snake_case * :_blocked * :!\n\n    set_signal_blocked_string = \"\"\"\n        ```\n        $set_signal_blocked_name(::T, ::Bool)\n        ```\n        If set to `true`, blocks emission of this signal until turned back on, where `T` is a signal emitter that supports signal `$id`.\n        \"\"\"\n\n    push!(out.args, :(@document $set_signal_blocked_name $set_signal_blocked_string))\n\n    ###\n\n    get_signal_blocked_name = :get_signal_ * snake_case * :_blocked\n\n    get_signal_blocked_string = \"\"\"\n        ```\n        $get_signal_blocked_name(::T) -> Bool\n        ```\n        Get whether the signal is currently blocked, where `T` is a signal emitter that supports signal `$id`.\n        \"\"\"\n\n    push!(out.args, :(@document $get_signal_blocked_name $get_signal_blocked_string))\n\n    return out\nend\n\nfor pair in signal_descriptors\n    id = pair[1]\n    eval(:(@generate_signal_function_docs $id))\nend\n\n@do_not_compile const _generate_function_docs = quote\n\n    for name in Mousetrap.functions\n\n        method_list = \"\"\n        method_table = methods(getproperty(Mousetrap, name))\n        for i in eachindex(method_table)\n            as_string = string(method_table[i])\n            method_list *= as_string[1:match(r\" \\@.*\", as_string).offset]\n            if i != length(method_table)\n                method_list *= \"\\n\"\n            end\n        end\n\n        println(\"\"\"\n        @document $name \\\"\\\"\\\"\n        ```\n        $method_list\n        ```\n        TODO\n        \\\"\\\"\\\"\n        \"\"\")\n    end\nend\n\n@do_not_compile const _generate_type_docs = quote\n    \n    for name in sort(union(\n        Mousetrap.types, \n        Mousetrap.signal_emitters, \n        Mousetrap.widgets, \n        Mousetrap.event_controllers, \n        Mousetrap.abstract_types    \n    ))\n\n        if name in Mousetrap.types\n            println(\"\"\"\n            @document $name \\\"\\\"\\\"\n                ## $name\n\n                TODO\n\n                \\$(@type_constructors(\n                ))\n\n                \\$(@type_fields(\n                ))\n            \\\"\\\"\\\"\n            \"\"\")\n        elseif name in Mousetrap.abstract_types\n            println(\"\"\"\n            @document $name abstract_type_docs($name, Any, \\\"\\\"\\\"\n                TODO\n            \\\"\\\"\\\")\n            \"\"\")            \n        else\n            super = \"\"\n\n            if name in Mousetrap.event_controllers\n                super = \"EventController\"\n            elseif name in Mousetrap.widgets\n                super = \"Widget\"\n            elseif name in Mousetrap.signal_emitters\n                super = \"SignalEmitter\"\n            else\n                continue\n            end\n            \n            println(\"\"\"\n            @document $name \\\"\\\"\\\"\n                ## $name <: $super\n\n                TODO\n\n                \\$(@type_constructors(\n                ))\n\n                \\$(@type_signals($name, \n                ))\n\n                \\$(@type_fields())\n            \\\"\\\"\\\"\n            \"\"\")\n        end\n    end\nend\n\n@do_not_compile const _generate_enum_docs = quote\n    for enum_name in Mousetrap.enums\n        enum = getproperty(Mousetrap, enum_name)\n        values = []\n        for value_name in Mousetrap.enum_values\n            if typeof(getproperty(Mousetrap, value_name)) <: enum\n                push!(values, value_name)\n            end\n        end\n\n        value_string = \"\"\n        for i in 1:length(values)\n            value_string *= \"    :\" * string(values[i])\n            if i != length(values)\n                value_string *= \",\"\n            end\n            value_string *= \"\\n\"\n        end\n\n        println(\"\"\"\n        @document $enum_name enum_docs(:$enum_name,\n            \"TODO\", [\n        $value_string])\"\"\")\n\n        for value in values\n           println(\"@document $value \\\"TODO\\\"\")\n        end\n\n        println()\n    end\nend"
  },
  {
    "path": "src/key_codes.jl",
    "content": "const KEY_VoidSymbol = detail.KEY_VoidSymbol\nexport KEY_VoidSymbol\n\nconst KEY_BackSpace = detail.KEY_BackSpace\nexport KEY_BackSpace\n\nconst KEY_Tab = detail.KEY_Tab\nexport KEY_Tab\n\nconst KEY_Linefeed = detail.KEY_Linefeed\nexport KEY_Linefeed\n\nconst KEY_Clear = detail.KEY_Clear\nexport KEY_Clear\n\nconst KEY_Return = detail.KEY_Return\nexport KEY_Return\n\nconst KEY_Pause = detail.KEY_Pause\nexport KEY_Pause\n\nconst KEY_Scroll_Lock = detail.KEY_Scroll_Lock\nexport KEY_Scroll_Lock\n\nconst KEY_Sys_Req = detail.KEY_Sys_Req\nexport KEY_Sys_Req\n\nconst KEY_Escape = detail.KEY_Escape\nexport KEY_Escape\n\nconst KEY_Delete = detail.KEY_Delete\nexport KEY_Delete\n\nconst KEY_Multi_key = detail.KEY_Multi_key\nexport KEY_Multi_key\n\nconst KEY_Codeinput = detail.KEY_Codeinput\nexport KEY_Codeinput\n\nconst KEY_SingleCandidate = detail.KEY_SingleCandidate\nexport KEY_SingleCandidate\n\nconst KEY_MultipleCandidate = detail.KEY_MultipleCandidate\nexport KEY_MultipleCandidate\n\nconst KEY_PreviousCandidate = detail.KEY_PreviousCandidate\nexport KEY_PreviousCandidate\n\nconst KEY_Kanji = detail.KEY_Kanji\nexport KEY_Kanji\n\nconst KEY_Muhenkan = detail.KEY_Muhenkan\nexport KEY_Muhenkan\n\nconst KEY_Henkan_Mode = detail.KEY_Henkan_Mode\nexport KEY_Henkan_Mode\n\nconst KEY_Henkan = detail.KEY_Henkan\nexport KEY_Henkan\n\nconst KEY_Romaji = detail.KEY_Romaji\nexport KEY_Romaji\n\nconst KEY_Hiragana = detail.KEY_Hiragana\nexport KEY_Hiragana\n\nconst KEY_Katakana = detail.KEY_Katakana\nexport KEY_Katakana\n\nconst KEY_Hiragana_Katakana = detail.KEY_Hiragana_Katakana\nexport KEY_Hiragana_Katakana\n\nconst KEY_Zenkaku = detail.KEY_Zenkaku\nexport KEY_Zenkaku\n\nconst KEY_Hankaku = detail.KEY_Hankaku\nexport KEY_Hankaku\n\nconst KEY_Zenkaku_Hankaku = detail.KEY_Zenkaku_Hankaku\nexport KEY_Zenkaku_Hankaku\n\nconst KEY_Touroku = detail.KEY_Touroku\nexport KEY_Touroku\n\nconst KEY_Massyo = detail.KEY_Massyo\nexport KEY_Massyo\n\nconst KEY_Kana_Lock = detail.KEY_Kana_Lock\nexport KEY_Kana_Lock\n\nconst KEY_Kana_Shift = detail.KEY_Kana_Shift\nexport KEY_Kana_Shift\n\nconst KEY_Eisu_Shift = detail.KEY_Eisu_Shift\nexport KEY_Eisu_Shift\n\nconst KEY_Eisu_toggle = detail.KEY_Eisu_toggle\nexport KEY_Eisu_toggle\n\nconst KEY_Kanji_Bangou = detail.KEY_Kanji_Bangou\nexport KEY_Kanji_Bangou\n\nconst KEY_Zen_Koho = detail.KEY_Zen_Koho\nexport KEY_Zen_Koho\n\nconst KEY_Mae_Koho = detail.KEY_Mae_Koho\nexport KEY_Mae_Koho\n\nconst KEY_Home = detail.KEY_Home\nexport KEY_Home\n\nconst KEY_Left = detail.KEY_Left\nexport KEY_Left\n\nconst KEY_Up = detail.KEY_Up\nexport KEY_Up\n\nconst KEY_Right = detail.KEY_Right\nexport KEY_Right\n\nconst KEY_Down = detail.KEY_Down\nexport KEY_Down\n\nconst KEY_Prior = detail.KEY_Prior\nexport KEY_Prior\n\nconst KEY_Page_Up = detail.KEY_Page_Up\nexport KEY_Page_Up\n\nconst KEY_Next = detail.KEY_Next\nexport KEY_Next\n\nconst KEY_Page_Down = detail.KEY_Page_Down\nexport KEY_Page_Down\n\nconst KEY_End = detail.KEY_End\nexport KEY_End\n\nconst KEY_Begin = detail.KEY_Begin\nexport KEY_Begin\n\nconst KEY_Select = detail.KEY_Select\nexport KEY_Select\n\nconst KEY_Print = detail.KEY_Print\nexport KEY_Print\n\nconst KEY_Execute = detail.KEY_Execute\nexport KEY_Execute\n\nconst KEY_Insert = detail.KEY_Insert\nexport KEY_Insert\n\nconst KEY_Undo = detail.KEY_Undo\nexport KEY_Undo\n\nconst KEY_Redo = detail.KEY_Redo\nexport KEY_Redo\n\nconst KEY_Menu = detail.KEY_Menu\nexport KEY_Menu\n\nconst KEY_Find = detail.KEY_Find\nexport KEY_Find\n\nconst KEY_Cancel = detail.KEY_Cancel\nexport KEY_Cancel\n\nconst KEY_Help = detail.KEY_Help\nexport KEY_Help\n\nconst KEY_Break = detail.KEY_Break\nexport KEY_Break\n\nconst KEY_Mode_switch = detail.KEY_Mode_switch\nexport KEY_Mode_switch\n\nconst KEY_script_switch = detail.KEY_script_switch\nexport KEY_script_switch\n\nconst KEY_Num_Lock = detail.KEY_Num_Lock\nexport KEY_Num_Lock\n\nconst KEY_KP_Space = detail.KEY_KP_Space\nexport KEY_KP_Space\n\nconst KEY_KP_Tab = detail.KEY_KP_Tab\nexport KEY_KP_Tab\n\nconst KEY_KP_Enter = detail.KEY_KP_Enter\nexport KEY_KP_Enter\n\nconst KEY_KP_F1 = detail.KEY_KP_F1\nexport KEY_KP_F1\n\nconst KEY_KP_F2 = detail.KEY_KP_F2\nexport KEY_KP_F2\n\nconst KEY_KP_F3 = detail.KEY_KP_F3\nexport KEY_KP_F3\n\nconst KEY_KP_F4 = detail.KEY_KP_F4\nexport KEY_KP_F4\n\nconst KEY_KP_Home = detail.KEY_KP_Home\nexport KEY_KP_Home\n\nconst KEY_KP_Left = detail.KEY_KP_Left\nexport KEY_KP_Left\n\nconst KEY_KP_Up = detail.KEY_KP_Up\nexport KEY_KP_Up\n\nconst KEY_KP_Right = detail.KEY_KP_Right\nexport KEY_KP_Right\n\nconst KEY_KP_Down = detail.KEY_KP_Down\nexport KEY_KP_Down\n\nconst KEY_KP_Prior = detail.KEY_KP_Prior\nexport KEY_KP_Prior\n\nconst KEY_KP_Page_Up = detail.KEY_KP_Page_Up\nexport KEY_KP_Page_Up\n\nconst KEY_KP_Next = detail.KEY_KP_Next\nexport KEY_KP_Next\n\nconst KEY_KP_Page_Down = detail.KEY_KP_Page_Down\nexport KEY_KP_Page_Down\n\nconst KEY_KP_End = detail.KEY_KP_End\nexport KEY_KP_End\n\nconst KEY_KP_Begin = detail.KEY_KP_Begin\nexport KEY_KP_Begin\n\nconst KEY_KP_Insert = detail.KEY_KP_Insert\nexport KEY_KP_Insert\n\nconst KEY_KP_Delete = detail.KEY_KP_Delete\nexport KEY_KP_Delete\n\nconst KEY_KP_Equal = detail.KEY_KP_Equal\nexport KEY_KP_Equal\n\nconst KEY_KP_Multiply = detail.KEY_KP_Multiply\nexport KEY_KP_Multiply\n\nconst KEY_KP_Add = detail.KEY_KP_Add\nexport KEY_KP_Add\n\nconst KEY_KP_Separator = detail.KEY_KP_Separator\nexport KEY_KP_Separator\n\nconst KEY_KP_Subtract = detail.KEY_KP_Subtract\nexport KEY_KP_Subtract\n\nconst KEY_KP_Decimal = detail.KEY_KP_Decimal\nexport KEY_KP_Decimal\n\nconst KEY_KP_Divide = detail.KEY_KP_Divide\nexport KEY_KP_Divide\n\nconst KEY_KP_0 = detail.KEY_KP_0\nexport KEY_KP_0\n\nconst KEY_KP_1 = detail.KEY_KP_1\nexport KEY_KP_1\n\nconst KEY_KP_2 = detail.KEY_KP_2\nexport KEY_KP_2\n\nconst KEY_KP_3 = detail.KEY_KP_3\nexport KEY_KP_3\n\nconst KEY_KP_4 = detail.KEY_KP_4\nexport KEY_KP_4\n\nconst KEY_KP_5 = detail.KEY_KP_5\nexport KEY_KP_5\n\nconst KEY_KP_6 = detail.KEY_KP_6\nexport KEY_KP_6\n\nconst KEY_KP_7 = detail.KEY_KP_7\nexport KEY_KP_7\n\nconst KEY_KP_8 = detail.KEY_KP_8\nexport KEY_KP_8\n\nconst KEY_KP_9 = detail.KEY_KP_9\nexport KEY_KP_9\n\nconst KEY_F1 = detail.KEY_F1\nexport KEY_F1\n\nconst KEY_F2 = detail.KEY_F2\nexport KEY_F2\n\nconst KEY_F3 = detail.KEY_F3\nexport KEY_F3\n\nconst KEY_F4 = detail.KEY_F4\nexport KEY_F4\n\nconst KEY_F5 = detail.KEY_F5\nexport KEY_F5\n\nconst KEY_F6 = detail.KEY_F6\nexport KEY_F6\n\nconst KEY_F7 = detail.KEY_F7\nexport KEY_F7\n\nconst KEY_F8 = detail.KEY_F8\nexport KEY_F8\n\nconst KEY_F9 = detail.KEY_F9\nexport KEY_F9\n\nconst KEY_F10 = detail.KEY_F10\nexport KEY_F10\n\nconst KEY_F11 = detail.KEY_F11\nexport KEY_F11\n\nconst KEY_L1 = detail.KEY_L1\nexport KEY_L1\n\nconst KEY_F12 = detail.KEY_F12\nexport KEY_F12\n\nconst KEY_L2 = detail.KEY_L2\nexport KEY_L2\n\nconst KEY_F13 = detail.KEY_F13\nexport KEY_F13\n\nconst KEY_L3 = detail.KEY_L3\nexport KEY_L3\n\nconst KEY_F14 = detail.KEY_F14\nexport KEY_F14\n\nconst KEY_L4 = detail.KEY_L4\nexport KEY_L4\n\nconst KEY_F15 = detail.KEY_F15\nexport KEY_F15\n\nconst KEY_L5 = detail.KEY_L5\nexport KEY_L5\n\nconst KEY_F16 = detail.KEY_F16\nexport KEY_F16\n\nconst KEY_L6 = detail.KEY_L6\nexport KEY_L6\n\nconst KEY_F17 = detail.KEY_F17\nexport KEY_F17\n\nconst KEY_L7 = detail.KEY_L7\nexport KEY_L7\n\nconst KEY_F18 = detail.KEY_F18\nexport KEY_F18\n\nconst KEY_L8 = detail.KEY_L8\nexport KEY_L8\n\nconst KEY_F19 = detail.KEY_F19\nexport KEY_F19\n\nconst KEY_L9 = detail.KEY_L9\nexport KEY_L9\n\nconst KEY_F20 = detail.KEY_F20\nexport KEY_F20\n\nconst KEY_L10 = detail.KEY_L10\nexport KEY_L10\n\nconst KEY_F21 = detail.KEY_F21\nexport KEY_F21\n\nconst KEY_R1 = detail.KEY_R1\nexport KEY_R1\n\nconst KEY_F22 = detail.KEY_F22\nexport KEY_F22\n\nconst KEY_R2 = detail.KEY_R2\nexport KEY_R2\n\nconst KEY_F23 = detail.KEY_F23\nexport KEY_F23\n\nconst KEY_R3 = detail.KEY_R3\nexport KEY_R3\n\nconst KEY_F24 = detail.KEY_F24\nexport KEY_F24\n\nconst KEY_R4 = detail.KEY_R4\nexport KEY_R4\n\nconst KEY_F25 = detail.KEY_F25\nexport KEY_F25\n\nconst KEY_R5 = detail.KEY_R5\nexport KEY_R5\n\nconst KEY_F26 = detail.KEY_F26\nexport KEY_F26\n\nconst KEY_R6 = detail.KEY_R6\nexport KEY_R6\n\nconst KEY_F27 = detail.KEY_F27\nexport KEY_F27\n\nconst KEY_R7 = detail.KEY_R7\nexport KEY_R7\n\nconst KEY_F28 = detail.KEY_F28\nexport KEY_F28\n\nconst KEY_R8 = detail.KEY_R8\nexport KEY_R8\n\nconst KEY_F29 = detail.KEY_F29\nexport KEY_F29\n\nconst KEY_R9 = detail.KEY_R9\nexport KEY_R9\n\nconst KEY_F30 = detail.KEY_F30\nexport KEY_F30\n\nconst KEY_R10 = detail.KEY_R10\nexport KEY_R10\n\nconst KEY_F31 = detail.KEY_F31\nexport KEY_F31\n\nconst KEY_R11 = detail.KEY_R11\nexport KEY_R11\n\nconst KEY_F32 = detail.KEY_F32\nexport KEY_F32\n\nconst KEY_R12 = detail.KEY_R12\nexport KEY_R12\n\nconst KEY_F33 = detail.KEY_F33\nexport KEY_F33\n\nconst KEY_R13 = detail.KEY_R13\nexport KEY_R13\n\nconst KEY_F34 = detail.KEY_F34\nexport KEY_F34\n\nconst KEY_R14 = detail.KEY_R14\nexport KEY_R14\n\nconst KEY_F35 = detail.KEY_F35\nexport KEY_F35\n\nconst KEY_R15 = detail.KEY_R15\nexport KEY_R15\n\nconst KEY_Shift_L = detail.KEY_Shift_L\nexport KEY_Shift_L\n\nconst KEY_Shift_R = detail.KEY_Shift_R\nexport KEY_Shift_R\n\nconst KEY_Control_L = detail.KEY_Control_L\nexport KEY_Control_L\n\nconst KEY_Control_R = detail.KEY_Control_R\nexport KEY_Control_R\n\nconst KEY_Caps_Lock = detail.KEY_Caps_Lock\nexport KEY_Caps_Lock\n\nconst KEY_Shift_Lock = detail.KEY_Shift_Lock\nexport KEY_Shift_Lock\n\nconst KEY_Meta_L = detail.KEY_Meta_L\nexport KEY_Meta_L\n\nconst KEY_Meta_R = detail.KEY_Meta_R\nexport KEY_Meta_R\n\nconst KEY_Alt_L = detail.KEY_Alt_L\nexport KEY_Alt_L\n\nconst KEY_Alt_R = detail.KEY_Alt_R\nexport KEY_Alt_R\n\nconst KEY_Super_L = detail.KEY_Super_L\nexport KEY_Super_L\n\nconst KEY_Super_R = detail.KEY_Super_R\nexport KEY_Super_R\n\nconst KEY_Hyper_L = detail.KEY_Hyper_L\nexport KEY_Hyper_L\n\nconst KEY_Hyper_R = detail.KEY_Hyper_R\nexport KEY_Hyper_R\n\nconst KEY_ISO_Lock = detail.KEY_ISO_Lock\nexport KEY_ISO_Lock\n\nconst KEY_ISO_Level2_Latch = detail.KEY_ISO_Level2_Latch\nexport KEY_ISO_Level2_Latch\n\nconst KEY_ISO_Level3_Shift = detail.KEY_ISO_Level3_Shift\nexport KEY_ISO_Level3_Shift\n\nconst KEY_ISO_Level3_Latch = detail.KEY_ISO_Level3_Latch\nexport KEY_ISO_Level3_Latch\n\nconst KEY_ISO_Level3_Lock = detail.KEY_ISO_Level3_Lock\nexport KEY_ISO_Level3_Lock\n\nconst KEY_ISO_Level5_Shift = detail.KEY_ISO_Level5_Shift\nexport KEY_ISO_Level5_Shift\n\nconst KEY_ISO_Level5_Latch = detail.KEY_ISO_Level5_Latch\nexport KEY_ISO_Level5_Latch\n\nconst KEY_ISO_Level5_Lock = detail.KEY_ISO_Level5_Lock\nexport KEY_ISO_Level5_Lock\n\nconst KEY_ISO_Group_Shift = detail.KEY_ISO_Group_Shift\nexport KEY_ISO_Group_Shift\n\nconst KEY_ISO_Group_Latch = detail.KEY_ISO_Group_Latch\nexport KEY_ISO_Group_Latch\n\nconst KEY_ISO_Group_Lock = detail.KEY_ISO_Group_Lock\nexport KEY_ISO_Group_Lock\n\nconst KEY_ISO_Next_Group = detail.KEY_ISO_Next_Group\nexport KEY_ISO_Next_Group\n\nconst KEY_ISO_Next_Group_Lock = detail.KEY_ISO_Next_Group_Lock\nexport KEY_ISO_Next_Group_Lock\n\nconst KEY_ISO_Prev_Group = detail.KEY_ISO_Prev_Group\nexport KEY_ISO_Prev_Group\n\nconst KEY_ISO_Prev_Group_Lock = detail.KEY_ISO_Prev_Group_Lock\nexport KEY_ISO_Prev_Group_Lock\n\nconst KEY_ISO_First_Group = detail.KEY_ISO_First_Group\nexport KEY_ISO_First_Group\n\nconst KEY_ISO_First_Group_Lock = detail.KEY_ISO_First_Group_Lock\nexport KEY_ISO_First_Group_Lock\n\nconst KEY_ISO_Last_Group = detail.KEY_ISO_Last_Group\nexport KEY_ISO_Last_Group\n\nconst KEY_ISO_Last_Group_Lock = detail.KEY_ISO_Last_Group_Lock\nexport KEY_ISO_Last_Group_Lock\n\nconst KEY_ISO_Left_Tab = detail.KEY_ISO_Left_Tab\nexport KEY_ISO_Left_Tab\n\nconst KEY_ISO_Move_Line_Up = detail.KEY_ISO_Move_Line_Up\nexport KEY_ISO_Move_Line_Up\n\nconst KEY_ISO_Move_Line_Down = detail.KEY_ISO_Move_Line_Down\nexport KEY_ISO_Move_Line_Down\n\nconst KEY_ISO_Partial_Line_Up = detail.KEY_ISO_Partial_Line_Up\nexport KEY_ISO_Partial_Line_Up\n\nconst KEY_ISO_Partial_Line_Down = detail.KEY_ISO_Partial_Line_Down\nexport KEY_ISO_Partial_Line_Down\n\nconst KEY_ISO_Partial_Space_Left = detail.KEY_ISO_Partial_Space_Left\nexport KEY_ISO_Partial_Space_Left\n\nconst KEY_ISO_Partial_Space_Right = detail.KEY_ISO_Partial_Space_Right\nexport KEY_ISO_Partial_Space_Right\n\nconst KEY_ISO_Set_Margin_Left = detail.KEY_ISO_Set_Margin_Left\nexport KEY_ISO_Set_Margin_Left\n\nconst KEY_ISO_Set_Margin_Right = detail.KEY_ISO_Set_Margin_Right\nexport KEY_ISO_Set_Margin_Right\n\nconst KEY_ISO_Release_Margin_Left = detail.KEY_ISO_Release_Margin_Left\nexport KEY_ISO_Release_Margin_Left\n\nconst KEY_ISO_Release_Margin_Right = detail.KEY_ISO_Release_Margin_Right\nexport KEY_ISO_Release_Margin_Right\n\nconst KEY_ISO_Release_Both_Margins = detail.KEY_ISO_Release_Both_Margins\nexport KEY_ISO_Release_Both_Margins\n\nconst KEY_ISO_Fast_Cursor_Left = detail.KEY_ISO_Fast_Cursor_Left\nexport KEY_ISO_Fast_Cursor_Left\n\nconst KEY_ISO_Fast_Cursor_Right = detail.KEY_ISO_Fast_Cursor_Right\nexport KEY_ISO_Fast_Cursor_Right\n\nconst KEY_ISO_Fast_Cursor_Up = detail.KEY_ISO_Fast_Cursor_Up\nexport KEY_ISO_Fast_Cursor_Up\n\nconst KEY_ISO_Fast_Cursor_Down = detail.KEY_ISO_Fast_Cursor_Down\nexport KEY_ISO_Fast_Cursor_Down\n\nconst KEY_ISO_Continuous_Underline = detail.KEY_ISO_Continuous_Underline\nexport KEY_ISO_Continuous_Underline\n\nconst KEY_ISO_Discontinuous_Underline = detail.KEY_ISO_Discontinuous_Underline\nexport KEY_ISO_Discontinuous_Underline\n\nconst KEY_ISO_Emphasize = detail.KEY_ISO_Emphasize\nexport KEY_ISO_Emphasize\n\nconst KEY_ISO_Center_Object = detail.KEY_ISO_Center_Object\nexport KEY_ISO_Center_Object\n\nconst KEY_ISO_Enter = detail.KEY_ISO_Enter\nexport KEY_ISO_Enter\n\nconst KEY_dead_grave = detail.KEY_dead_grave\nexport KEY_dead_grave\n\nconst KEY_dead_acute = detail.KEY_dead_acute\nexport KEY_dead_acute\n\nconst KEY_dead_circumflex = detail.KEY_dead_circumflex\nexport KEY_dead_circumflex\n\nconst KEY_dead_tilde = detail.KEY_dead_tilde\nexport KEY_dead_tilde\n\nconst KEY_dead_perispomeni = detail.KEY_dead_perispomeni\nexport KEY_dead_perispomeni\n\nconst KEY_dead_macron = detail.KEY_dead_macron\nexport KEY_dead_macron\n\nconst KEY_dead_breve = detail.KEY_dead_breve\nexport KEY_dead_breve\n\nconst KEY_dead_abovedot = detail.KEY_dead_abovedot\nexport KEY_dead_abovedot\n\nconst KEY_dead_diaeresis = detail.KEY_dead_diaeresis\nexport KEY_dead_diaeresis\n\nconst KEY_dead_abovering = detail.KEY_dead_abovering\nexport KEY_dead_abovering\n\nconst KEY_dead_doubleacute = detail.KEY_dead_doubleacute\nexport KEY_dead_doubleacute\n\nconst KEY_dead_caron = detail.KEY_dead_caron\nexport KEY_dead_caron\n\nconst KEY_dead_cedilla = detail.KEY_dead_cedilla\nexport KEY_dead_cedilla\n\nconst KEY_dead_ogonek = detail.KEY_dead_ogonek\nexport KEY_dead_ogonek\n\nconst KEY_dead_iota = detail.KEY_dead_iota\nexport KEY_dead_iota\n\nconst KEY_dead_voiced_sound = detail.KEY_dead_voiced_sound\nexport KEY_dead_voiced_sound\n\nconst KEY_dead_semivoiced_sound = detail.KEY_dead_semivoiced_sound\nexport KEY_dead_semivoiced_sound\n\nconst KEY_dead_belowdot = detail.KEY_dead_belowdot\nexport KEY_dead_belowdot\n\nconst KEY_dead_hook = detail.KEY_dead_hook\nexport KEY_dead_hook\n\nconst KEY_dead_horn = detail.KEY_dead_horn\nexport KEY_dead_horn\n\nconst KEY_dead_stroke = detail.KEY_dead_stroke\nexport KEY_dead_stroke\n\nconst KEY_dead_abovecomma = detail.KEY_dead_abovecomma\nexport KEY_dead_abovecomma\n\nconst KEY_dead_psili = detail.KEY_dead_psili\nexport KEY_dead_psili\n\nconst KEY_dead_abovereversedcomma = detail.KEY_dead_abovereversedcomma\nexport KEY_dead_abovereversedcomma\n\nconst KEY_dead_dasia = detail.KEY_dead_dasia\nexport KEY_dead_dasia\n\nconst KEY_dead_doublegrave = detail.KEY_dead_doublegrave\nexport KEY_dead_doublegrave\n\nconst KEY_dead_belowring = detail.KEY_dead_belowring\nexport KEY_dead_belowring\n\nconst KEY_dead_belowmacron = detail.KEY_dead_belowmacron\nexport KEY_dead_belowmacron\n\nconst KEY_dead_belowcircumflex = detail.KEY_dead_belowcircumflex\nexport KEY_dead_belowcircumflex\n\nconst KEY_dead_belowtilde = detail.KEY_dead_belowtilde\nexport KEY_dead_belowtilde\n\nconst KEY_dead_belowbreve = detail.KEY_dead_belowbreve\nexport KEY_dead_belowbreve\n\nconst KEY_dead_belowdiaeresis = detail.KEY_dead_belowdiaeresis\nexport KEY_dead_belowdiaeresis\n\nconst KEY_dead_invertedbreve = detail.KEY_dead_invertedbreve\nexport KEY_dead_invertedbreve\n\nconst KEY_dead_belowcomma = detail.KEY_dead_belowcomma\nexport KEY_dead_belowcomma\n\nconst KEY_dead_currency = detail.KEY_dead_currency\nexport KEY_dead_currency\n\nconst KEY_dead_lowline = detail.KEY_dead_lowline\nexport KEY_dead_lowline\n\nconst KEY_dead_aboveverticalline = detail.KEY_dead_aboveverticalline\nexport KEY_dead_aboveverticalline\n\nconst KEY_dead_belowverticalline = detail.KEY_dead_belowverticalline\nexport KEY_dead_belowverticalline\n\nconst KEY_dead_longsolidusoverlay = detail.KEY_dead_longsolidusoverlay\nexport KEY_dead_longsolidusoverlay\n\nconst KEY_dead_a = detail.KEY_dead_a\nexport KEY_dead_a\n\nconst KEY_dead_A = detail.KEY_dead_A\nexport KEY_dead_A\n\nconst KEY_dead_e = detail.KEY_dead_e\nexport KEY_dead_e\n\nconst KEY_dead_E = detail.KEY_dead_E\nexport KEY_dead_E\n\nconst KEY_dead_i = detail.KEY_dead_i\nexport KEY_dead_i\n\nconst KEY_dead_I = detail.KEY_dead_I\nexport KEY_dead_I\n\nconst KEY_dead_o = detail.KEY_dead_o\nexport KEY_dead_o\n\nconst KEY_dead_O = detail.KEY_dead_O\nexport KEY_dead_O\n\nconst KEY_dead_u = detail.KEY_dead_u\nexport KEY_dead_u\n\nconst KEY_dead_U = detail.KEY_dead_U\nexport KEY_dead_U\n\nconst KEY_dead_small_schwa = detail.KEY_dead_small_schwa\nexport KEY_dead_small_schwa\n\nconst KEY_dead_capital_schwa = detail.KEY_dead_capital_schwa\nexport KEY_dead_capital_schwa\n\nconst KEY_dead_greek = detail.KEY_dead_greek\nexport KEY_dead_greek\n\nconst KEY_First_Virtual_Screen = detail.KEY_First_Virtual_Screen\nexport KEY_First_Virtual_Screen\n\nconst KEY_Prev_Virtual_Screen = detail.KEY_Prev_Virtual_Screen\nexport KEY_Prev_Virtual_Screen\n\nconst KEY_Next_Virtual_Screen = detail.KEY_Next_Virtual_Screen\nexport KEY_Next_Virtual_Screen\n\nconst KEY_Last_Virtual_Screen = detail.KEY_Last_Virtual_Screen\nexport KEY_Last_Virtual_Screen\n\nconst KEY_Terminate_Server = detail.KEY_Terminate_Server\nexport KEY_Terminate_Server\n\nconst KEY_AccessX_Enable = detail.KEY_AccessX_Enable\nexport KEY_AccessX_Enable\n\nconst KEY_AccessX_Feedback_Enable = detail.KEY_AccessX_Feedback_Enable\nexport KEY_AccessX_Feedback_Enable\n\nconst KEY_RepeatKeys_Enable = detail.KEY_RepeatKeys_Enable\nexport KEY_RepeatKeys_Enable\n\nconst KEY_SlowKeys_Enable = detail.KEY_SlowKeys_Enable\nexport KEY_SlowKeys_Enable\n\nconst KEY_BounceKeys_Enable = detail.KEY_BounceKeys_Enable\nexport KEY_BounceKeys_Enable\n\nconst KEY_StickyKeys_Enable = detail.KEY_StickyKeys_Enable\nexport KEY_StickyKeys_Enable\n\nconst KEY_MouseKeys_Enable = detail.KEY_MouseKeys_Enable\nexport KEY_MouseKeys_Enable\n\nconst KEY_MouseKeys_Accel_Enable = detail.KEY_MouseKeys_Accel_Enable\nexport KEY_MouseKeys_Accel_Enable\n\nconst KEY_Overlay1_Enable = detail.KEY_Overlay1_Enable\nexport KEY_Overlay1_Enable\n\nconst KEY_Overlay2_Enable = detail.KEY_Overlay2_Enable\nexport KEY_Overlay2_Enable\n\nconst KEY_AudibleBell_Enable = detail.KEY_AudibleBell_Enable\nexport KEY_AudibleBell_Enable\n\nconst KEY_Pointer_Left = detail.KEY_Pointer_Left\nexport KEY_Pointer_Left\n\nconst KEY_Pointer_Right = detail.KEY_Pointer_Right\nexport KEY_Pointer_Right\n\nconst KEY_Pointer_Up = detail.KEY_Pointer_Up\nexport KEY_Pointer_Up\n\nconst KEY_Pointer_Down = detail.KEY_Pointer_Down\nexport KEY_Pointer_Down\n\nconst KEY_Pointer_UpLeft = detail.KEY_Pointer_UpLeft\nexport KEY_Pointer_UpLeft\n\nconst KEY_Pointer_UpRight = detail.KEY_Pointer_UpRight\nexport KEY_Pointer_UpRight\n\nconst KEY_Pointer_DownLeft = detail.KEY_Pointer_DownLeft\nexport KEY_Pointer_DownLeft\n\nconst KEY_Pointer_DownRight = detail.KEY_Pointer_DownRight\nexport KEY_Pointer_DownRight\n\nconst KEY_Pointer_Button_Dflt = detail.KEY_Pointer_Button_Dflt\nexport KEY_Pointer_Button_Dflt\n\nconst KEY_Pointer_Button1 = detail.KEY_Pointer_Button1\nexport KEY_Pointer_Button1\n\nconst KEY_Pointer_Button2 = detail.KEY_Pointer_Button2\nexport KEY_Pointer_Button2\n\nconst KEY_Pointer_Button3 = detail.KEY_Pointer_Button3\nexport KEY_Pointer_Button3\n\nconst KEY_Pointer_Button4 = detail.KEY_Pointer_Button4\nexport KEY_Pointer_Button4\n\nconst KEY_Pointer_Button5 = detail.KEY_Pointer_Button5\nexport KEY_Pointer_Button5\n\nconst KEY_Pointer_DblClick_Dflt = detail.KEY_Pointer_DblClick_Dflt\nexport KEY_Pointer_DblClick_Dflt\n\nconst KEY_Pointer_DblClick1 = detail.KEY_Pointer_DblClick1\nexport KEY_Pointer_DblClick1\n\nconst KEY_Pointer_DblClick2 = detail.KEY_Pointer_DblClick2\nexport KEY_Pointer_DblClick2\n\nconst KEY_Pointer_DblClick3 = detail.KEY_Pointer_DblClick3\nexport KEY_Pointer_DblClick3\n\nconst KEY_Pointer_DblClick4 = detail.KEY_Pointer_DblClick4\nexport KEY_Pointer_DblClick4\n\nconst KEY_Pointer_DblClick5 = detail.KEY_Pointer_DblClick5\nexport KEY_Pointer_DblClick5\n\nconst KEY_Pointer_Drag_Dflt = detail.KEY_Pointer_Drag_Dflt\nexport KEY_Pointer_Drag_Dflt\n\nconst KEY_Pointer_Drag1 = detail.KEY_Pointer_Drag1\nexport KEY_Pointer_Drag1\n\nconst KEY_Pointer_Drag2 = detail.KEY_Pointer_Drag2\nexport KEY_Pointer_Drag2\n\nconst KEY_Pointer_Drag3 = detail.KEY_Pointer_Drag3\nexport KEY_Pointer_Drag3\n\nconst KEY_Pointer_Drag4 = detail.KEY_Pointer_Drag4\nexport KEY_Pointer_Drag4\n\nconst KEY_Pointer_Drag5 = detail.KEY_Pointer_Drag5\nexport KEY_Pointer_Drag5\n\nconst KEY_Pointer_EnableKeys = detail.KEY_Pointer_EnableKeys\nexport KEY_Pointer_EnableKeys\n\nconst KEY_Pointer_Accelerate = detail.KEY_Pointer_Accelerate\nexport KEY_Pointer_Accelerate\n\nconst KEY_Pointer_DfltBtnNext = detail.KEY_Pointer_DfltBtnNext\nexport KEY_Pointer_DfltBtnNext\n\nconst KEY_Pointer_DfltBtnPrev = detail.KEY_Pointer_DfltBtnPrev\nexport KEY_Pointer_DfltBtnPrev\n\nconst KEY_ch = detail.KEY_ch\nexport KEY_ch\n\nconst KEY_Ch = detail.KEY_Ch\nexport KEY_Ch\n\nconst KEY_CH = detail.KEY_CH\nexport KEY_CH\n\nconst KEY_c_h = detail.KEY_c_h\nexport KEY_c_h\n\nconst KEY_C_h = detail.KEY_C_h\nexport KEY_C_h\n\nconst KEY_C_H = detail.KEY_C_H\nexport KEY_C_H\n\nconst KEY_3270_Duplicate = detail.KEY_3270_Duplicate\nexport KEY_3270_Duplicate\n\nconst KEY_3270_FieldMark = detail.KEY_3270_FieldMark\nexport KEY_3270_FieldMark\n\nconst KEY_3270_Right2 = detail.KEY_3270_Right2\nexport KEY_3270_Right2\n\nconst KEY_3270_Left2 = detail.KEY_3270_Left2\nexport KEY_3270_Left2\n\nconst KEY_3270_BackTab = detail.KEY_3270_BackTab\nexport KEY_3270_BackTab\n\nconst KEY_3270_EraseEOF = detail.KEY_3270_EraseEOF\nexport KEY_3270_EraseEOF\n\nconst KEY_3270_EraseInput = detail.KEY_3270_EraseInput\nexport KEY_3270_EraseInput\n\nconst KEY_3270_Reset = detail.KEY_3270_Reset\nexport KEY_3270_Reset\n\nconst KEY_3270_Quit = detail.KEY_3270_Quit\nexport KEY_3270_Quit\n\nconst KEY_3270_PA1 = detail.KEY_3270_PA1\nexport KEY_3270_PA1\n\nconst KEY_3270_PA2 = detail.KEY_3270_PA2\nexport KEY_3270_PA2\n\nconst KEY_3270_PA3 = detail.KEY_3270_PA3\nexport KEY_3270_PA3\n\nconst KEY_3270_Test = detail.KEY_3270_Test\nexport KEY_3270_Test\n\nconst KEY_3270_Attn = detail.KEY_3270_Attn\nexport KEY_3270_Attn\n\nconst KEY_3270_CursorBlink = detail.KEY_3270_CursorBlink\nexport KEY_3270_CursorBlink\n\nconst KEY_3270_AltCursor = detail.KEY_3270_AltCursor\nexport KEY_3270_AltCursor\n\nconst KEY_3270_KeyClick = detail.KEY_3270_KeyClick\nexport KEY_3270_KeyClick\n\nconst KEY_3270_Jump = detail.KEY_3270_Jump\nexport KEY_3270_Jump\n\nconst KEY_3270_Ident = detail.KEY_3270_Ident\nexport KEY_3270_Ident\n\nconst KEY_3270_Rule = detail.KEY_3270_Rule\nexport KEY_3270_Rule\n\nconst KEY_3270_Copy = detail.KEY_3270_Copy\nexport KEY_3270_Copy\n\nconst KEY_3270_Play = detail.KEY_3270_Play\nexport KEY_3270_Play\n\nconst KEY_3270_Setup = detail.KEY_3270_Setup\nexport KEY_3270_Setup\n\nconst KEY_3270_Record = detail.KEY_3270_Record\nexport KEY_3270_Record\n\nconst KEY_3270_ChangeScreen = detail.KEY_3270_ChangeScreen\nexport KEY_3270_ChangeScreen\n\nconst KEY_3270_DeleteWord = detail.KEY_3270_DeleteWord\nexport KEY_3270_DeleteWord\n\nconst KEY_3270_ExSelect = detail.KEY_3270_ExSelect\nexport KEY_3270_ExSelect\n\nconst KEY_3270_CursorSelect = detail.KEY_3270_CursorSelect\nexport KEY_3270_CursorSelect\n\nconst KEY_3270_PrintScreen = detail.KEY_3270_PrintScreen\nexport KEY_3270_PrintScreen\n\nconst KEY_3270_Enter = detail.KEY_3270_Enter\nexport KEY_3270_Enter\n\nconst KEY_space = detail.KEY_space\nexport KEY_space\n\nconst KEY_exclam = detail.KEY_exclam\nexport KEY_exclam\n\nconst KEY_quotedbl = detail.KEY_quotedbl\nexport KEY_quotedbl\n\nconst KEY_numbersign = detail.KEY_numbersign\nexport KEY_numbersign\n\nconst KEY_dollar = detail.KEY_dollar\nexport KEY_dollar\n\nconst KEY_percent = detail.KEY_percent\nexport KEY_percent\n\nconst KEY_ampersand = detail.KEY_ampersand\nexport KEY_ampersand\n\nconst KEY_apostrophe = detail.KEY_apostrophe\nexport KEY_apostrophe\n\nconst KEY_quoteright = detail.KEY_quoteright\nexport KEY_quoteright\n\nconst KEY_parenleft = detail.KEY_parenleft\nexport KEY_parenleft\n\nconst KEY_parenright = detail.KEY_parenright\nexport KEY_parenright\n\nconst KEY_asterisk = detail.KEY_asterisk\nexport KEY_asterisk\n\nconst KEY_plus = detail.KEY_plus\nexport KEY_plus\n\nconst KEY_comma = detail.KEY_comma\nexport KEY_comma\n\nconst KEY_minus = detail.KEY_minus\nexport KEY_minus\n\nconst KEY_period = detail.KEY_period\nexport KEY_period\n\nconst KEY_slash = detail.KEY_slash\nexport KEY_slash\n\nconst KEY_0 = detail.KEY_0\nexport KEY_0\n\nconst KEY_1 = detail.KEY_1\nexport KEY_1\n\nconst KEY_2 = detail.KEY_2\nexport KEY_2\n\nconst KEY_3 = detail.KEY_3\nexport KEY_3\n\nconst KEY_4 = detail.KEY_4\nexport KEY_4\n\nconst KEY_5 = detail.KEY_5\nexport KEY_5\n\nconst KEY_6 = detail.KEY_6\nexport KEY_6\n\nconst KEY_7 = detail.KEY_7\nexport KEY_7\n\nconst KEY_8 = detail.KEY_8\nexport KEY_8\n\nconst KEY_9 = detail.KEY_9\nexport KEY_9\n\nconst KEY_colon = detail.KEY_colon\nexport KEY_colon\n\nconst KEY_semicolon = detail.KEY_semicolon\nexport KEY_semicolon\n\nconst KEY_less = detail.KEY_less\nexport KEY_less\n\nconst KEY_equal = detail.KEY_equal\nexport KEY_equal\n\nconst KEY_greater = detail.KEY_greater\nexport KEY_greater\n\nconst KEY_question = detail.KEY_question\nexport KEY_question\n\nconst KEY_at = detail.KEY_at\nexport KEY_at\n\nconst KEY_A = detail.KEY_A\nexport KEY_A\n\nconst KEY_B = detail.KEY_B\nexport KEY_B\n\nconst KEY_C = detail.KEY_C\nexport KEY_C\n\nconst KEY_D = detail.KEY_D\nexport KEY_D\n\nconst KEY_E = detail.KEY_E\nexport KEY_E\n\nconst KEY_F = detail.KEY_F\nexport KEY_F\n\nconst KEY_G = detail.KEY_G\nexport KEY_G\n\nconst KEY_H = detail.KEY_H\nexport KEY_H\n\nconst KEY_I = detail.KEY_I\nexport KEY_I\n\nconst KEY_J = detail.KEY_J\nexport KEY_J\n\nconst KEY_K = detail.KEY_K\nexport KEY_K\n\nconst KEY_L = detail.KEY_L\nexport KEY_L\n\nconst KEY_M = detail.KEY_M\nexport KEY_M\n\nconst KEY_N = detail.KEY_N\nexport KEY_N\n\nconst KEY_O = detail.KEY_O\nexport KEY_O\n\nconst KEY_P = detail.KEY_P\nexport KEY_P\n\nconst KEY_Q = detail.KEY_Q\nexport KEY_Q\n\nconst KEY_R = detail.KEY_R\nexport KEY_R\n\nconst KEY_S = detail.KEY_S\nexport KEY_S\n\nconst KEY_T = detail.KEY_T\nexport KEY_T\n\nconst KEY_U = detail.KEY_U\nexport KEY_U\n\nconst KEY_V = detail.KEY_V\nexport KEY_V\n\nconst KEY_W = detail.KEY_W\nexport KEY_W\n\nconst KEY_X = detail.KEY_X\nexport KEY_X\n\nconst KEY_Y = detail.KEY_Y\nexport KEY_Y\n\nconst KEY_Z = detail.KEY_Z\nexport KEY_Z\n\nconst KEY_bracketleft = detail.KEY_bracketleft\nexport KEY_bracketleft\n\nconst KEY_backslash = detail.KEY_backslash\nexport KEY_backslash\n\nconst KEY_bracketright = detail.KEY_bracketright\nexport KEY_bracketright\n\nconst KEY_asciicircum = detail.KEY_asciicircum\nexport KEY_asciicircum\n\nconst KEY_underscore = detail.KEY_underscore\nexport KEY_underscore\n\nconst KEY_grave = detail.KEY_grave\nexport KEY_grave\n\nconst KEY_quoteleft = detail.KEY_quoteleft\nexport KEY_quoteleft\n\nconst KEY_a = detail.KEY_a\nexport KEY_a\n\nconst KEY_b = detail.KEY_b\nexport KEY_b\n\nconst KEY_c = detail.KEY_c\nexport KEY_c\n\nconst KEY_d = detail.KEY_d\nexport KEY_d\n\nconst KEY_e = detail.KEY_e\nexport KEY_e\n\nconst KEY_f = detail.KEY_f\nexport KEY_f\n\nconst KEY_g = detail.KEY_g\nexport KEY_g\n\nconst KEY_h = detail.KEY_h\nexport KEY_h\n\nconst KEY_i = detail.KEY_i\nexport KEY_i\n\nconst KEY_j = detail.KEY_j\nexport KEY_j\n\nconst KEY_k = detail.KEY_k\nexport KEY_k\n\nconst KEY_l = detail.KEY_l\nexport KEY_l\n\nconst KEY_m = detail.KEY_m\nexport KEY_m\n\nconst KEY_n = detail.KEY_n\nexport KEY_n\n\nconst KEY_o = detail.KEY_o\nexport KEY_o\n\nconst KEY_p = detail.KEY_p\nexport KEY_p\n\nconst KEY_q = detail.KEY_q\nexport KEY_q\n\nconst KEY_r = detail.KEY_r\nexport KEY_r\n\nconst KEY_s = detail.KEY_s\nexport KEY_s\n\nconst KEY_t = detail.KEY_t\nexport KEY_t\n\nconst KEY_u = detail.KEY_u\nexport KEY_u\n\nconst KEY_v = detail.KEY_v\nexport KEY_v\n\nconst KEY_w = detail.KEY_w\nexport KEY_w\n\nconst KEY_x = detail.KEY_x\nexport KEY_x\n\nconst KEY_y = detail.KEY_y\nexport KEY_y\n\nconst KEY_z = detail.KEY_z\nexport KEY_z\n\nconst KEY_braceleft = detail.KEY_braceleft\nexport KEY_braceleft\n\nconst KEY_bar = detail.KEY_bar\nexport KEY_bar\n\nconst KEY_braceright = detail.KEY_braceright\nexport KEY_braceright\n\nconst KEY_asciitilde = detail.KEY_asciitilde\nexport KEY_asciitilde\n\nconst KEY_nobreakspace = detail.KEY_nobreakspace\nexport KEY_nobreakspace\n\nconst KEY_exclamdown = detail.KEY_exclamdown\nexport KEY_exclamdown\n\nconst KEY_cent = detail.KEY_cent\nexport KEY_cent\n\nconst KEY_sterling = detail.KEY_sterling\nexport KEY_sterling\n\nconst KEY_currency = detail.KEY_currency\nexport KEY_currency\n\nconst KEY_yen = detail.KEY_yen\nexport KEY_yen\n\nconst KEY_brokenbar = detail.KEY_brokenbar\nexport KEY_brokenbar\n\nconst KEY_section = detail.KEY_section\nexport KEY_section\n\nconst KEY_diaeresis = detail.KEY_diaeresis\nexport KEY_diaeresis\n\nconst KEY_copyright = detail.KEY_copyright\nexport KEY_copyright\n\nconst KEY_ordfeminine = detail.KEY_ordfeminine\nexport KEY_ordfeminine\n\nconst KEY_guillemotleft = detail.KEY_guillemotleft\nexport KEY_guillemotleft\n\nconst KEY_notsign = detail.KEY_notsign\nexport KEY_notsign\n\nconst KEY_hyphen = detail.KEY_hyphen\nexport KEY_hyphen\n\nconst KEY_registered = detail.KEY_registered\nexport KEY_registered\n\nconst KEY_macron = detail.KEY_macron\nexport KEY_macron\n\nconst KEY_degree = detail.KEY_degree\nexport KEY_degree\n\nconst KEY_plusminus = detail.KEY_plusminus\nexport KEY_plusminus\n\nconst KEY_twosuperior = detail.KEY_twosuperior\nexport KEY_twosuperior\n\nconst KEY_threesuperior = detail.KEY_threesuperior\nexport KEY_threesuperior\n\nconst KEY_acute = detail.KEY_acute\nexport KEY_acute\n\nconst KEY_mu = detail.KEY_mu\nexport KEY_mu\n\nconst KEY_paragraph = detail.KEY_paragraph\nexport KEY_paragraph\n\nconst KEY_periodcentered = detail.KEY_periodcentered\nexport KEY_periodcentered\n\nconst KEY_cedilla = detail.KEY_cedilla\nexport KEY_cedilla\n\nconst KEY_onesuperior = detail.KEY_onesuperior\nexport KEY_onesuperior\n\nconst KEY_masculine = detail.KEY_masculine\nexport KEY_masculine\n\nconst KEY_guillemotright = detail.KEY_guillemotright\nexport KEY_guillemotright\n\nconst KEY_onequarter = detail.KEY_onequarter\nexport KEY_onequarter\n\nconst KEY_onehalf = detail.KEY_onehalf\nexport KEY_onehalf\n\nconst KEY_threequarters = detail.KEY_threequarters\nexport KEY_threequarters\n\nconst KEY_questiondown = detail.KEY_questiondown\nexport KEY_questiondown\n\nconst KEY_Agrave = detail.KEY_Agrave\nexport KEY_Agrave\n\nconst KEY_Aacute = detail.KEY_Aacute\nexport KEY_Aacute\n\nconst KEY_Acircumflex = detail.KEY_Acircumflex\nexport KEY_Acircumflex\n\nconst KEY_Atilde = detail.KEY_Atilde\nexport KEY_Atilde\n\nconst KEY_Adiaeresis = detail.KEY_Adiaeresis\nexport KEY_Adiaeresis\n\nconst KEY_Aring = detail.KEY_Aring\nexport KEY_Aring\n\nconst KEY_AE = detail.KEY_AE\nexport KEY_AE\n\nconst KEY_Ccedilla = detail.KEY_Ccedilla\nexport KEY_Ccedilla\n\nconst KEY_Egrave = detail.KEY_Egrave\nexport KEY_Egrave\n\nconst KEY_Eacute = detail.KEY_Eacute\nexport KEY_Eacute\n\nconst KEY_Ecircumflex = detail.KEY_Ecircumflex\nexport KEY_Ecircumflex\n\nconst KEY_Ediaeresis = detail.KEY_Ediaeresis\nexport KEY_Ediaeresis\n\nconst KEY_Igrave = detail.KEY_Igrave\nexport KEY_Igrave\n\nconst KEY_Iacute = detail.KEY_Iacute\nexport KEY_Iacute\n\nconst KEY_Icircumflex = detail.KEY_Icircumflex\nexport KEY_Icircumflex\n\nconst KEY_Idiaeresis = detail.KEY_Idiaeresis\nexport KEY_Idiaeresis\n\nconst KEY_ETH = detail.KEY_ETH\nexport KEY_ETH\n\nconst KEY_Eth = detail.KEY_Eth\nexport KEY_Eth\n\nconst KEY_Ntilde = detail.KEY_Ntilde\nexport KEY_Ntilde\n\nconst KEY_Ograve = detail.KEY_Ograve\nexport KEY_Ograve\n\nconst KEY_Oacute = detail.KEY_Oacute\nexport KEY_Oacute\n\nconst KEY_Ocircumflex = detail.KEY_Ocircumflex\nexport KEY_Ocircumflex\n\nconst KEY_Otilde = detail.KEY_Otilde\nexport KEY_Otilde\n\nconst KEY_Odiaeresis = detail.KEY_Odiaeresis\nexport KEY_Odiaeresis\n\nconst KEY_multiply = detail.KEY_multiply\nexport KEY_multiply\n\nconst KEY_Oslash = detail.KEY_Oslash\nexport KEY_Oslash\n\nconst KEY_Ooblique = detail.KEY_Ooblique\nexport KEY_Ooblique\n\nconst KEY_Ugrave = detail.KEY_Ugrave\nexport KEY_Ugrave\n\nconst KEY_Uacute = detail.KEY_Uacute\nexport KEY_Uacute\n\nconst KEY_Ucircumflex = detail.KEY_Ucircumflex\nexport KEY_Ucircumflex\n\nconst KEY_Udiaeresis = detail.KEY_Udiaeresis\nexport KEY_Udiaeresis\n\nconst KEY_Yacute = detail.KEY_Yacute\nexport KEY_Yacute\n\nconst KEY_THORN = detail.KEY_THORN\nexport KEY_THORN\n\nconst KEY_Thorn = detail.KEY_Thorn\nexport KEY_Thorn\n\nconst KEY_ssharp = detail.KEY_ssharp\nexport KEY_ssharp\n\nconst KEY_agrave = detail.KEY_agrave\nexport KEY_agrave\n\nconst KEY_aacute = detail.KEY_aacute\nexport KEY_aacute\n\nconst KEY_acircumflex = detail.KEY_acircumflex\nexport KEY_acircumflex\n\nconst KEY_atilde = detail.KEY_atilde\nexport KEY_atilde\n\nconst KEY_adiaeresis = detail.KEY_adiaeresis\nexport KEY_adiaeresis\n\nconst KEY_aring = detail.KEY_aring\nexport KEY_aring\n\nconst KEY_ae = detail.KEY_ae\nexport KEY_ae\n\nconst KEY_ccedilla = detail.KEY_ccedilla\nexport KEY_ccedilla\n\nconst KEY_egrave = detail.KEY_egrave\nexport KEY_egrave\n\nconst KEY_eacute = detail.KEY_eacute\nexport KEY_eacute\n\nconst KEY_ecircumflex = detail.KEY_ecircumflex\nexport KEY_ecircumflex\n\nconst KEY_ediaeresis = detail.KEY_ediaeresis\nexport KEY_ediaeresis\n\nconst KEY_igrave = detail.KEY_igrave\nexport KEY_igrave\n\nconst KEY_iacute = detail.KEY_iacute\nexport KEY_iacute\n\nconst KEY_icircumflex = detail.KEY_icircumflex\nexport KEY_icircumflex\n\nconst KEY_idiaeresis = detail.KEY_idiaeresis\nexport KEY_idiaeresis\n\nconst KEY_eth = detail.KEY_eth\nexport KEY_eth\n\nconst KEY_ntilde = detail.KEY_ntilde\nexport KEY_ntilde\n\nconst KEY_ograve = detail.KEY_ograve\nexport KEY_ograve\n\nconst KEY_oacute = detail.KEY_oacute\nexport KEY_oacute\n\nconst KEY_ocircumflex = detail.KEY_ocircumflex\nexport KEY_ocircumflex\n\nconst KEY_otilde = detail.KEY_otilde\nexport KEY_otilde\n\nconst KEY_odiaeresis = detail.KEY_odiaeresis\nexport KEY_odiaeresis\n\nconst KEY_division = detail.KEY_division\nexport KEY_division\n\nconst KEY_oslash = detail.KEY_oslash\nexport KEY_oslash\n\nconst KEY_ooblique = detail.KEY_ooblique\nexport KEY_ooblique\n\nconst KEY_ugrave = detail.KEY_ugrave\nexport KEY_ugrave\n\nconst KEY_uacute = detail.KEY_uacute\nexport KEY_uacute\n\nconst KEY_ucircumflex = detail.KEY_ucircumflex\nexport KEY_ucircumflex\n\nconst KEY_udiaeresis = detail.KEY_udiaeresis\nexport KEY_udiaeresis\n\nconst KEY_yacute = detail.KEY_yacute\nexport KEY_yacute\n\nconst KEY_thorn = detail.KEY_thorn\nexport KEY_thorn\n\nconst KEY_ydiaeresis = detail.KEY_ydiaeresis\nexport KEY_ydiaeresis\n\nconst KEY_Aogonek = detail.KEY_Aogonek\nexport KEY_Aogonek\n\nconst KEY_breve = detail.KEY_breve\nexport KEY_breve\n\nconst KEY_Lstroke = detail.KEY_Lstroke\nexport KEY_Lstroke\n\nconst KEY_Lcaron = detail.KEY_Lcaron\nexport KEY_Lcaron\n\nconst KEY_Sacute = detail.KEY_Sacute\nexport KEY_Sacute\n\nconst KEY_Scaron = detail.KEY_Scaron\nexport KEY_Scaron\n\nconst KEY_Scedilla = detail.KEY_Scedilla\nexport KEY_Scedilla\n\nconst KEY_Tcaron = detail.KEY_Tcaron\nexport KEY_Tcaron\n\nconst KEY_Zacute = detail.KEY_Zacute\nexport KEY_Zacute\n\nconst KEY_Zcaron = detail.KEY_Zcaron\nexport KEY_Zcaron\n\nconst KEY_Zabovedot = detail.KEY_Zabovedot\nexport KEY_Zabovedot\n\nconst KEY_aogonek = detail.KEY_aogonek\nexport KEY_aogonek\n\nconst KEY_ogonek = detail.KEY_ogonek\nexport KEY_ogonek\n\nconst KEY_lstroke = detail.KEY_lstroke\nexport KEY_lstroke\n\nconst KEY_lcaron = detail.KEY_lcaron\nexport KEY_lcaron\n\nconst KEY_sacute = detail.KEY_sacute\nexport KEY_sacute\n\nconst KEY_caron = detail.KEY_caron\nexport KEY_caron\n\nconst KEY_scaron = detail.KEY_scaron\nexport KEY_scaron\n\nconst KEY_scedilla = detail.KEY_scedilla\nexport KEY_scedilla\n\nconst KEY_tcaron = detail.KEY_tcaron\nexport KEY_tcaron\n\nconst KEY_zacute = detail.KEY_zacute\nexport KEY_zacute\n\nconst KEY_doubleacute = detail.KEY_doubleacute\nexport KEY_doubleacute\n\nconst KEY_zcaron = detail.KEY_zcaron\nexport KEY_zcaron\n\nconst KEY_zabovedot = detail.KEY_zabovedot\nexport KEY_zabovedot\n\nconst KEY_Racute = detail.KEY_Racute\nexport KEY_Racute\n\nconst KEY_Abreve = detail.KEY_Abreve\nexport KEY_Abreve\n\nconst KEY_Lacute = detail.KEY_Lacute\nexport KEY_Lacute\n\nconst KEY_Cacute = detail.KEY_Cacute\nexport KEY_Cacute\n\nconst KEY_Ccaron = detail.KEY_Ccaron\nexport KEY_Ccaron\n\nconst KEY_Eogonek = detail.KEY_Eogonek\nexport KEY_Eogonek\n\nconst KEY_Ecaron = detail.KEY_Ecaron\nexport KEY_Ecaron\n\nconst KEY_Dcaron = detail.KEY_Dcaron\nexport KEY_Dcaron\n\nconst KEY_Dstroke = detail.KEY_Dstroke\nexport KEY_Dstroke\n\nconst KEY_Nacute = detail.KEY_Nacute\nexport KEY_Nacute\n\nconst KEY_Ncaron = detail.KEY_Ncaron\nexport KEY_Ncaron\n\nconst KEY_Odoubleacute = detail.KEY_Odoubleacute\nexport KEY_Odoubleacute\n\nconst KEY_Rcaron = detail.KEY_Rcaron\nexport KEY_Rcaron\n\nconst KEY_Uring = detail.KEY_Uring\nexport KEY_Uring\n\nconst KEY_Udoubleacute = detail.KEY_Udoubleacute\nexport KEY_Udoubleacute\n\nconst KEY_Tcedilla = detail.KEY_Tcedilla\nexport KEY_Tcedilla\n\nconst KEY_racute = detail.KEY_racute\nexport KEY_racute\n\nconst KEY_abreve = detail.KEY_abreve\nexport KEY_abreve\n\nconst KEY_lacute = detail.KEY_lacute\nexport KEY_lacute\n\nconst KEY_cacute = detail.KEY_cacute\nexport KEY_cacute\n\nconst KEY_ccaron = detail.KEY_ccaron\nexport KEY_ccaron\n\nconst KEY_eogonek = detail.KEY_eogonek\nexport KEY_eogonek\n\nconst KEY_ecaron = detail.KEY_ecaron\nexport KEY_ecaron\n\nconst KEY_dcaron = detail.KEY_dcaron\nexport KEY_dcaron\n\nconst KEY_dstroke = detail.KEY_dstroke\nexport KEY_dstroke\n\nconst KEY_nacute = detail.KEY_nacute\nexport KEY_nacute\n\nconst KEY_ncaron = detail.KEY_ncaron\nexport KEY_ncaron\n\nconst KEY_odoubleacute = detail.KEY_odoubleacute\nexport KEY_odoubleacute\n\nconst KEY_rcaron = detail.KEY_rcaron\nexport KEY_rcaron\n\nconst KEY_uring = detail.KEY_uring\nexport KEY_uring\n\nconst KEY_udoubleacute = detail.KEY_udoubleacute\nexport KEY_udoubleacute\n\nconst KEY_tcedilla = detail.KEY_tcedilla\nexport KEY_tcedilla\n\nconst KEY_abovedot = detail.KEY_abovedot\nexport KEY_abovedot\n\nconst KEY_Hstroke = detail.KEY_Hstroke\nexport KEY_Hstroke\n\nconst KEY_Hcircumflex = detail.KEY_Hcircumflex\nexport KEY_Hcircumflex\n\nconst KEY_Iabovedot = detail.KEY_Iabovedot\nexport KEY_Iabovedot\n\nconst KEY_Gbreve = detail.KEY_Gbreve\nexport KEY_Gbreve\n\nconst KEY_Jcircumflex = detail.KEY_Jcircumflex\nexport KEY_Jcircumflex\n\nconst KEY_hstroke = detail.KEY_hstroke\nexport KEY_hstroke\n\nconst KEY_hcircumflex = detail.KEY_hcircumflex\nexport KEY_hcircumflex\n\nconst KEY_idotless = detail.KEY_idotless\nexport KEY_idotless\n\nconst KEY_gbreve = detail.KEY_gbreve\nexport KEY_gbreve\n\nconst KEY_jcircumflex = detail.KEY_jcircumflex\nexport KEY_jcircumflex\n\nconst KEY_Cabovedot = detail.KEY_Cabovedot\nexport KEY_Cabovedot\n\nconst KEY_Ccircumflex = detail.KEY_Ccircumflex\nexport KEY_Ccircumflex\n\nconst KEY_Gabovedot = detail.KEY_Gabovedot\nexport KEY_Gabovedot\n\nconst KEY_Gcircumflex = detail.KEY_Gcircumflex\nexport KEY_Gcircumflex\n\nconst KEY_Ubreve = detail.KEY_Ubreve\nexport KEY_Ubreve\n\nconst KEY_Scircumflex = detail.KEY_Scircumflex\nexport KEY_Scircumflex\n\nconst KEY_cabovedot = detail.KEY_cabovedot\nexport KEY_cabovedot\n\nconst KEY_ccircumflex = detail.KEY_ccircumflex\nexport KEY_ccircumflex\n\nconst KEY_gabovedot = detail.KEY_gabovedot\nexport KEY_gabovedot\n\nconst KEY_gcircumflex = detail.KEY_gcircumflex\nexport KEY_gcircumflex\n\nconst KEY_ubreve = detail.KEY_ubreve\nexport KEY_ubreve\n\nconst KEY_scircumflex = detail.KEY_scircumflex\nexport KEY_scircumflex\n\nconst KEY_kra = detail.KEY_kra\nexport KEY_kra\n\nconst KEY_kappa = detail.KEY_kappa\nexport KEY_kappa\n\nconst KEY_Rcedilla = detail.KEY_Rcedilla\nexport KEY_Rcedilla\n\nconst KEY_Itilde = detail.KEY_Itilde\nexport KEY_Itilde\n\nconst KEY_Lcedilla = detail.KEY_Lcedilla\nexport KEY_Lcedilla\n\nconst KEY_Emacron = detail.KEY_Emacron\nexport KEY_Emacron\n\nconst KEY_Gcedilla = detail.KEY_Gcedilla\nexport KEY_Gcedilla\n\nconst KEY_Tslash = detail.KEY_Tslash\nexport KEY_Tslash\n\nconst KEY_rcedilla = detail.KEY_rcedilla\nexport KEY_rcedilla\n\nconst KEY_itilde = detail.KEY_itilde\nexport KEY_itilde\n\nconst KEY_lcedilla = detail.KEY_lcedilla\nexport KEY_lcedilla\n\nconst KEY_emacron = detail.KEY_emacron\nexport KEY_emacron\n\nconst KEY_gcedilla = detail.KEY_gcedilla\nexport KEY_gcedilla\n\nconst KEY_tslash = detail.KEY_tslash\nexport KEY_tslash\n\nconst KEY_ENG = detail.KEY_ENG\nexport KEY_ENG\n\nconst KEY_eng = detail.KEY_eng\nexport KEY_eng\n\nconst KEY_Amacron = detail.KEY_Amacron\nexport KEY_Amacron\n\nconst KEY_Iogonek = detail.KEY_Iogonek\nexport KEY_Iogonek\n\nconst KEY_Eabovedot = detail.KEY_Eabovedot\nexport KEY_Eabovedot\n\nconst KEY_Imacron = detail.KEY_Imacron\nexport KEY_Imacron\n\nconst KEY_Ncedilla = detail.KEY_Ncedilla\nexport KEY_Ncedilla\n\nconst KEY_Omacron = detail.KEY_Omacron\nexport KEY_Omacron\n\nconst KEY_Kcedilla = detail.KEY_Kcedilla\nexport KEY_Kcedilla\n\nconst KEY_Uogonek = detail.KEY_Uogonek\nexport KEY_Uogonek\n\nconst KEY_Utilde = detail.KEY_Utilde\nexport KEY_Utilde\n\nconst KEY_Umacron = detail.KEY_Umacron\nexport KEY_Umacron\n\nconst KEY_amacron = detail.KEY_amacron\nexport KEY_amacron\n\nconst KEY_iogonek = detail.KEY_iogonek\nexport KEY_iogonek\n\nconst KEY_eabovedot = detail.KEY_eabovedot\nexport KEY_eabovedot\n\nconst KEY_imacron = detail.KEY_imacron\nexport KEY_imacron\n\nconst KEY_ncedilla = detail.KEY_ncedilla\nexport KEY_ncedilla\n\nconst KEY_omacron = detail.KEY_omacron\nexport KEY_omacron\n\nconst KEY_kcedilla = detail.KEY_kcedilla\nexport KEY_kcedilla\n\nconst KEY_uogonek = detail.KEY_uogonek\nexport KEY_uogonek\n\nconst KEY_utilde = detail.KEY_utilde\nexport KEY_utilde\n\nconst KEY_umacron = detail.KEY_umacron\nexport KEY_umacron\n\nconst KEY_Wcircumflex = detail.KEY_Wcircumflex\nexport KEY_Wcircumflex\n\nconst KEY_wcircumflex = detail.KEY_wcircumflex\nexport KEY_wcircumflex\n\nconst KEY_Ycircumflex = detail.KEY_Ycircumflex\nexport KEY_Ycircumflex\n\nconst KEY_ycircumflex = detail.KEY_ycircumflex\nexport KEY_ycircumflex\n\nconst KEY_Babovedot = detail.KEY_Babovedot\nexport KEY_Babovedot\n\nconst KEY_babovedot = detail.KEY_babovedot\nexport KEY_babovedot\n\nconst KEY_Dabovedot = detail.KEY_Dabovedot\nexport KEY_Dabovedot\n\nconst KEY_dabovedot = detail.KEY_dabovedot\nexport KEY_dabovedot\n\nconst KEY_Fabovedot = detail.KEY_Fabovedot\nexport KEY_Fabovedot\n\nconst KEY_fabovedot = detail.KEY_fabovedot\nexport KEY_fabovedot\n\nconst KEY_Mabovedot = detail.KEY_Mabovedot\nexport KEY_Mabovedot\n\nconst KEY_mabovedot = detail.KEY_mabovedot\nexport KEY_mabovedot\n\nconst KEY_Pabovedot = detail.KEY_Pabovedot\nexport KEY_Pabovedot\n\nconst KEY_pabovedot = detail.KEY_pabovedot\nexport KEY_pabovedot\n\nconst KEY_Sabovedot = detail.KEY_Sabovedot\nexport KEY_Sabovedot\n\nconst KEY_sabovedot = detail.KEY_sabovedot\nexport KEY_sabovedot\n\nconst KEY_Tabovedot = detail.KEY_Tabovedot\nexport KEY_Tabovedot\n\nconst KEY_tabovedot = detail.KEY_tabovedot\nexport KEY_tabovedot\n\nconst KEY_Wgrave = detail.KEY_Wgrave\nexport KEY_Wgrave\n\nconst KEY_wgrave = detail.KEY_wgrave\nexport KEY_wgrave\n\nconst KEY_Wacute = detail.KEY_Wacute\nexport KEY_Wacute\n\nconst KEY_wacute = detail.KEY_wacute\nexport KEY_wacute\n\nconst KEY_Wdiaeresis = detail.KEY_Wdiaeresis\nexport KEY_Wdiaeresis\n\nconst KEY_wdiaeresis = detail.KEY_wdiaeresis\nexport KEY_wdiaeresis\n\nconst KEY_Ygrave = detail.KEY_Ygrave\nexport KEY_Ygrave\n\nconst KEY_ygrave = detail.KEY_ygrave\nexport KEY_ygrave\n\nconst KEY_OE = detail.KEY_OE\nexport KEY_OE\n\nconst KEY_oe = detail.KEY_oe\nexport KEY_oe\n\nconst KEY_Ydiaeresis = detail.KEY_Ydiaeresis\nexport KEY_Ydiaeresis\n\nconst KEY_overline = detail.KEY_overline\nexport KEY_overline\n\nconst KEY_kana_fullstop = detail.KEY_kana_fullstop\nexport KEY_kana_fullstop\n\nconst KEY_kana_openingbracket = detail.KEY_kana_openingbracket\nexport KEY_kana_openingbracket\n\nconst KEY_kana_closingbracket = detail.KEY_kana_closingbracket\nexport KEY_kana_closingbracket\n\nconst KEY_kana_comma = detail.KEY_kana_comma\nexport KEY_kana_comma\n\nconst KEY_kana_conjunctive = detail.KEY_kana_conjunctive\nexport KEY_kana_conjunctive\n\nconst KEY_kana_middledot = detail.KEY_kana_middledot\nexport KEY_kana_middledot\n\nconst KEY_kana_WO = detail.KEY_kana_WO\nexport KEY_kana_WO\n\nconst KEY_kana_a = detail.KEY_kana_a\nexport KEY_kana_a\n\nconst KEY_kana_i = detail.KEY_kana_i\nexport KEY_kana_i\n\nconst KEY_kana_u = detail.KEY_kana_u\nexport KEY_kana_u\n\nconst KEY_kana_e = detail.KEY_kana_e\nexport KEY_kana_e\n\nconst KEY_kana_o = detail.KEY_kana_o\nexport KEY_kana_o\n\nconst KEY_kana_ya = detail.KEY_kana_ya\nexport KEY_kana_ya\n\nconst KEY_kana_yu = detail.KEY_kana_yu\nexport KEY_kana_yu\n\nconst KEY_kana_yo = detail.KEY_kana_yo\nexport KEY_kana_yo\n\nconst KEY_kana_tsu = detail.KEY_kana_tsu\nexport KEY_kana_tsu\n\nconst KEY_kana_tu = detail.KEY_kana_tu\nexport KEY_kana_tu\n\nconst KEY_prolongedsound = detail.KEY_prolongedsound\nexport KEY_prolongedsound\n\nconst KEY_kana_A = detail.KEY_kana_A\nexport KEY_kana_A\n\nconst KEY_kana_I = detail.KEY_kana_I\nexport KEY_kana_I\n\nconst KEY_kana_U = detail.KEY_kana_U\nexport KEY_kana_U\n\nconst KEY_kana_E = detail.KEY_kana_E\nexport KEY_kana_E\n\nconst KEY_kana_O = detail.KEY_kana_O\nexport KEY_kana_O\n\nconst KEY_kana_KA = detail.KEY_kana_KA\nexport KEY_kana_KA\n\nconst KEY_kana_KI = detail.KEY_kana_KI\nexport KEY_kana_KI\n\nconst KEY_kana_KU = detail.KEY_kana_KU\nexport KEY_kana_KU\n\nconst KEY_kana_KE = detail.KEY_kana_KE\nexport KEY_kana_KE\n\nconst KEY_kana_KO = detail.KEY_kana_KO\nexport KEY_kana_KO\n\nconst KEY_kana_SA = detail.KEY_kana_SA\nexport KEY_kana_SA\n\nconst KEY_kana_SHI = detail.KEY_kana_SHI\nexport KEY_kana_SHI\n\nconst KEY_kana_SU = detail.KEY_kana_SU\nexport KEY_kana_SU\n\nconst KEY_kana_SE = detail.KEY_kana_SE\nexport KEY_kana_SE\n\nconst KEY_kana_SO = detail.KEY_kana_SO\nexport KEY_kana_SO\n\nconst KEY_kana_TA = detail.KEY_kana_TA\nexport KEY_kana_TA\n\nconst KEY_kana_CHI = detail.KEY_kana_CHI\nexport KEY_kana_CHI\n\nconst KEY_kana_TI = detail.KEY_kana_TI\nexport KEY_kana_TI\n\nconst KEY_kana_TSU = detail.KEY_kana_TSU\nexport KEY_kana_TSU\n\nconst KEY_kana_TU = detail.KEY_kana_TU\nexport KEY_kana_TU\n\nconst KEY_kana_TE = detail.KEY_kana_TE\nexport KEY_kana_TE\n\nconst KEY_kana_TO = detail.KEY_kana_TO\nexport KEY_kana_TO\n\nconst KEY_kana_NA = detail.KEY_kana_NA\nexport KEY_kana_NA\n\nconst KEY_kana_NI = detail.KEY_kana_NI\nexport KEY_kana_NI\n\nconst KEY_kana_NU = detail.KEY_kana_NU\nexport KEY_kana_NU\n\nconst KEY_kana_NE = detail.KEY_kana_NE\nexport KEY_kana_NE\n\nconst KEY_kana_NO = detail.KEY_kana_NO\nexport KEY_kana_NO\n\nconst KEY_kana_HA = detail.KEY_kana_HA\nexport KEY_kana_HA\n\nconst KEY_kana_HI = detail.KEY_kana_HI\nexport KEY_kana_HI\n\nconst KEY_kana_FU = detail.KEY_kana_FU\nexport KEY_kana_FU\n\nconst KEY_kana_HU = detail.KEY_kana_HU\nexport KEY_kana_HU\n\nconst KEY_kana_HE = detail.KEY_kana_HE\nexport KEY_kana_HE\n\nconst KEY_kana_HO = detail.KEY_kana_HO\nexport KEY_kana_HO\n\nconst KEY_kana_MA = detail.KEY_kana_MA\nexport KEY_kana_MA\n\nconst KEY_kana_MI = detail.KEY_kana_MI\nexport KEY_kana_MI\n\nconst KEY_kana_MU = detail.KEY_kana_MU\nexport KEY_kana_MU\n\nconst KEY_kana_ME = detail.KEY_kana_ME\nexport KEY_kana_ME\n\nconst KEY_kana_MO = detail.KEY_kana_MO\nexport KEY_kana_MO\n\nconst KEY_kana_YA = detail.KEY_kana_YA\nexport KEY_kana_YA\n\nconst KEY_kana_YU = detail.KEY_kana_YU\nexport KEY_kana_YU\n\nconst KEY_kana_YO = detail.KEY_kana_YO\nexport KEY_kana_YO\n\nconst KEY_kana_RA = detail.KEY_kana_RA\nexport KEY_kana_RA\n\nconst KEY_kana_RI = detail.KEY_kana_RI\nexport KEY_kana_RI\n\nconst KEY_kana_RU = detail.KEY_kana_RU\nexport KEY_kana_RU\n\nconst KEY_kana_RE = detail.KEY_kana_RE\nexport KEY_kana_RE\n\nconst KEY_kana_RO = detail.KEY_kana_RO\nexport KEY_kana_RO\n\nconst KEY_kana_WA = detail.KEY_kana_WA\nexport KEY_kana_WA\n\nconst KEY_kana_N = detail.KEY_kana_N\nexport KEY_kana_N\n\nconst KEY_voicedsound = detail.KEY_voicedsound\nexport KEY_voicedsound\n\nconst KEY_semivoicedsound = detail.KEY_semivoicedsound\nexport KEY_semivoicedsound\n\nconst KEY_kana_switch = detail.KEY_kana_switch\nexport KEY_kana_switch\n\nconst KEY_Farsi_0 = detail.KEY_Farsi_0\nexport KEY_Farsi_0\n\nconst KEY_Farsi_1 = detail.KEY_Farsi_1\nexport KEY_Farsi_1\n\nconst KEY_Farsi_2 = detail.KEY_Farsi_2\nexport KEY_Farsi_2\n\nconst KEY_Farsi_3 = detail.KEY_Farsi_3\nexport KEY_Farsi_3\n\nconst KEY_Farsi_4 = detail.KEY_Farsi_4\nexport KEY_Farsi_4\n\nconst KEY_Farsi_5 = detail.KEY_Farsi_5\nexport KEY_Farsi_5\n\nconst KEY_Farsi_6 = detail.KEY_Farsi_6\nexport KEY_Farsi_6\n\nconst KEY_Farsi_7 = detail.KEY_Farsi_7\nexport KEY_Farsi_7\n\nconst KEY_Farsi_8 = detail.KEY_Farsi_8\nexport KEY_Farsi_8\n\nconst KEY_Farsi_9 = detail.KEY_Farsi_9\nexport KEY_Farsi_9\n\nconst KEY_Arabic_percent = detail.KEY_Arabic_percent\nexport KEY_Arabic_percent\n\nconst KEY_Arabic_superscript_alef = detail.KEY_Arabic_superscript_alef\nexport KEY_Arabic_superscript_alef\n\nconst KEY_Arabic_tteh = detail.KEY_Arabic_tteh\nexport KEY_Arabic_tteh\n\nconst KEY_Arabic_peh = detail.KEY_Arabic_peh\nexport KEY_Arabic_peh\n\nconst KEY_Arabic_tcheh = detail.KEY_Arabic_tcheh\nexport KEY_Arabic_tcheh\n\nconst KEY_Arabic_ddal = detail.KEY_Arabic_ddal\nexport KEY_Arabic_ddal\n\nconst KEY_Arabic_rreh = detail.KEY_Arabic_rreh\nexport KEY_Arabic_rreh\n\nconst KEY_Arabic_comma = detail.KEY_Arabic_comma\nexport KEY_Arabic_comma\n\nconst KEY_Arabic_fullstop = detail.KEY_Arabic_fullstop\nexport KEY_Arabic_fullstop\n\nconst KEY_Arabic_0 = detail.KEY_Arabic_0\nexport KEY_Arabic_0\n\nconst KEY_Arabic_1 = detail.KEY_Arabic_1\nexport KEY_Arabic_1\n\nconst KEY_Arabic_2 = detail.KEY_Arabic_2\nexport KEY_Arabic_2\n\nconst KEY_Arabic_3 = detail.KEY_Arabic_3\nexport KEY_Arabic_3\n\nconst KEY_Arabic_4 = detail.KEY_Arabic_4\nexport KEY_Arabic_4\n\nconst KEY_Arabic_5 = detail.KEY_Arabic_5\nexport KEY_Arabic_5\n\nconst KEY_Arabic_6 = detail.KEY_Arabic_6\nexport KEY_Arabic_6\n\nconst KEY_Arabic_7 = detail.KEY_Arabic_7\nexport KEY_Arabic_7\n\nconst KEY_Arabic_8 = detail.KEY_Arabic_8\nexport KEY_Arabic_8\n\nconst KEY_Arabic_9 = detail.KEY_Arabic_9\nexport KEY_Arabic_9\n\nconst KEY_Arabic_semicolon = detail.KEY_Arabic_semicolon\nexport KEY_Arabic_semicolon\n\nconst KEY_Arabic_question_mark = detail.KEY_Arabic_question_mark\nexport KEY_Arabic_question_mark\n\nconst KEY_Arabic_hamza = detail.KEY_Arabic_hamza\nexport KEY_Arabic_hamza\n\nconst KEY_Arabic_maddaonalef = detail.KEY_Arabic_maddaonalef\nexport KEY_Arabic_maddaonalef\n\nconst KEY_Arabic_hamzaonalef = detail.KEY_Arabic_hamzaonalef\nexport KEY_Arabic_hamzaonalef\n\nconst KEY_Arabic_hamzaonwaw = detail.KEY_Arabic_hamzaonwaw\nexport KEY_Arabic_hamzaonwaw\n\nconst KEY_Arabic_hamzaunderalef = detail.KEY_Arabic_hamzaunderalef\nexport KEY_Arabic_hamzaunderalef\n\nconst KEY_Arabic_hamzaonyeh = detail.KEY_Arabic_hamzaonyeh\nexport KEY_Arabic_hamzaonyeh\n\nconst KEY_Arabic_alef = detail.KEY_Arabic_alef\nexport KEY_Arabic_alef\n\nconst KEY_Arabic_beh = detail.KEY_Arabic_beh\nexport KEY_Arabic_beh\n\nconst KEY_Arabic_tehmarbuta = detail.KEY_Arabic_tehmarbuta\nexport KEY_Arabic_tehmarbuta\n\nconst KEY_Arabic_teh = detail.KEY_Arabic_teh\nexport KEY_Arabic_teh\n\nconst KEY_Arabic_theh = detail.KEY_Arabic_theh\nexport KEY_Arabic_theh\n\nconst KEY_Arabic_jeem = detail.KEY_Arabic_jeem\nexport KEY_Arabic_jeem\n\nconst KEY_Arabic_hah = detail.KEY_Arabic_hah\nexport KEY_Arabic_hah\n\nconst KEY_Arabic_khah = detail.KEY_Arabic_khah\nexport KEY_Arabic_khah\n\nconst KEY_Arabic_dal = detail.KEY_Arabic_dal\nexport KEY_Arabic_dal\n\nconst KEY_Arabic_thal = detail.KEY_Arabic_thal\nexport KEY_Arabic_thal\n\nconst KEY_Arabic_ra = detail.KEY_Arabic_ra\nexport KEY_Arabic_ra\n\nconst KEY_Arabic_zain = detail.KEY_Arabic_zain\nexport KEY_Arabic_zain\n\nconst KEY_Arabic_seen = detail.KEY_Arabic_seen\nexport KEY_Arabic_seen\n\nconst KEY_Arabic_sheen = detail.KEY_Arabic_sheen\nexport KEY_Arabic_sheen\n\nconst KEY_Arabic_sad = detail.KEY_Arabic_sad\nexport KEY_Arabic_sad\n\nconst KEY_Arabic_dad = detail.KEY_Arabic_dad\nexport KEY_Arabic_dad\n\nconst KEY_Arabic_tah = detail.KEY_Arabic_tah\nexport KEY_Arabic_tah\n\nconst KEY_Arabic_zah = detail.KEY_Arabic_zah\nexport KEY_Arabic_zah\n\nconst KEY_Arabic_ain = detail.KEY_Arabic_ain\nexport KEY_Arabic_ain\n\nconst KEY_Arabic_ghain = detail.KEY_Arabic_ghain\nexport KEY_Arabic_ghain\n\nconst KEY_Arabic_tatweel = detail.KEY_Arabic_tatweel\nexport KEY_Arabic_tatweel\n\nconst KEY_Arabic_feh = detail.KEY_Arabic_feh\nexport KEY_Arabic_feh\n\nconst KEY_Arabic_qaf = detail.KEY_Arabic_qaf\nexport KEY_Arabic_qaf\n\nconst KEY_Arabic_kaf = detail.KEY_Arabic_kaf\nexport KEY_Arabic_kaf\n\nconst KEY_Arabic_lam = detail.KEY_Arabic_lam\nexport KEY_Arabic_lam\n\nconst KEY_Arabic_meem = detail.KEY_Arabic_meem\nexport KEY_Arabic_meem\n\nconst KEY_Arabic_noon = detail.KEY_Arabic_noon\nexport KEY_Arabic_noon\n\nconst KEY_Arabic_ha = detail.KEY_Arabic_ha\nexport KEY_Arabic_ha\n\nconst KEY_Arabic_heh = detail.KEY_Arabic_heh\nexport KEY_Arabic_heh\n\nconst KEY_Arabic_waw = detail.KEY_Arabic_waw\nexport KEY_Arabic_waw\n\nconst KEY_Arabic_alefmaksura = detail.KEY_Arabic_alefmaksura\nexport KEY_Arabic_alefmaksura\n\nconst KEY_Arabic_yeh = detail.KEY_Arabic_yeh\nexport KEY_Arabic_yeh\n\nconst KEY_Arabic_fathatan = detail.KEY_Arabic_fathatan\nexport KEY_Arabic_fathatan\n\nconst KEY_Arabic_dammatan = detail.KEY_Arabic_dammatan\nexport KEY_Arabic_dammatan\n\nconst KEY_Arabic_kasratan = detail.KEY_Arabic_kasratan\nexport KEY_Arabic_kasratan\n\nconst KEY_Arabic_fatha = detail.KEY_Arabic_fatha\nexport KEY_Arabic_fatha\n\nconst KEY_Arabic_damma = detail.KEY_Arabic_damma\nexport KEY_Arabic_damma\n\nconst KEY_Arabic_kasra = detail.KEY_Arabic_kasra\nexport KEY_Arabic_kasra\n\nconst KEY_Arabic_shadda = detail.KEY_Arabic_shadda\nexport KEY_Arabic_shadda\n\nconst KEY_Arabic_sukun = detail.KEY_Arabic_sukun\nexport KEY_Arabic_sukun\n\nconst KEY_Arabic_madda_above = detail.KEY_Arabic_madda_above\nexport KEY_Arabic_madda_above\n\nconst KEY_Arabic_hamza_above = detail.KEY_Arabic_hamza_above\nexport KEY_Arabic_hamza_above\n\nconst KEY_Arabic_hamza_below = detail.KEY_Arabic_hamza_below\nexport KEY_Arabic_hamza_below\n\nconst KEY_Arabic_jeh = detail.KEY_Arabic_jeh\nexport KEY_Arabic_jeh\n\nconst KEY_Arabic_veh = detail.KEY_Arabic_veh\nexport KEY_Arabic_veh\n\nconst KEY_Arabic_keheh = detail.KEY_Arabic_keheh\nexport KEY_Arabic_keheh\n\nconst KEY_Arabic_gaf = detail.KEY_Arabic_gaf\nexport KEY_Arabic_gaf\n\nconst KEY_Arabic_noon_ghunna = detail.KEY_Arabic_noon_ghunna\nexport KEY_Arabic_noon_ghunna\n\nconst KEY_Arabic_heh_doachashmee = detail.KEY_Arabic_heh_doachashmee\nexport KEY_Arabic_heh_doachashmee\n\nconst KEY_Farsi_yeh = detail.KEY_Farsi_yeh\nexport KEY_Farsi_yeh\n\nconst KEY_Arabic_farsi_yeh = detail.KEY_Arabic_farsi_yeh\nexport KEY_Arabic_farsi_yeh\n\nconst KEY_Arabic_yeh_baree = detail.KEY_Arabic_yeh_baree\nexport KEY_Arabic_yeh_baree\n\nconst KEY_Arabic_heh_goal = detail.KEY_Arabic_heh_goal\nexport KEY_Arabic_heh_goal\n\nconst KEY_Arabic_switch = detail.KEY_Arabic_switch\nexport KEY_Arabic_switch\n\nconst KEY_Cyrillic_GHE_bar = detail.KEY_Cyrillic_GHE_bar\nexport KEY_Cyrillic_GHE_bar\n\nconst KEY_Cyrillic_ghe_bar = detail.KEY_Cyrillic_ghe_bar\nexport KEY_Cyrillic_ghe_bar\n\nconst KEY_Cyrillic_ZHE_descender = detail.KEY_Cyrillic_ZHE_descender\nexport KEY_Cyrillic_ZHE_descender\n\nconst KEY_Cyrillic_zhe_descender = detail.KEY_Cyrillic_zhe_descender\nexport KEY_Cyrillic_zhe_descender\n\nconst KEY_Cyrillic_KA_descender = detail.KEY_Cyrillic_KA_descender\nexport KEY_Cyrillic_KA_descender\n\nconst KEY_Cyrillic_ka_descender = detail.KEY_Cyrillic_ka_descender\nexport KEY_Cyrillic_ka_descender\n\nconst KEY_Cyrillic_KA_vertstroke = detail.KEY_Cyrillic_KA_vertstroke\nexport KEY_Cyrillic_KA_vertstroke\n\nconst KEY_Cyrillic_ka_vertstroke = detail.KEY_Cyrillic_ka_vertstroke\nexport KEY_Cyrillic_ka_vertstroke\n\nconst KEY_Cyrillic_EN_descender = detail.KEY_Cyrillic_EN_descender\nexport KEY_Cyrillic_EN_descender\n\nconst KEY_Cyrillic_en_descender = detail.KEY_Cyrillic_en_descender\nexport KEY_Cyrillic_en_descender\n\nconst KEY_Cyrillic_U_straight = detail.KEY_Cyrillic_U_straight\nexport KEY_Cyrillic_U_straight\n\nconst KEY_Cyrillic_u_straight = detail.KEY_Cyrillic_u_straight\nexport KEY_Cyrillic_u_straight\n\nconst KEY_Cyrillic_U_straight_bar = detail.KEY_Cyrillic_U_straight_bar\nexport KEY_Cyrillic_U_straight_bar\n\nconst KEY_Cyrillic_u_straight_bar = detail.KEY_Cyrillic_u_straight_bar\nexport KEY_Cyrillic_u_straight_bar\n\nconst KEY_Cyrillic_HA_descender = detail.KEY_Cyrillic_HA_descender\nexport KEY_Cyrillic_HA_descender\n\nconst KEY_Cyrillic_ha_descender = detail.KEY_Cyrillic_ha_descender\nexport KEY_Cyrillic_ha_descender\n\nconst KEY_Cyrillic_CHE_descender = detail.KEY_Cyrillic_CHE_descender\nexport KEY_Cyrillic_CHE_descender\n\nconst KEY_Cyrillic_che_descender = detail.KEY_Cyrillic_che_descender\nexport KEY_Cyrillic_che_descender\n\nconst KEY_Cyrillic_CHE_vertstroke = detail.KEY_Cyrillic_CHE_vertstroke\nexport KEY_Cyrillic_CHE_vertstroke\n\nconst KEY_Cyrillic_che_vertstroke = detail.KEY_Cyrillic_che_vertstroke\nexport KEY_Cyrillic_che_vertstroke\n\nconst KEY_Cyrillic_SHHA = detail.KEY_Cyrillic_SHHA\nexport KEY_Cyrillic_SHHA\n\nconst KEY_Cyrillic_shha = detail.KEY_Cyrillic_shha\nexport KEY_Cyrillic_shha\n\nconst KEY_Cyrillic_SCHWA = detail.KEY_Cyrillic_SCHWA\nexport KEY_Cyrillic_SCHWA\n\nconst KEY_Cyrillic_schwa = detail.KEY_Cyrillic_schwa\nexport KEY_Cyrillic_schwa\n\nconst KEY_Cyrillic_I_macron = detail.KEY_Cyrillic_I_macron\nexport KEY_Cyrillic_I_macron\n\nconst KEY_Cyrillic_i_macron = detail.KEY_Cyrillic_i_macron\nexport KEY_Cyrillic_i_macron\n\nconst KEY_Cyrillic_O_bar = detail.KEY_Cyrillic_O_bar\nexport KEY_Cyrillic_O_bar\n\nconst KEY_Cyrillic_o_bar = detail.KEY_Cyrillic_o_bar\nexport KEY_Cyrillic_o_bar\n\nconst KEY_Cyrillic_U_macron = detail.KEY_Cyrillic_U_macron\nexport KEY_Cyrillic_U_macron\n\nconst KEY_Cyrillic_u_macron = detail.KEY_Cyrillic_u_macron\nexport KEY_Cyrillic_u_macron\n\nconst KEY_Serbian_dje = detail.KEY_Serbian_dje\nexport KEY_Serbian_dje\n\nconst KEY_Macedonia_gje = detail.KEY_Macedonia_gje\nexport KEY_Macedonia_gje\n\nconst KEY_Cyrillic_io = detail.KEY_Cyrillic_io\nexport KEY_Cyrillic_io\n\nconst KEY_Ukrainian_ie = detail.KEY_Ukrainian_ie\nexport KEY_Ukrainian_ie\n\nconst KEY_Ukranian_je = detail.KEY_Ukranian_je\nexport KEY_Ukranian_je\n\nconst KEY_Macedonia_dse = detail.KEY_Macedonia_dse\nexport KEY_Macedonia_dse\n\nconst KEY_Ukrainian_i = detail.KEY_Ukrainian_i\nexport KEY_Ukrainian_i\n\nconst KEY_Ukranian_i = detail.KEY_Ukranian_i\nexport KEY_Ukranian_i\n\nconst KEY_Ukrainian_yi = detail.KEY_Ukrainian_yi\nexport KEY_Ukrainian_yi\n\nconst KEY_Ukranian_yi = detail.KEY_Ukranian_yi\nexport KEY_Ukranian_yi\n\nconst KEY_Cyrillic_je = detail.KEY_Cyrillic_je\nexport KEY_Cyrillic_je\n\nconst KEY_Serbian_je = detail.KEY_Serbian_je\nexport KEY_Serbian_je\n\nconst KEY_Cyrillic_lje = detail.KEY_Cyrillic_lje\nexport KEY_Cyrillic_lje\n\nconst KEY_Serbian_lje = detail.KEY_Serbian_lje\nexport KEY_Serbian_lje\n\nconst KEY_Cyrillic_nje = detail.KEY_Cyrillic_nje\nexport KEY_Cyrillic_nje\n\nconst KEY_Serbian_nje = detail.KEY_Serbian_nje\nexport KEY_Serbian_nje\n\nconst KEY_Serbian_tshe = detail.KEY_Serbian_tshe\nexport KEY_Serbian_tshe\n\nconst KEY_Macedonia_kje = detail.KEY_Macedonia_kje\nexport KEY_Macedonia_kje\n\nconst KEY_Ukrainian_ghe_with_upturn = detail.KEY_Ukrainian_ghe_with_upturn\nexport KEY_Ukrainian_ghe_with_upturn\n\nconst KEY_Byelorussian_shortu = detail.KEY_Byelorussian_shortu\nexport KEY_Byelorussian_shortu\n\nconst KEY_Cyrillic_dzhe = detail.KEY_Cyrillic_dzhe\nexport KEY_Cyrillic_dzhe\n\nconst KEY_Serbian_dze = detail.KEY_Serbian_dze\nexport KEY_Serbian_dze\n\nconst KEY_numerosign = detail.KEY_numerosign\nexport KEY_numerosign\n\nconst KEY_Serbian_DJE = detail.KEY_Serbian_DJE\nexport KEY_Serbian_DJE\n\nconst KEY_Macedonia_GJE = detail.KEY_Macedonia_GJE\nexport KEY_Macedonia_GJE\n\nconst KEY_Cyrillic_IO = detail.KEY_Cyrillic_IO\nexport KEY_Cyrillic_IO\n\nconst KEY_Ukrainian_IE = detail.KEY_Ukrainian_IE\nexport KEY_Ukrainian_IE\n\nconst KEY_Ukranian_JE = detail.KEY_Ukranian_JE\nexport KEY_Ukranian_JE\n\nconst KEY_Macedonia_DSE = detail.KEY_Macedonia_DSE\nexport KEY_Macedonia_DSE\n\nconst KEY_Ukrainian_I = detail.KEY_Ukrainian_I\nexport KEY_Ukrainian_I\n\nconst KEY_Ukranian_I = detail.KEY_Ukranian_I\nexport KEY_Ukranian_I\n\nconst KEY_Ukrainian_YI = detail.KEY_Ukrainian_YI\nexport KEY_Ukrainian_YI\n\nconst KEY_Ukranian_YI = detail.KEY_Ukranian_YI\nexport KEY_Ukranian_YI\n\nconst KEY_Cyrillic_JE = detail.KEY_Cyrillic_JE\nexport KEY_Cyrillic_JE\n\nconst KEY_Serbian_JE = detail.KEY_Serbian_JE\nexport KEY_Serbian_JE\n\nconst KEY_Cyrillic_LJE = detail.KEY_Cyrillic_LJE\nexport KEY_Cyrillic_LJE\n\nconst KEY_Serbian_LJE = detail.KEY_Serbian_LJE\nexport KEY_Serbian_LJE\n\nconst KEY_Cyrillic_NJE = detail.KEY_Cyrillic_NJE\nexport KEY_Cyrillic_NJE\n\nconst KEY_Serbian_NJE = detail.KEY_Serbian_NJE\nexport KEY_Serbian_NJE\n\nconst KEY_Serbian_TSHE = detail.KEY_Serbian_TSHE\nexport KEY_Serbian_TSHE\n\nconst KEY_Macedonia_KJE = detail.KEY_Macedonia_KJE\nexport KEY_Macedonia_KJE\n\nconst KEY_Ukrainian_GHE_WITH_UPTURN = detail.KEY_Ukrainian_GHE_WITH_UPTURN\nexport KEY_Ukrainian_GHE_WITH_UPTURN\n\nconst KEY_Byelorussian_SHORTU = detail.KEY_Byelorussian_SHORTU\nexport KEY_Byelorussian_SHORTU\n\nconst KEY_Cyrillic_DZHE = detail.KEY_Cyrillic_DZHE\nexport KEY_Cyrillic_DZHE\n\nconst KEY_Serbian_DZE = detail.KEY_Serbian_DZE\nexport KEY_Serbian_DZE\n\nconst KEY_Cyrillic_yu = detail.KEY_Cyrillic_yu\nexport KEY_Cyrillic_yu\n\nconst KEY_Cyrillic_a = detail.KEY_Cyrillic_a\nexport KEY_Cyrillic_a\n\nconst KEY_Cyrillic_be = detail.KEY_Cyrillic_be\nexport KEY_Cyrillic_be\n\nconst KEY_Cyrillic_tse = detail.KEY_Cyrillic_tse\nexport KEY_Cyrillic_tse\n\nconst KEY_Cyrillic_de = detail.KEY_Cyrillic_de\nexport KEY_Cyrillic_de\n\nconst KEY_Cyrillic_ie = detail.KEY_Cyrillic_ie\nexport KEY_Cyrillic_ie\n\nconst KEY_Cyrillic_ef = detail.KEY_Cyrillic_ef\nexport KEY_Cyrillic_ef\n\nconst KEY_Cyrillic_ghe = detail.KEY_Cyrillic_ghe\nexport KEY_Cyrillic_ghe\n\nconst KEY_Cyrillic_ha = detail.KEY_Cyrillic_ha\nexport KEY_Cyrillic_ha\n\nconst KEY_Cyrillic_i = detail.KEY_Cyrillic_i\nexport KEY_Cyrillic_i\n\nconst KEY_Cyrillic_shorti = detail.KEY_Cyrillic_shorti\nexport KEY_Cyrillic_shorti\n\nconst KEY_Cyrillic_ka = detail.KEY_Cyrillic_ka\nexport KEY_Cyrillic_ka\n\nconst KEY_Cyrillic_el = detail.KEY_Cyrillic_el\nexport KEY_Cyrillic_el\n\nconst KEY_Cyrillic_em = detail.KEY_Cyrillic_em\nexport KEY_Cyrillic_em\n\nconst KEY_Cyrillic_en = detail.KEY_Cyrillic_en\nexport KEY_Cyrillic_en\n\nconst KEY_Cyrillic_o = detail.KEY_Cyrillic_o\nexport KEY_Cyrillic_o\n\nconst KEY_Cyrillic_pe = detail.KEY_Cyrillic_pe\nexport KEY_Cyrillic_pe\n\nconst KEY_Cyrillic_ya = detail.KEY_Cyrillic_ya\nexport KEY_Cyrillic_ya\n\nconst KEY_Cyrillic_er = detail.KEY_Cyrillic_er\nexport KEY_Cyrillic_er\n\nconst KEY_Cyrillic_es = detail.KEY_Cyrillic_es\nexport KEY_Cyrillic_es\n\nconst KEY_Cyrillic_te = detail.KEY_Cyrillic_te\nexport KEY_Cyrillic_te\n\nconst KEY_Cyrillic_u = detail.KEY_Cyrillic_u\nexport KEY_Cyrillic_u\n\nconst KEY_Cyrillic_zhe = detail.KEY_Cyrillic_zhe\nexport KEY_Cyrillic_zhe\n\nconst KEY_Cyrillic_ve = detail.KEY_Cyrillic_ve\nexport KEY_Cyrillic_ve\n\nconst KEY_Cyrillic_softsign = detail.KEY_Cyrillic_softsign\nexport KEY_Cyrillic_softsign\n\nconst KEY_Cyrillic_yeru = detail.KEY_Cyrillic_yeru\nexport KEY_Cyrillic_yeru\n\nconst KEY_Cyrillic_ze = detail.KEY_Cyrillic_ze\nexport KEY_Cyrillic_ze\n\nconst KEY_Cyrillic_sha = detail.KEY_Cyrillic_sha\nexport KEY_Cyrillic_sha\n\nconst KEY_Cyrillic_e = detail.KEY_Cyrillic_e\nexport KEY_Cyrillic_e\n\nconst KEY_Cyrillic_shcha = detail.KEY_Cyrillic_shcha\nexport KEY_Cyrillic_shcha\n\nconst KEY_Cyrillic_che = detail.KEY_Cyrillic_che\nexport KEY_Cyrillic_che\n\nconst KEY_Cyrillic_hardsign = detail.KEY_Cyrillic_hardsign\nexport KEY_Cyrillic_hardsign\n\nconst KEY_Cyrillic_YU = detail.KEY_Cyrillic_YU\nexport KEY_Cyrillic_YU\n\nconst KEY_Cyrillic_A = detail.KEY_Cyrillic_A\nexport KEY_Cyrillic_A\n\nconst KEY_Cyrillic_BE = detail.KEY_Cyrillic_BE\nexport KEY_Cyrillic_BE\n\nconst KEY_Cyrillic_TSE = detail.KEY_Cyrillic_TSE\nexport KEY_Cyrillic_TSE\n\nconst KEY_Cyrillic_DE = detail.KEY_Cyrillic_DE\nexport KEY_Cyrillic_DE\n\nconst KEY_Cyrillic_IE = detail.KEY_Cyrillic_IE\nexport KEY_Cyrillic_IE\n\nconst KEY_Cyrillic_EF = detail.KEY_Cyrillic_EF\nexport KEY_Cyrillic_EF\n\nconst KEY_Cyrillic_GHE = detail.KEY_Cyrillic_GHE\nexport KEY_Cyrillic_GHE\n\nconst KEY_Cyrillic_HA = detail.KEY_Cyrillic_HA\nexport KEY_Cyrillic_HA\n\nconst KEY_Cyrillic_I = detail.KEY_Cyrillic_I\nexport KEY_Cyrillic_I\n\nconst KEY_Cyrillic_SHORTI = detail.KEY_Cyrillic_SHORTI\nexport KEY_Cyrillic_SHORTI\n\nconst KEY_Cyrillic_KA = detail.KEY_Cyrillic_KA\nexport KEY_Cyrillic_KA\n\nconst KEY_Cyrillic_EL = detail.KEY_Cyrillic_EL\nexport KEY_Cyrillic_EL\n\nconst KEY_Cyrillic_EM = detail.KEY_Cyrillic_EM\nexport KEY_Cyrillic_EM\n\nconst KEY_Cyrillic_EN = detail.KEY_Cyrillic_EN\nexport KEY_Cyrillic_EN\n\nconst KEY_Cyrillic_O = detail.KEY_Cyrillic_O\nexport KEY_Cyrillic_O\n\nconst KEY_Cyrillic_PE = detail.KEY_Cyrillic_PE\nexport KEY_Cyrillic_PE\n\nconst KEY_Cyrillic_YA = detail.KEY_Cyrillic_YA\nexport KEY_Cyrillic_YA\n\nconst KEY_Cyrillic_ER = detail.KEY_Cyrillic_ER\nexport KEY_Cyrillic_ER\n\nconst KEY_Cyrillic_ES = detail.KEY_Cyrillic_ES\nexport KEY_Cyrillic_ES\n\nconst KEY_Cyrillic_TE = detail.KEY_Cyrillic_TE\nexport KEY_Cyrillic_TE\n\nconst KEY_Cyrillic_U = detail.KEY_Cyrillic_U\nexport KEY_Cyrillic_U\n\nconst KEY_Cyrillic_ZHE = detail.KEY_Cyrillic_ZHE\nexport KEY_Cyrillic_ZHE\n\nconst KEY_Cyrillic_VE = detail.KEY_Cyrillic_VE\nexport KEY_Cyrillic_VE\n\nconst KEY_Cyrillic_SOFTSIGN = detail.KEY_Cyrillic_SOFTSIGN\nexport KEY_Cyrillic_SOFTSIGN\n\nconst KEY_Cyrillic_YERU = detail.KEY_Cyrillic_YERU\nexport KEY_Cyrillic_YERU\n\nconst KEY_Cyrillic_ZE = detail.KEY_Cyrillic_ZE\nexport KEY_Cyrillic_ZE\n\nconst KEY_Cyrillic_SHA = detail.KEY_Cyrillic_SHA\nexport KEY_Cyrillic_SHA\n\nconst KEY_Cyrillic_E = detail.KEY_Cyrillic_E\nexport KEY_Cyrillic_E\n\nconst KEY_Cyrillic_SHCHA = detail.KEY_Cyrillic_SHCHA\nexport KEY_Cyrillic_SHCHA\n\nconst KEY_Cyrillic_CHE = detail.KEY_Cyrillic_CHE\nexport KEY_Cyrillic_CHE\n\nconst KEY_Cyrillic_HARDSIGN = detail.KEY_Cyrillic_HARDSIGN\nexport KEY_Cyrillic_HARDSIGN\n\nconst KEY_Greek_ALPHAaccent = detail.KEY_Greek_ALPHAaccent\nexport KEY_Greek_ALPHAaccent\n\nconst KEY_Greek_EPSILONaccent = detail.KEY_Greek_EPSILONaccent\nexport KEY_Greek_EPSILONaccent\n\nconst KEY_Greek_ETAaccent = detail.KEY_Greek_ETAaccent\nexport KEY_Greek_ETAaccent\n\nconst KEY_Greek_IOTAaccent = detail.KEY_Greek_IOTAaccent\nexport KEY_Greek_IOTAaccent\n\nconst KEY_Greek_IOTAdieresis = detail.KEY_Greek_IOTAdieresis\nexport KEY_Greek_IOTAdieresis\n\nconst KEY_Greek_IOTAdiaeresis = detail.KEY_Greek_IOTAdiaeresis\nexport KEY_Greek_IOTAdiaeresis\n\nconst KEY_Greek_OMICRONaccent = detail.KEY_Greek_OMICRONaccent\nexport KEY_Greek_OMICRONaccent\n\nconst KEY_Greek_UPSILONaccent = detail.KEY_Greek_UPSILONaccent\nexport KEY_Greek_UPSILONaccent\n\nconst KEY_Greek_UPSILONdieresis = detail.KEY_Greek_UPSILONdieresis\nexport KEY_Greek_UPSILONdieresis\n\nconst KEY_Greek_OMEGAaccent = detail.KEY_Greek_OMEGAaccent\nexport KEY_Greek_OMEGAaccent\n\nconst KEY_Greek_accentdieresis = detail.KEY_Greek_accentdieresis\nexport KEY_Greek_accentdieresis\n\nconst KEY_Greek_horizbar = detail.KEY_Greek_horizbar\nexport KEY_Greek_horizbar\n\nconst KEY_Greek_alphaaccent = detail.KEY_Greek_alphaaccent\nexport KEY_Greek_alphaaccent\n\nconst KEY_Greek_epsilonaccent = detail.KEY_Greek_epsilonaccent\nexport KEY_Greek_epsilonaccent\n\nconst KEY_Greek_etaaccent = detail.KEY_Greek_etaaccent\nexport KEY_Greek_etaaccent\n\nconst KEY_Greek_iotaaccent = detail.KEY_Greek_iotaaccent\nexport KEY_Greek_iotaaccent\n\nconst KEY_Greek_iotadieresis = detail.KEY_Greek_iotadieresis\nexport KEY_Greek_iotadieresis\n\nconst KEY_Greek_iotaaccentdieresis = detail.KEY_Greek_iotaaccentdieresis\nexport KEY_Greek_iotaaccentdieresis\n\nconst KEY_Greek_omicronaccent = detail.KEY_Greek_omicronaccent\nexport KEY_Greek_omicronaccent\n\nconst KEY_Greek_upsilonaccent = detail.KEY_Greek_upsilonaccent\nexport KEY_Greek_upsilonaccent\n\nconst KEY_Greek_upsilondieresis = detail.KEY_Greek_upsilondieresis\nexport KEY_Greek_upsilondieresis\n\nconst KEY_Greek_upsilonaccentdieresis = detail.KEY_Greek_upsilonaccentdieresis\nexport KEY_Greek_upsilonaccentdieresis\n\nconst KEY_Greek_omegaaccent = detail.KEY_Greek_omegaaccent\nexport KEY_Greek_omegaaccent\n\nconst KEY_Greek_ALPHA = detail.KEY_Greek_ALPHA\nexport KEY_Greek_ALPHA\n\nconst KEY_Greek_BETA = detail.KEY_Greek_BETA\nexport KEY_Greek_BETA\n\nconst KEY_Greek_GAMMA = detail.KEY_Greek_GAMMA\nexport KEY_Greek_GAMMA\n\nconst KEY_Greek_DELTA = detail.KEY_Greek_DELTA\nexport KEY_Greek_DELTA\n\nconst KEY_Greek_EPSILON = detail.KEY_Greek_EPSILON\nexport KEY_Greek_EPSILON\n\nconst KEY_Greek_ZETA = detail.KEY_Greek_ZETA\nexport KEY_Greek_ZETA\n\nconst KEY_Greek_ETA = detail.KEY_Greek_ETA\nexport KEY_Greek_ETA\n\nconst KEY_Greek_THETA = detail.KEY_Greek_THETA\nexport KEY_Greek_THETA\n\nconst KEY_Greek_IOTA = detail.KEY_Greek_IOTA\nexport KEY_Greek_IOTA\n\nconst KEY_Greek_KAPPA = detail.KEY_Greek_KAPPA\nexport KEY_Greek_KAPPA\n\nconst KEY_Greek_LAMDA = detail.KEY_Greek_LAMDA\nexport KEY_Greek_LAMDA\n\nconst KEY_Greek_LAMBDA = detail.KEY_Greek_LAMBDA\nexport KEY_Greek_LAMBDA\n\nconst KEY_Greek_MU = detail.KEY_Greek_MU\nexport KEY_Greek_MU\n\nconst KEY_Greek_NU = detail.KEY_Greek_NU\nexport KEY_Greek_NU\n\nconst KEY_Greek_XI = detail.KEY_Greek_XI\nexport KEY_Greek_XI\n\nconst KEY_Greek_OMICRON = detail.KEY_Greek_OMICRON\nexport KEY_Greek_OMICRON\n\nconst KEY_Greek_PI = detail.KEY_Greek_PI\nexport KEY_Greek_PI\n\nconst KEY_Greek_RHO = detail.KEY_Greek_RHO\nexport KEY_Greek_RHO\n\nconst KEY_Greek_SIGMA = detail.KEY_Greek_SIGMA\nexport KEY_Greek_SIGMA\n\nconst KEY_Greek_TAU = detail.KEY_Greek_TAU\nexport KEY_Greek_TAU\n\nconst KEY_Greek_UPSILON = detail.KEY_Greek_UPSILON\nexport KEY_Greek_UPSILON\n\nconst KEY_Greek_PHI = detail.KEY_Greek_PHI\nexport KEY_Greek_PHI\n\nconst KEY_Greek_CHI = detail.KEY_Greek_CHI\nexport KEY_Greek_CHI\n\nconst KEY_Greek_PSI = detail.KEY_Greek_PSI\nexport KEY_Greek_PSI\n\nconst KEY_Greek_OMEGA = detail.KEY_Greek_OMEGA\nexport KEY_Greek_OMEGA\n\nconst KEY_Greek_alpha = detail.KEY_Greek_alpha\nexport KEY_Greek_alpha\n\nconst KEY_Greek_beta = detail.KEY_Greek_beta\nexport KEY_Greek_beta\n\nconst KEY_Greek_gamma = detail.KEY_Greek_gamma\nexport KEY_Greek_gamma\n\nconst KEY_Greek_delta = detail.KEY_Greek_delta\nexport KEY_Greek_delta\n\nconst KEY_Greek_epsilon = detail.KEY_Greek_epsilon\nexport KEY_Greek_epsilon\n\nconst KEY_Greek_zeta = detail.KEY_Greek_zeta\nexport KEY_Greek_zeta\n\nconst KEY_Greek_eta = detail.KEY_Greek_eta\nexport KEY_Greek_eta\n\nconst KEY_Greek_theta = detail.KEY_Greek_theta\nexport KEY_Greek_theta\n\nconst KEY_Greek_iota = detail.KEY_Greek_iota\nexport KEY_Greek_iota\n\nconst KEY_Greek_kappa = detail.KEY_Greek_kappa\nexport KEY_Greek_kappa\n\nconst KEY_Greek_lamda = detail.KEY_Greek_lamda\nexport KEY_Greek_lamda\n\nconst KEY_Greek_lambda = detail.KEY_Greek_lambda\nexport KEY_Greek_lambda\n\nconst KEY_Greek_mu = detail.KEY_Greek_mu\nexport KEY_Greek_mu\n\nconst KEY_Greek_nu = detail.KEY_Greek_nu\nexport KEY_Greek_nu\n\nconst KEY_Greek_xi = detail.KEY_Greek_xi\nexport KEY_Greek_xi\n\nconst KEY_Greek_omicron = detail.KEY_Greek_omicron\nexport KEY_Greek_omicron\n\nconst KEY_Greek_pi = detail.KEY_Greek_pi\nexport KEY_Greek_pi\n\nconst KEY_Greek_rho = detail.KEY_Greek_rho\nexport KEY_Greek_rho\n\nconst KEY_Greek_sigma = detail.KEY_Greek_sigma\nexport KEY_Greek_sigma\n\nconst KEY_Greek_finalsmallsigma = detail.KEY_Greek_finalsmallsigma\nexport KEY_Greek_finalsmallsigma\n\nconst KEY_Greek_tau = detail.KEY_Greek_tau\nexport KEY_Greek_tau\n\nconst KEY_Greek_upsilon = detail.KEY_Greek_upsilon\nexport KEY_Greek_upsilon\n\nconst KEY_Greek_phi = detail.KEY_Greek_phi\nexport KEY_Greek_phi\n\nconst KEY_Greek_chi = detail.KEY_Greek_chi\nexport KEY_Greek_chi\n\nconst KEY_Greek_psi = detail.KEY_Greek_psi\nexport KEY_Greek_psi\n\nconst KEY_Greek_omega = detail.KEY_Greek_omega\nexport KEY_Greek_omega\n\nconst KEY_Greek_switch = detail.KEY_Greek_switch\nexport KEY_Greek_switch\n\nconst KEY_leftradical = detail.KEY_leftradical\nexport KEY_leftradical\n\nconst KEY_topleftradical = detail.KEY_topleftradical\nexport KEY_topleftradical\n\nconst KEY_horizconnector = detail.KEY_horizconnector\nexport KEY_horizconnector\n\nconst KEY_topintegral = detail.KEY_topintegral\nexport KEY_topintegral\n\nconst KEY_botintegral = detail.KEY_botintegral\nexport KEY_botintegral\n\nconst KEY_vertconnector = detail.KEY_vertconnector\nexport KEY_vertconnector\n\nconst KEY_topleftsqbracket = detail.KEY_topleftsqbracket\nexport KEY_topleftsqbracket\n\nconst KEY_botleftsqbracket = detail.KEY_botleftsqbracket\nexport KEY_botleftsqbracket\n\nconst KEY_toprightsqbracket = detail.KEY_toprightsqbracket\nexport KEY_toprightsqbracket\n\nconst KEY_botrightsqbracket = detail.KEY_botrightsqbracket\nexport KEY_botrightsqbracket\n\nconst KEY_topleftparens = detail.KEY_topleftparens\nexport KEY_topleftparens\n\nconst KEY_botleftparens = detail.KEY_botleftparens\nexport KEY_botleftparens\n\nconst KEY_toprightparens = detail.KEY_toprightparens\nexport KEY_toprightparens\n\nconst KEY_botrightparens = detail.KEY_botrightparens\nexport KEY_botrightparens\n\nconst KEY_leftmiddlecurlybrace = detail.KEY_leftmiddlecurlybrace\nexport KEY_leftmiddlecurlybrace\n\nconst KEY_rightmiddlecurlybrace = detail.KEY_rightmiddlecurlybrace\nexport KEY_rightmiddlecurlybrace\n\nconst KEY_topleftsummation = detail.KEY_topleftsummation\nexport KEY_topleftsummation\n\nconst KEY_botleftsummation = detail.KEY_botleftsummation\nexport KEY_botleftsummation\n\nconst KEY_topvertsummationconnector = detail.KEY_topvertsummationconnector\nexport KEY_topvertsummationconnector\n\nconst KEY_botvertsummationconnector = detail.KEY_botvertsummationconnector\nexport KEY_botvertsummationconnector\n\nconst KEY_toprightsummation = detail.KEY_toprightsummation\nexport KEY_toprightsummation\n\nconst KEY_botrightsummation = detail.KEY_botrightsummation\nexport KEY_botrightsummation\n\nconst KEY_rightmiddlesummation = detail.KEY_rightmiddlesummation\nexport KEY_rightmiddlesummation\n\nconst KEY_lessthanequal = detail.KEY_lessthanequal\nexport KEY_lessthanequal\n\nconst KEY_notequal = detail.KEY_notequal\nexport KEY_notequal\n\nconst KEY_greaterthanequal = detail.KEY_greaterthanequal\nexport KEY_greaterthanequal\n\nconst KEY_integral = detail.KEY_integral\nexport KEY_integral\n\nconst KEY_therefore = detail.KEY_therefore\nexport KEY_therefore\n\nconst KEY_variation = detail.KEY_variation\nexport KEY_variation\n\nconst KEY_infinity = detail.KEY_infinity\nexport KEY_infinity\n\nconst KEY_nabla = detail.KEY_nabla\nexport KEY_nabla\n\nconst KEY_approximate = detail.KEY_approximate\nexport KEY_approximate\n\nconst KEY_similarequal = detail.KEY_similarequal\nexport KEY_similarequal\n\nconst KEY_ifonlyif = detail.KEY_ifonlyif\nexport KEY_ifonlyif\n\nconst KEY_implies = detail.KEY_implies\nexport KEY_implies\n\nconst KEY_identical = detail.KEY_identical\nexport KEY_identical\n\nconst KEY_radical = detail.KEY_radical\nexport KEY_radical\n\nconst KEY_includedin = detail.KEY_includedin\nexport KEY_includedin\n\nconst KEY_includes = detail.KEY_includes\nexport KEY_includes\n\nconst KEY_intersection = detail.KEY_intersection\nexport KEY_intersection\n\nconst KEY_union = detail.KEY_union\nexport KEY_union\n\nconst KEY_logicaland = detail.KEY_logicaland\nexport KEY_logicaland\n\nconst KEY_logicalor = detail.KEY_logicalor\nexport KEY_logicalor\n\nconst KEY_partialderivative = detail.KEY_partialderivative\nexport KEY_partialderivative\n\nconst KEY_function = detail.KEY_function\nexport KEY_function\n\nconst KEY_leftarrow = detail.KEY_leftarrow\nexport KEY_leftarrow\n\nconst KEY_uparrow = detail.KEY_uparrow\nexport KEY_uparrow\n\nconst KEY_rightarrow = detail.KEY_rightarrow\nexport KEY_rightarrow\n\nconst KEY_downarrow = detail.KEY_downarrow\nexport KEY_downarrow\n\nconst KEY_blank = detail.KEY_blank\nexport KEY_blank\n\nconst KEY_soliddiamond = detail.KEY_soliddiamond\nexport KEY_soliddiamond\n\nconst KEY_checkerboard = detail.KEY_checkerboard\nexport KEY_checkerboard\n\nconst KEY_ht = detail.KEY_ht\nexport KEY_ht\n\nconst KEY_ff = detail.KEY_ff\nexport KEY_ff\n\nconst KEY_cr = detail.KEY_cr\nexport KEY_cr\n\nconst KEY_lf = detail.KEY_lf\nexport KEY_lf\n\nconst KEY_nl = detail.KEY_nl\nexport KEY_nl\n\nconst KEY_vt = detail.KEY_vt\nexport KEY_vt\n\nconst KEY_lowrightcorner = detail.KEY_lowrightcorner\nexport KEY_lowrightcorner\n\nconst KEY_uprightcorner = detail.KEY_uprightcorner\nexport KEY_uprightcorner\n\nconst KEY_upleftcorner = detail.KEY_upleftcorner\nexport KEY_upleftcorner\n\nconst KEY_lowleftcorner = detail.KEY_lowleftcorner\nexport KEY_lowleftcorner\n\nconst KEY_crossinglines = detail.KEY_crossinglines\nexport KEY_crossinglines\n\nconst KEY_horizlinescan1 = detail.KEY_horizlinescan1\nexport KEY_horizlinescan1\n\nconst KEY_horizlinescan3 = detail.KEY_horizlinescan3\nexport KEY_horizlinescan3\n\nconst KEY_horizlinescan5 = detail.KEY_horizlinescan5\nexport KEY_horizlinescan5\n\nconst KEY_horizlinescan7 = detail.KEY_horizlinescan7\nexport KEY_horizlinescan7\n\nconst KEY_horizlinescan9 = detail.KEY_horizlinescan9\nexport KEY_horizlinescan9\n\nconst KEY_leftt = detail.KEY_leftt\nexport KEY_leftt\n\nconst KEY_rightt = detail.KEY_rightt\nexport KEY_rightt\n\nconst KEY_bott = detail.KEY_bott\nexport KEY_bott\n\nconst KEY_topt = detail.KEY_topt\nexport KEY_topt\n\nconst KEY_vertbar = detail.KEY_vertbar\nexport KEY_vertbar\n\nconst KEY_emspace = detail.KEY_emspace\nexport KEY_emspace\n\nconst KEY_enspace = detail.KEY_enspace\nexport KEY_enspace\n\nconst KEY_em3space = detail.KEY_em3space\nexport KEY_em3space\n\nconst KEY_em4space = detail.KEY_em4space\nexport KEY_em4space\n\nconst KEY_digitspace = detail.KEY_digitspace\nexport KEY_digitspace\n\nconst KEY_punctspace = detail.KEY_punctspace\nexport KEY_punctspace\n\nconst KEY_thinspace = detail.KEY_thinspace\nexport KEY_thinspace\n\nconst KEY_hairspace = detail.KEY_hairspace\nexport KEY_hairspace\n\nconst KEY_emdash = detail.KEY_emdash\nexport KEY_emdash\n\nconst KEY_endash = detail.KEY_endash\nexport KEY_endash\n\nconst KEY_signifblank = detail.KEY_signifblank\nexport KEY_signifblank\n\nconst KEY_ellipsis = detail.KEY_ellipsis\nexport KEY_ellipsis\n\nconst KEY_doubbaselinedot = detail.KEY_doubbaselinedot\nexport KEY_doubbaselinedot\n\nconst KEY_onethird = detail.KEY_onethird\nexport KEY_onethird\n\nconst KEY_twothirds = detail.KEY_twothirds\nexport KEY_twothirds\n\nconst KEY_onefifth = detail.KEY_onefifth\nexport KEY_onefifth\n\nconst KEY_twofifths = detail.KEY_twofifths\nexport KEY_twofifths\n\nconst KEY_threefifths = detail.KEY_threefifths\nexport KEY_threefifths\n\nconst KEY_fourfifths = detail.KEY_fourfifths\nexport KEY_fourfifths\n\nconst KEY_onesixth = detail.KEY_onesixth\nexport KEY_onesixth\n\nconst KEY_fivesixths = detail.KEY_fivesixths\nexport KEY_fivesixths\n\nconst KEY_careof = detail.KEY_careof\nexport KEY_careof\n\nconst KEY_figdash = detail.KEY_figdash\nexport KEY_figdash\n\nconst KEY_leftanglebracket = detail.KEY_leftanglebracket\nexport KEY_leftanglebracket\n\nconst KEY_decimalpoint = detail.KEY_decimalpoint\nexport KEY_decimalpoint\n\nconst KEY_rightanglebracket = detail.KEY_rightanglebracket\nexport KEY_rightanglebracket\n\nconst KEY_marker = detail.KEY_marker\nexport KEY_marker\n\nconst KEY_oneeighth = detail.KEY_oneeighth\nexport KEY_oneeighth\n\nconst KEY_threeeighths = detail.KEY_threeeighths\nexport KEY_threeeighths\n\nconst KEY_fiveeighths = detail.KEY_fiveeighths\nexport KEY_fiveeighths\n\nconst KEY_seveneighths = detail.KEY_seveneighths\nexport KEY_seveneighths\n\nconst KEY_trademark = detail.KEY_trademark\nexport KEY_trademark\n\nconst KEY_signaturemark = detail.KEY_signaturemark\nexport KEY_signaturemark\n\nconst KEY_trademarkincircle = detail.KEY_trademarkincircle\nexport KEY_trademarkincircle\n\nconst KEY_leftopentriangle = detail.KEY_leftopentriangle\nexport KEY_leftopentriangle\n\nconst KEY_rightopentriangle = detail.KEY_rightopentriangle\nexport KEY_rightopentriangle\n\nconst KEY_emopencircle = detail.KEY_emopencircle\nexport KEY_emopencircle\n\nconst KEY_emopenrectangle = detail.KEY_emopenrectangle\nexport KEY_emopenrectangle\n\nconst KEY_leftsinglequotemark = detail.KEY_leftsinglequotemark\nexport KEY_leftsinglequotemark\n\nconst KEY_rightsinglequotemark = detail.KEY_rightsinglequotemark\nexport KEY_rightsinglequotemark\n\nconst KEY_leftdoublequotemark = detail.KEY_leftdoublequotemark\nexport KEY_leftdoublequotemark\n\nconst KEY_rightdoublequotemark = detail.KEY_rightdoublequotemark\nexport KEY_rightdoublequotemark\n\nconst KEY_prescription = detail.KEY_prescription\nexport KEY_prescription\n\nconst KEY_permille = detail.KEY_permille\nexport KEY_permille\n\nconst KEY_minutes = detail.KEY_minutes\nexport KEY_minutes\n\nconst KEY_seconds = detail.KEY_seconds\nexport KEY_seconds\n\nconst KEY_latincross = detail.KEY_latincross\nexport KEY_latincross\n\nconst KEY_hexagram = detail.KEY_hexagram\nexport KEY_hexagram\n\nconst KEY_filledrectbullet = detail.KEY_filledrectbullet\nexport KEY_filledrectbullet\n\nconst KEY_filledlefttribullet = detail.KEY_filledlefttribullet\nexport KEY_filledlefttribullet\n\nconst KEY_filledrighttribullet = detail.KEY_filledrighttribullet\nexport KEY_filledrighttribullet\n\nconst KEY_emfilledcircle = detail.KEY_emfilledcircle\nexport KEY_emfilledcircle\n\nconst KEY_emfilledrect = detail.KEY_emfilledrect\nexport KEY_emfilledrect\n\nconst KEY_enopencircbullet = detail.KEY_enopencircbullet\nexport KEY_enopencircbullet\n\nconst KEY_enopensquarebullet = detail.KEY_enopensquarebullet\nexport KEY_enopensquarebullet\n\nconst KEY_openrectbullet = detail.KEY_openrectbullet\nexport KEY_openrectbullet\n\nconst KEY_opentribulletup = detail.KEY_opentribulletup\nexport KEY_opentribulletup\n\nconst KEY_opentribulletdown = detail.KEY_opentribulletdown\nexport KEY_opentribulletdown\n\nconst KEY_openstar = detail.KEY_openstar\nexport KEY_openstar\n\nconst KEY_enfilledcircbullet = detail.KEY_enfilledcircbullet\nexport KEY_enfilledcircbullet\n\nconst KEY_enfilledsqbullet = detail.KEY_enfilledsqbullet\nexport KEY_enfilledsqbullet\n\nconst KEY_filledtribulletup = detail.KEY_filledtribulletup\nexport KEY_filledtribulletup\n\nconst KEY_filledtribulletdown = detail.KEY_filledtribulletdown\nexport KEY_filledtribulletdown\n\nconst KEY_leftpointer = detail.KEY_leftpointer\nexport KEY_leftpointer\n\nconst KEY_rightpointer = detail.KEY_rightpointer\nexport KEY_rightpointer\n\nconst KEY_club = detail.KEY_club\nexport KEY_club\n\nconst KEY_diamond = detail.KEY_diamond\nexport KEY_diamond\n\nconst KEY_heart = detail.KEY_heart\nexport KEY_heart\n\nconst KEY_maltesecross = detail.KEY_maltesecross\nexport KEY_maltesecross\n\nconst KEY_dagger = detail.KEY_dagger\nexport KEY_dagger\n\nconst KEY_doubledagger = detail.KEY_doubledagger\nexport KEY_doubledagger\n\nconst KEY_checkmark = detail.KEY_checkmark\nexport KEY_checkmark\n\nconst KEY_ballotcross = detail.KEY_ballotcross\nexport KEY_ballotcross\n\nconst KEY_musicalsharp = detail.KEY_musicalsharp\nexport KEY_musicalsharp\n\nconst KEY_musicalflat = detail.KEY_musicalflat\nexport KEY_musicalflat\n\nconst KEY_malesymbol = detail.KEY_malesymbol\nexport KEY_malesymbol\n\nconst KEY_femalesymbol = detail.KEY_femalesymbol\nexport KEY_femalesymbol\n\nconst KEY_telephone = detail.KEY_telephone\nexport KEY_telephone\n\nconst KEY_telephonerecorder = detail.KEY_telephonerecorder\nexport KEY_telephonerecorder\n\nconst KEY_phonographcopyright = detail.KEY_phonographcopyright\nexport KEY_phonographcopyright\n\nconst KEY_caret = detail.KEY_caret\nexport KEY_caret\n\nconst KEY_singlelowquotemark = detail.KEY_singlelowquotemark\nexport KEY_singlelowquotemark\n\nconst KEY_doublelowquotemark = detail.KEY_doublelowquotemark\nexport KEY_doublelowquotemark\n\nconst KEY_cursor = detail.KEY_cursor\nexport KEY_cursor\n\nconst KEY_leftcaret = detail.KEY_leftcaret\nexport KEY_leftcaret\n\nconst KEY_rightcaret = detail.KEY_rightcaret\nexport KEY_rightcaret\n\nconst KEY_downcaret = detail.KEY_downcaret\nexport KEY_downcaret\n\nconst KEY_upcaret = detail.KEY_upcaret\nexport KEY_upcaret\n\nconst KEY_overbar = detail.KEY_overbar\nexport KEY_overbar\n\nconst KEY_downtack = detail.KEY_downtack\nexport KEY_downtack\n\nconst KEY_upshoe = detail.KEY_upshoe\nexport KEY_upshoe\n\nconst KEY_downstile = detail.KEY_downstile\nexport KEY_downstile\n\nconst KEY_underbar = detail.KEY_underbar\nexport KEY_underbar\n\nconst KEY_jot = detail.KEY_jot\nexport KEY_jot\n\nconst KEY_quad = detail.KEY_quad\nexport KEY_quad\n\nconst KEY_uptack = detail.KEY_uptack\nexport KEY_uptack\n\nconst KEY_circle = detail.KEY_circle\nexport KEY_circle\n\nconst KEY_upstile = detail.KEY_upstile\nexport KEY_upstile\n\nconst KEY_downshoe = detail.KEY_downshoe\nexport KEY_downshoe\n\nconst KEY_rightshoe = detail.KEY_rightshoe\nexport KEY_rightshoe\n\nconst KEY_leftshoe = detail.KEY_leftshoe\nexport KEY_leftshoe\n\nconst KEY_lefttack = detail.KEY_lefttack\nexport KEY_lefttack\n\nconst KEY_righttack = detail.KEY_righttack\nexport KEY_righttack\n\nconst KEY_hebrew_doublelowline = detail.KEY_hebrew_doublelowline\nexport KEY_hebrew_doublelowline\n\nconst KEY_hebrew_aleph = detail.KEY_hebrew_aleph\nexport KEY_hebrew_aleph\n\nconst KEY_hebrew_bet = detail.KEY_hebrew_bet\nexport KEY_hebrew_bet\n\nconst KEY_hebrew_beth = detail.KEY_hebrew_beth\nexport KEY_hebrew_beth\n\nconst KEY_hebrew_gimel = detail.KEY_hebrew_gimel\nexport KEY_hebrew_gimel\n\nconst KEY_hebrew_gimmel = detail.KEY_hebrew_gimmel\nexport KEY_hebrew_gimmel\n\nconst KEY_hebrew_dalet = detail.KEY_hebrew_dalet\nexport KEY_hebrew_dalet\n\nconst KEY_hebrew_daleth = detail.KEY_hebrew_daleth\nexport KEY_hebrew_daleth\n\nconst KEY_hebrew_he = detail.KEY_hebrew_he\nexport KEY_hebrew_he\n\nconst KEY_hebrew_waw = detail.KEY_hebrew_waw\nexport KEY_hebrew_waw\n\nconst KEY_hebrew_zain = detail.KEY_hebrew_zain\nexport KEY_hebrew_zain\n\nconst KEY_hebrew_zayin = detail.KEY_hebrew_zayin\nexport KEY_hebrew_zayin\n\nconst KEY_hebrew_chet = detail.KEY_hebrew_chet\nexport KEY_hebrew_chet\n\nconst KEY_hebrew_het = detail.KEY_hebrew_het\nexport KEY_hebrew_het\n\nconst KEY_hebrew_tet = detail.KEY_hebrew_tet\nexport KEY_hebrew_tet\n\nconst KEY_hebrew_teth = detail.KEY_hebrew_teth\nexport KEY_hebrew_teth\n\nconst KEY_hebrew_yod = detail.KEY_hebrew_yod\nexport KEY_hebrew_yod\n\nconst KEY_hebrew_finalkaph = detail.KEY_hebrew_finalkaph\nexport KEY_hebrew_finalkaph\n\nconst KEY_hebrew_kaph = detail.KEY_hebrew_kaph\nexport KEY_hebrew_kaph\n\nconst KEY_hebrew_lamed = detail.KEY_hebrew_lamed\nexport KEY_hebrew_lamed\n\nconst KEY_hebrew_finalmem = detail.KEY_hebrew_finalmem\nexport KEY_hebrew_finalmem\n\nconst KEY_hebrew_mem = detail.KEY_hebrew_mem\nexport KEY_hebrew_mem\n\nconst KEY_hebrew_finalnun = detail.KEY_hebrew_finalnun\nexport KEY_hebrew_finalnun\n\nconst KEY_hebrew_nun = detail.KEY_hebrew_nun\nexport KEY_hebrew_nun\n\nconst KEY_hebrew_samech = detail.KEY_hebrew_samech\nexport KEY_hebrew_samech\n\nconst KEY_hebrew_samekh = detail.KEY_hebrew_samekh\nexport KEY_hebrew_samekh\n\nconst KEY_hebrew_ayin = detail.KEY_hebrew_ayin\nexport KEY_hebrew_ayin\n\nconst KEY_hebrew_finalpe = detail.KEY_hebrew_finalpe\nexport KEY_hebrew_finalpe\n\nconst KEY_hebrew_pe = detail.KEY_hebrew_pe\nexport KEY_hebrew_pe\n\nconst KEY_hebrew_finalzade = detail.KEY_hebrew_finalzade\nexport KEY_hebrew_finalzade\n\nconst KEY_hebrew_finalzadi = detail.KEY_hebrew_finalzadi\nexport KEY_hebrew_finalzadi\n\nconst KEY_hebrew_zade = detail.KEY_hebrew_zade\nexport KEY_hebrew_zade\n\nconst KEY_hebrew_zadi = detail.KEY_hebrew_zadi\nexport KEY_hebrew_zadi\n\nconst KEY_hebrew_qoph = detail.KEY_hebrew_qoph\nexport KEY_hebrew_qoph\n\nconst KEY_hebrew_kuf = detail.KEY_hebrew_kuf\nexport KEY_hebrew_kuf\n\nconst KEY_hebrew_resh = detail.KEY_hebrew_resh\nexport KEY_hebrew_resh\n\nconst KEY_hebrew_shin = detail.KEY_hebrew_shin\nexport KEY_hebrew_shin\n\nconst KEY_hebrew_taw = detail.KEY_hebrew_taw\nexport KEY_hebrew_taw\n\nconst KEY_hebrew_taf = detail.KEY_hebrew_taf\nexport KEY_hebrew_taf\n\nconst KEY_Hebrew_switch = detail.KEY_Hebrew_switch\nexport KEY_Hebrew_switch\n\nconst KEY_Thai_kokai = detail.KEY_Thai_kokai\nexport KEY_Thai_kokai\n\nconst KEY_Thai_khokhai = detail.KEY_Thai_khokhai\nexport KEY_Thai_khokhai\n\nconst KEY_Thai_khokhuat = detail.KEY_Thai_khokhuat\nexport KEY_Thai_khokhuat\n\nconst KEY_Thai_khokhwai = detail.KEY_Thai_khokhwai\nexport KEY_Thai_khokhwai\n\nconst KEY_Thai_khokhon = detail.KEY_Thai_khokhon\nexport KEY_Thai_khokhon\n\nconst KEY_Thai_khorakhang = detail.KEY_Thai_khorakhang\nexport KEY_Thai_khorakhang\n\nconst KEY_Thai_ngongu = detail.KEY_Thai_ngongu\nexport KEY_Thai_ngongu\n\nconst KEY_Thai_chochan = detail.KEY_Thai_chochan\nexport KEY_Thai_chochan\n\nconst KEY_Thai_choching = detail.KEY_Thai_choching\nexport KEY_Thai_choching\n\nconst KEY_Thai_chochang = detail.KEY_Thai_chochang\nexport KEY_Thai_chochang\n\nconst KEY_Thai_soso = detail.KEY_Thai_soso\nexport KEY_Thai_soso\n\nconst KEY_Thai_chochoe = detail.KEY_Thai_chochoe\nexport KEY_Thai_chochoe\n\nconst KEY_Thai_yoying = detail.KEY_Thai_yoying\nexport KEY_Thai_yoying\n\nconst KEY_Thai_dochada = detail.KEY_Thai_dochada\nexport KEY_Thai_dochada\n\nconst KEY_Thai_topatak = detail.KEY_Thai_topatak\nexport KEY_Thai_topatak\n\nconst KEY_Thai_thothan = detail.KEY_Thai_thothan\nexport KEY_Thai_thothan\n\nconst KEY_Thai_thonangmontho = detail.KEY_Thai_thonangmontho\nexport KEY_Thai_thonangmontho\n\nconst KEY_Thai_thophuthao = detail.KEY_Thai_thophuthao\nexport KEY_Thai_thophuthao\n\nconst KEY_Thai_nonen = detail.KEY_Thai_nonen\nexport KEY_Thai_nonen\n\nconst KEY_Thai_dodek = detail.KEY_Thai_dodek\nexport KEY_Thai_dodek\n\nconst KEY_Thai_totao = detail.KEY_Thai_totao\nexport KEY_Thai_totao\n\nconst KEY_Thai_thothung = detail.KEY_Thai_thothung\nexport KEY_Thai_thothung\n\nconst KEY_Thai_thothahan = detail.KEY_Thai_thothahan\nexport KEY_Thai_thothahan\n\nconst KEY_Thai_thothong = detail.KEY_Thai_thothong\nexport KEY_Thai_thothong\n\nconst KEY_Thai_nonu = detail.KEY_Thai_nonu\nexport KEY_Thai_nonu\n\nconst KEY_Thai_bobaimai = detail.KEY_Thai_bobaimai\nexport KEY_Thai_bobaimai\n\nconst KEY_Thai_popla = detail.KEY_Thai_popla\nexport KEY_Thai_popla\n\nconst KEY_Thai_phophung = detail.KEY_Thai_phophung\nexport KEY_Thai_phophung\n\nconst KEY_Thai_fofa = detail.KEY_Thai_fofa\nexport KEY_Thai_fofa\n\nconst KEY_Thai_phophan = detail.KEY_Thai_phophan\nexport KEY_Thai_phophan\n\nconst KEY_Thai_fofan = detail.KEY_Thai_fofan\nexport KEY_Thai_fofan\n\nconst KEY_Thai_phosamphao = detail.KEY_Thai_phosamphao\nexport KEY_Thai_phosamphao\n\nconst KEY_Thai_moma = detail.KEY_Thai_moma\nexport KEY_Thai_moma\n\nconst KEY_Thai_yoyak = detail.KEY_Thai_yoyak\nexport KEY_Thai_yoyak\n\nconst KEY_Thai_rorua = detail.KEY_Thai_rorua\nexport KEY_Thai_rorua\n\nconst KEY_Thai_ru = detail.KEY_Thai_ru\nexport KEY_Thai_ru\n\nconst KEY_Thai_loling = detail.KEY_Thai_loling\nexport KEY_Thai_loling\n\nconst KEY_Thai_lu = detail.KEY_Thai_lu\nexport KEY_Thai_lu\n\nconst KEY_Thai_wowaen = detail.KEY_Thai_wowaen\nexport KEY_Thai_wowaen\n\nconst KEY_Thai_sosala = detail.KEY_Thai_sosala\nexport KEY_Thai_sosala\n\nconst KEY_Thai_sorusi = detail.KEY_Thai_sorusi\nexport KEY_Thai_sorusi\n\nconst KEY_Thai_sosua = detail.KEY_Thai_sosua\nexport KEY_Thai_sosua\n\nconst KEY_Thai_hohip = detail.KEY_Thai_hohip\nexport KEY_Thai_hohip\n\nconst KEY_Thai_lochula = detail.KEY_Thai_lochula\nexport KEY_Thai_lochula\n\nconst KEY_Thai_oang = detail.KEY_Thai_oang\nexport KEY_Thai_oang\n\nconst KEY_Thai_honokhuk = detail.KEY_Thai_honokhuk\nexport KEY_Thai_honokhuk\n\nconst KEY_Thai_paiyannoi = detail.KEY_Thai_paiyannoi\nexport KEY_Thai_paiyannoi\n\nconst KEY_Thai_saraa = detail.KEY_Thai_saraa\nexport KEY_Thai_saraa\n\nconst KEY_Thai_maihanakat = detail.KEY_Thai_maihanakat\nexport KEY_Thai_maihanakat\n\nconst KEY_Thai_saraaa = detail.KEY_Thai_saraaa\nexport KEY_Thai_saraaa\n\nconst KEY_Thai_saraam = detail.KEY_Thai_saraam\nexport KEY_Thai_saraam\n\nconst KEY_Thai_sarai = detail.KEY_Thai_sarai\nexport KEY_Thai_sarai\n\nconst KEY_Thai_saraii = detail.KEY_Thai_saraii\nexport KEY_Thai_saraii\n\nconst KEY_Thai_saraue = detail.KEY_Thai_saraue\nexport KEY_Thai_saraue\n\nconst KEY_Thai_sarauee = detail.KEY_Thai_sarauee\nexport KEY_Thai_sarauee\n\nconst KEY_Thai_sarau = detail.KEY_Thai_sarau\nexport KEY_Thai_sarau\n\nconst KEY_Thai_sarauu = detail.KEY_Thai_sarauu\nexport KEY_Thai_sarauu\n\nconst KEY_Thai_phinthu = detail.KEY_Thai_phinthu\nexport KEY_Thai_phinthu\n\nconst KEY_Thai_maihanakat_maitho = detail.KEY_Thai_maihanakat_maitho\nexport KEY_Thai_maihanakat_maitho\n\nconst KEY_Thai_baht = detail.KEY_Thai_baht\nexport KEY_Thai_baht\n\nconst KEY_Thai_sarae = detail.KEY_Thai_sarae\nexport KEY_Thai_sarae\n\nconst KEY_Thai_saraae = detail.KEY_Thai_saraae\nexport KEY_Thai_saraae\n\nconst KEY_Thai_sarao = detail.KEY_Thai_sarao\nexport KEY_Thai_sarao\n\nconst KEY_Thai_saraaimaimuan = detail.KEY_Thai_saraaimaimuan\nexport KEY_Thai_saraaimaimuan\n\nconst KEY_Thai_saraaimaimalai = detail.KEY_Thai_saraaimaimalai\nexport KEY_Thai_saraaimaimalai\n\nconst KEY_Thai_lakkhangyao = detail.KEY_Thai_lakkhangyao\nexport KEY_Thai_lakkhangyao\n\nconst KEY_Thai_maiyamok = detail.KEY_Thai_maiyamok\nexport KEY_Thai_maiyamok\n\nconst KEY_Thai_maitaikhu = detail.KEY_Thai_maitaikhu\nexport KEY_Thai_maitaikhu\n\nconst KEY_Thai_maiek = detail.KEY_Thai_maiek\nexport KEY_Thai_maiek\n\nconst KEY_Thai_maitho = detail.KEY_Thai_maitho\nexport KEY_Thai_maitho\n\nconst KEY_Thai_maitri = detail.KEY_Thai_maitri\nexport KEY_Thai_maitri\n\nconst KEY_Thai_maichattawa = detail.KEY_Thai_maichattawa\nexport KEY_Thai_maichattawa\n\nconst KEY_Thai_thanthakhat = detail.KEY_Thai_thanthakhat\nexport KEY_Thai_thanthakhat\n\nconst KEY_Thai_nikhahit = detail.KEY_Thai_nikhahit\nexport KEY_Thai_nikhahit\n\nconst KEY_Thai_leksun = detail.KEY_Thai_leksun\nexport KEY_Thai_leksun\n\nconst KEY_Thai_leknung = detail.KEY_Thai_leknung\nexport KEY_Thai_leknung\n\nconst KEY_Thai_leksong = detail.KEY_Thai_leksong\nexport KEY_Thai_leksong\n\nconst KEY_Thai_leksam = detail.KEY_Thai_leksam\nexport KEY_Thai_leksam\n\nconst KEY_Thai_leksi = detail.KEY_Thai_leksi\nexport KEY_Thai_leksi\n\nconst KEY_Thai_lekha = detail.KEY_Thai_lekha\nexport KEY_Thai_lekha\n\nconst KEY_Thai_lekhok = detail.KEY_Thai_lekhok\nexport KEY_Thai_lekhok\n\nconst KEY_Thai_lekchet = detail.KEY_Thai_lekchet\nexport KEY_Thai_lekchet\n\nconst KEY_Thai_lekpaet = detail.KEY_Thai_lekpaet\nexport KEY_Thai_lekpaet\n\nconst KEY_Thai_lekkao = detail.KEY_Thai_lekkao\nexport KEY_Thai_lekkao\n\nconst KEY_Hangul = detail.KEY_Hangul\nexport KEY_Hangul\n\nconst KEY_Hangul_Start = detail.KEY_Hangul_Start\nexport KEY_Hangul_Start\n\nconst KEY_Hangul_End = detail.KEY_Hangul_End\nexport KEY_Hangul_End\n\nconst KEY_Hangul_Hanja = detail.KEY_Hangul_Hanja\nexport KEY_Hangul_Hanja\n\nconst KEY_Hangul_Jamo = detail.KEY_Hangul_Jamo\nexport KEY_Hangul_Jamo\n\nconst KEY_Hangul_Romaja = detail.KEY_Hangul_Romaja\nexport KEY_Hangul_Romaja\n\nconst KEY_Hangul_Codeinput = detail.KEY_Hangul_Codeinput\nexport KEY_Hangul_Codeinput\n\nconst KEY_Hangul_Jeonja = detail.KEY_Hangul_Jeonja\nexport KEY_Hangul_Jeonja\n\nconst KEY_Hangul_Banja = detail.KEY_Hangul_Banja\nexport KEY_Hangul_Banja\n\nconst KEY_Hangul_PreHanja = detail.KEY_Hangul_PreHanja\nexport KEY_Hangul_PreHanja\n\nconst KEY_Hangul_PostHanja = detail.KEY_Hangul_PostHanja\nexport KEY_Hangul_PostHanja\n\nconst KEY_Hangul_SingleCandidate = detail.KEY_Hangul_SingleCandidate\nexport KEY_Hangul_SingleCandidate\n\nconst KEY_Hangul_MultipleCandidate = detail.KEY_Hangul_MultipleCandidate\nexport KEY_Hangul_MultipleCandidate\n\nconst KEY_Hangul_PreviousCandidate = detail.KEY_Hangul_PreviousCandidate\nexport KEY_Hangul_PreviousCandidate\n\nconst KEY_Hangul_Special = detail.KEY_Hangul_Special\nexport KEY_Hangul_Special\n\nconst KEY_Hangul_switch = detail.KEY_Hangul_switch\nexport KEY_Hangul_switch\n\nconst KEY_Hangul_Kiyeog = detail.KEY_Hangul_Kiyeog\nexport KEY_Hangul_Kiyeog\n\nconst KEY_Hangul_SsangKiyeog = detail.KEY_Hangul_SsangKiyeog\nexport KEY_Hangul_SsangKiyeog\n\nconst KEY_Hangul_KiyeogSios = detail.KEY_Hangul_KiyeogSios\nexport KEY_Hangul_KiyeogSios\n\nconst KEY_Hangul_Nieun = detail.KEY_Hangul_Nieun\nexport KEY_Hangul_Nieun\n\nconst KEY_Hangul_NieunJieuj = detail.KEY_Hangul_NieunJieuj\nexport KEY_Hangul_NieunJieuj\n\nconst KEY_Hangul_NieunHieuh = detail.KEY_Hangul_NieunHieuh\nexport KEY_Hangul_NieunHieuh\n\nconst KEY_Hangul_Dikeud = detail.KEY_Hangul_Dikeud\nexport KEY_Hangul_Dikeud\n\nconst KEY_Hangul_SsangDikeud = detail.KEY_Hangul_SsangDikeud\nexport KEY_Hangul_SsangDikeud\n\nconst KEY_Hangul_Rieul = detail.KEY_Hangul_Rieul\nexport KEY_Hangul_Rieul\n\nconst KEY_Hangul_RieulKiyeog = detail.KEY_Hangul_RieulKiyeog\nexport KEY_Hangul_RieulKiyeog\n\nconst KEY_Hangul_RieulMieum = detail.KEY_Hangul_RieulMieum\nexport KEY_Hangul_RieulMieum\n\nconst KEY_Hangul_RieulPieub = detail.KEY_Hangul_RieulPieub\nexport KEY_Hangul_RieulPieub\n\nconst KEY_Hangul_RieulSios = detail.KEY_Hangul_RieulSios\nexport KEY_Hangul_RieulSios\n\nconst KEY_Hangul_RieulTieut = detail.KEY_Hangul_RieulTieut\nexport KEY_Hangul_RieulTieut\n\nconst KEY_Hangul_RieulPhieuf = detail.KEY_Hangul_RieulPhieuf\nexport KEY_Hangul_RieulPhieuf\n\nconst KEY_Hangul_RieulHieuh = detail.KEY_Hangul_RieulHieuh\nexport KEY_Hangul_RieulHieuh\n\nconst KEY_Hangul_Mieum = detail.KEY_Hangul_Mieum\nexport KEY_Hangul_Mieum\n\nconst KEY_Hangul_Pieub = detail.KEY_Hangul_Pieub\nexport KEY_Hangul_Pieub\n\nconst KEY_Hangul_SsangPieub = detail.KEY_Hangul_SsangPieub\nexport KEY_Hangul_SsangPieub\n\nconst KEY_Hangul_PieubSios = detail.KEY_Hangul_PieubSios\nexport KEY_Hangul_PieubSios\n\nconst KEY_Hangul_Sios = detail.KEY_Hangul_Sios\nexport KEY_Hangul_Sios\n\nconst KEY_Hangul_SsangSios = detail.KEY_Hangul_SsangSios\nexport KEY_Hangul_SsangSios\n\nconst KEY_Hangul_Ieung = detail.KEY_Hangul_Ieung\nexport KEY_Hangul_Ieung\n\nconst KEY_Hangul_Jieuj = detail.KEY_Hangul_Jieuj\nexport KEY_Hangul_Jieuj\n\nconst KEY_Hangul_SsangJieuj = detail.KEY_Hangul_SsangJieuj\nexport KEY_Hangul_SsangJieuj\n\nconst KEY_Hangul_Cieuc = detail.KEY_Hangul_Cieuc\nexport KEY_Hangul_Cieuc\n\nconst KEY_Hangul_Khieuq = detail.KEY_Hangul_Khieuq\nexport KEY_Hangul_Khieuq\n\nconst KEY_Hangul_Tieut = detail.KEY_Hangul_Tieut\nexport KEY_Hangul_Tieut\n\nconst KEY_Hangul_Phieuf = detail.KEY_Hangul_Phieuf\nexport KEY_Hangul_Phieuf\n\nconst KEY_Hangul_Hieuh = detail.KEY_Hangul_Hieuh\nexport KEY_Hangul_Hieuh\n\nconst KEY_Hangul_A = detail.KEY_Hangul_A\nexport KEY_Hangul_A\n\nconst KEY_Hangul_AE = detail.KEY_Hangul_AE\nexport KEY_Hangul_AE\n\nconst KEY_Hangul_YA = detail.KEY_Hangul_YA\nexport KEY_Hangul_YA\n\nconst KEY_Hangul_YAE = detail.KEY_Hangul_YAE\nexport KEY_Hangul_YAE\n\nconst KEY_Hangul_EO = detail.KEY_Hangul_EO\nexport KEY_Hangul_EO\n\nconst KEY_Hangul_E = detail.KEY_Hangul_E\nexport KEY_Hangul_E\n\nconst KEY_Hangul_YEO = detail.KEY_Hangul_YEO\nexport KEY_Hangul_YEO\n\nconst KEY_Hangul_YE = detail.KEY_Hangul_YE\nexport KEY_Hangul_YE\n\nconst KEY_Hangul_O = detail.KEY_Hangul_O\nexport KEY_Hangul_O\n\nconst KEY_Hangul_WA = detail.KEY_Hangul_WA\nexport KEY_Hangul_WA\n\nconst KEY_Hangul_WAE = detail.KEY_Hangul_WAE\nexport KEY_Hangul_WAE\n\nconst KEY_Hangul_OE = detail.KEY_Hangul_OE\nexport KEY_Hangul_OE\n\nconst KEY_Hangul_YO = detail.KEY_Hangul_YO\nexport KEY_Hangul_YO\n\nconst KEY_Hangul_U = detail.KEY_Hangul_U\nexport KEY_Hangul_U\n\nconst KEY_Hangul_WEO = detail.KEY_Hangul_WEO\nexport KEY_Hangul_WEO\n\nconst KEY_Hangul_WE = detail.KEY_Hangul_WE\nexport KEY_Hangul_WE\n\nconst KEY_Hangul_WI = detail.KEY_Hangul_WI\nexport KEY_Hangul_WI\n\nconst KEY_Hangul_YU = detail.KEY_Hangul_YU\nexport KEY_Hangul_YU\n\nconst KEY_Hangul_EU = detail.KEY_Hangul_EU\nexport KEY_Hangul_EU\n\nconst KEY_Hangul_YI = detail.KEY_Hangul_YI\nexport KEY_Hangul_YI\n\nconst KEY_Hangul_I = detail.KEY_Hangul_I\nexport KEY_Hangul_I\n\nconst KEY_Hangul_J_Kiyeog = detail.KEY_Hangul_J_Kiyeog\nexport KEY_Hangul_J_Kiyeog\n\nconst KEY_Hangul_J_SsangKiyeog = detail.KEY_Hangul_J_SsangKiyeog\nexport KEY_Hangul_J_SsangKiyeog\n\nconst KEY_Hangul_J_KiyeogSios = detail.KEY_Hangul_J_KiyeogSios\nexport KEY_Hangul_J_KiyeogSios\n\nconst KEY_Hangul_J_Nieun = detail.KEY_Hangul_J_Nieun\nexport KEY_Hangul_J_Nieun\n\nconst KEY_Hangul_J_NieunJieuj = detail.KEY_Hangul_J_NieunJieuj\nexport KEY_Hangul_J_NieunJieuj\n\nconst KEY_Hangul_J_NieunHieuh = detail.KEY_Hangul_J_NieunHieuh\nexport KEY_Hangul_J_NieunHieuh\n\nconst KEY_Hangul_J_Dikeud = detail.KEY_Hangul_J_Dikeud\nexport KEY_Hangul_J_Dikeud\n\nconst KEY_Hangul_J_Rieul = detail.KEY_Hangul_J_Rieul\nexport KEY_Hangul_J_Rieul\n\nconst KEY_Hangul_J_RieulKiyeog = detail.KEY_Hangul_J_RieulKiyeog\nexport KEY_Hangul_J_RieulKiyeog\n\nconst KEY_Hangul_J_RieulMieum = detail.KEY_Hangul_J_RieulMieum\nexport KEY_Hangul_J_RieulMieum\n\nconst KEY_Hangul_J_RieulPieub = detail.KEY_Hangul_J_RieulPieub\nexport KEY_Hangul_J_RieulPieub\n\nconst KEY_Hangul_J_RieulSios = detail.KEY_Hangul_J_RieulSios\nexport KEY_Hangul_J_RieulSios\n\nconst KEY_Hangul_J_RieulTieut = detail.KEY_Hangul_J_RieulTieut\nexport KEY_Hangul_J_RieulTieut\n\nconst KEY_Hangul_J_RieulPhieuf = detail.KEY_Hangul_J_RieulPhieuf\nexport KEY_Hangul_J_RieulPhieuf\n\nconst KEY_Hangul_J_RieulHieuh = detail.KEY_Hangul_J_RieulHieuh\nexport KEY_Hangul_J_RieulHieuh\n\nconst KEY_Hangul_J_Mieum = detail.KEY_Hangul_J_Mieum\nexport KEY_Hangul_J_Mieum\n\nconst KEY_Hangul_J_Pieub = detail.KEY_Hangul_J_Pieub\nexport KEY_Hangul_J_Pieub\n\nconst KEY_Hangul_J_PieubSios = detail.KEY_Hangul_J_PieubSios\nexport KEY_Hangul_J_PieubSios\n\nconst KEY_Hangul_J_Sios = detail.KEY_Hangul_J_Sios\nexport KEY_Hangul_J_Sios\n\nconst KEY_Hangul_J_SsangSios = detail.KEY_Hangul_J_SsangSios\nexport KEY_Hangul_J_SsangSios\n\nconst KEY_Hangul_J_Ieung = detail.KEY_Hangul_J_Ieung\nexport KEY_Hangul_J_Ieung\n\nconst KEY_Hangul_J_Jieuj = detail.KEY_Hangul_J_Jieuj\nexport KEY_Hangul_J_Jieuj\n\nconst KEY_Hangul_J_Cieuc = detail.KEY_Hangul_J_Cieuc\nexport KEY_Hangul_J_Cieuc\n\nconst KEY_Hangul_J_Khieuq = detail.KEY_Hangul_J_Khieuq\nexport KEY_Hangul_J_Khieuq\n\nconst KEY_Hangul_J_Tieut = detail.KEY_Hangul_J_Tieut\nexport KEY_Hangul_J_Tieut\n\nconst KEY_Hangul_J_Phieuf = detail.KEY_Hangul_J_Phieuf\nexport KEY_Hangul_J_Phieuf\n\nconst KEY_Hangul_J_Hieuh = detail.KEY_Hangul_J_Hieuh\nexport KEY_Hangul_J_Hieuh\n\nconst KEY_Hangul_RieulYeorinHieuh = detail.KEY_Hangul_RieulYeorinHieuh\nexport KEY_Hangul_RieulYeorinHieuh\n\nconst KEY_Hangul_SunkyeongeumMieum = detail.KEY_Hangul_SunkyeongeumMieum\nexport KEY_Hangul_SunkyeongeumMieum\n\nconst KEY_Hangul_SunkyeongeumPieub = detail.KEY_Hangul_SunkyeongeumPieub\nexport KEY_Hangul_SunkyeongeumPieub\n\nconst KEY_Hangul_PanSios = detail.KEY_Hangul_PanSios\nexport KEY_Hangul_PanSios\n\nconst KEY_Hangul_KkogjiDalrinIeung = detail.KEY_Hangul_KkogjiDalrinIeung\nexport KEY_Hangul_KkogjiDalrinIeung\n\nconst KEY_Hangul_SunkyeongeumPhieuf = detail.KEY_Hangul_SunkyeongeumPhieuf\nexport KEY_Hangul_SunkyeongeumPhieuf\n\nconst KEY_Hangul_YeorinHieuh = detail.KEY_Hangul_YeorinHieuh\nexport KEY_Hangul_YeorinHieuh\n\nconst KEY_Hangul_AraeA = detail.KEY_Hangul_AraeA\nexport KEY_Hangul_AraeA\n\nconst KEY_Hangul_AraeAE = detail.KEY_Hangul_AraeAE\nexport KEY_Hangul_AraeAE\n\nconst KEY_Hangul_J_PanSios = detail.KEY_Hangul_J_PanSios\nexport KEY_Hangul_J_PanSios\n\nconst KEY_Hangul_J_KkogjiDalrinIeung = detail.KEY_Hangul_J_KkogjiDalrinIeung\nexport KEY_Hangul_J_KkogjiDalrinIeung\n\nconst KEY_Hangul_J_YeorinHieuh = detail.KEY_Hangul_J_YeorinHieuh\nexport KEY_Hangul_J_YeorinHieuh\n\nconst KEY_Korean_Won = detail.KEY_Korean_Won\nexport KEY_Korean_Won\n\nconst KEY_Armenian_ligature_ew = detail.KEY_Armenian_ligature_ew\nexport KEY_Armenian_ligature_ew\n\nconst KEY_Armenian_full_stop = detail.KEY_Armenian_full_stop\nexport KEY_Armenian_full_stop\n\nconst KEY_Armenian_verjaket = detail.KEY_Armenian_verjaket\nexport KEY_Armenian_verjaket\n\nconst KEY_Armenian_separation_mark = detail.KEY_Armenian_separation_mark\nexport KEY_Armenian_separation_mark\n\nconst KEY_Armenian_but = detail.KEY_Armenian_but\nexport KEY_Armenian_but\n\nconst KEY_Armenian_hyphen = detail.KEY_Armenian_hyphen\nexport KEY_Armenian_hyphen\n\nconst KEY_Armenian_yentamna = detail.KEY_Armenian_yentamna\nexport KEY_Armenian_yentamna\n\nconst KEY_Armenian_exclam = detail.KEY_Armenian_exclam\nexport KEY_Armenian_exclam\n\nconst KEY_Armenian_amanak = detail.KEY_Armenian_amanak\nexport KEY_Armenian_amanak\n\nconst KEY_Armenian_accent = detail.KEY_Armenian_accent\nexport KEY_Armenian_accent\n\nconst KEY_Armenian_shesht = detail.KEY_Armenian_shesht\nexport KEY_Armenian_shesht\n\nconst KEY_Armenian_question = detail.KEY_Armenian_question\nexport KEY_Armenian_question\n\nconst KEY_Armenian_paruyk = detail.KEY_Armenian_paruyk\nexport KEY_Armenian_paruyk\n\nconst KEY_Armenian_AYB = detail.KEY_Armenian_AYB\nexport KEY_Armenian_AYB\n\nconst KEY_Armenian_ayb = detail.KEY_Armenian_ayb\nexport KEY_Armenian_ayb\n\nconst KEY_Armenian_BEN = detail.KEY_Armenian_BEN\nexport KEY_Armenian_BEN\n\nconst KEY_Armenian_ben = detail.KEY_Armenian_ben\nexport KEY_Armenian_ben\n\nconst KEY_Armenian_GIM = detail.KEY_Armenian_GIM\nexport KEY_Armenian_GIM\n\nconst KEY_Armenian_gim = detail.KEY_Armenian_gim\nexport KEY_Armenian_gim\n\nconst KEY_Armenian_DA = detail.KEY_Armenian_DA\nexport KEY_Armenian_DA\n\nconst KEY_Armenian_da = detail.KEY_Armenian_da\nexport KEY_Armenian_da\n\nconst KEY_Armenian_YECH = detail.KEY_Armenian_YECH\nexport KEY_Armenian_YECH\n\nconst KEY_Armenian_yech = detail.KEY_Armenian_yech\nexport KEY_Armenian_yech\n\nconst KEY_Armenian_ZA = detail.KEY_Armenian_ZA\nexport KEY_Armenian_ZA\n\nconst KEY_Armenian_za = detail.KEY_Armenian_za\nexport KEY_Armenian_za\n\nconst KEY_Armenian_E = detail.KEY_Armenian_E\nexport KEY_Armenian_E\n\nconst KEY_Armenian_e = detail.KEY_Armenian_e\nexport KEY_Armenian_e\n\nconst KEY_Armenian_AT = detail.KEY_Armenian_AT\nexport KEY_Armenian_AT\n\nconst KEY_Armenian_at = detail.KEY_Armenian_at\nexport KEY_Armenian_at\n\nconst KEY_Armenian_TO = detail.KEY_Armenian_TO\nexport KEY_Armenian_TO\n\nconst KEY_Armenian_to = detail.KEY_Armenian_to\nexport KEY_Armenian_to\n\nconst KEY_Armenian_ZHE = detail.KEY_Armenian_ZHE\nexport KEY_Armenian_ZHE\n\nconst KEY_Armenian_zhe = detail.KEY_Armenian_zhe\nexport KEY_Armenian_zhe\n\nconst KEY_Armenian_INI = detail.KEY_Armenian_INI\nexport KEY_Armenian_INI\n\nconst KEY_Armenian_ini = detail.KEY_Armenian_ini\nexport KEY_Armenian_ini\n\nconst KEY_Armenian_LYUN = detail.KEY_Armenian_LYUN\nexport KEY_Armenian_LYUN\n\nconst KEY_Armenian_lyun = detail.KEY_Armenian_lyun\nexport KEY_Armenian_lyun\n\nconst KEY_Armenian_KHE = detail.KEY_Armenian_KHE\nexport KEY_Armenian_KHE\n\nconst KEY_Armenian_khe = detail.KEY_Armenian_khe\nexport KEY_Armenian_khe\n\nconst KEY_Armenian_TSA = detail.KEY_Armenian_TSA\nexport KEY_Armenian_TSA\n\nconst KEY_Armenian_tsa = detail.KEY_Armenian_tsa\nexport KEY_Armenian_tsa\n\nconst KEY_Armenian_KEN = detail.KEY_Armenian_KEN\nexport KEY_Armenian_KEN\n\nconst KEY_Armenian_ken = detail.KEY_Armenian_ken\nexport KEY_Armenian_ken\n\nconst KEY_Armenian_HO = detail.KEY_Armenian_HO\nexport KEY_Armenian_HO\n\nconst KEY_Armenian_ho = detail.KEY_Armenian_ho\nexport KEY_Armenian_ho\n\nconst KEY_Armenian_DZA = detail.KEY_Armenian_DZA\nexport KEY_Armenian_DZA\n\nconst KEY_Armenian_dza = detail.KEY_Armenian_dza\nexport KEY_Armenian_dza\n\nconst KEY_Armenian_GHAT = detail.KEY_Armenian_GHAT\nexport KEY_Armenian_GHAT\n\nconst KEY_Armenian_ghat = detail.KEY_Armenian_ghat\nexport KEY_Armenian_ghat\n\nconst KEY_Armenian_TCHE = detail.KEY_Armenian_TCHE\nexport KEY_Armenian_TCHE\n\nconst KEY_Armenian_tche = detail.KEY_Armenian_tche\nexport KEY_Armenian_tche\n\nconst KEY_Armenian_MEN = detail.KEY_Armenian_MEN\nexport KEY_Armenian_MEN\n\nconst KEY_Armenian_men = detail.KEY_Armenian_men\nexport KEY_Armenian_men\n\nconst KEY_Armenian_HI = detail.KEY_Armenian_HI\nexport KEY_Armenian_HI\n\nconst KEY_Armenian_hi = detail.KEY_Armenian_hi\nexport KEY_Armenian_hi\n\nconst KEY_Armenian_NU = detail.KEY_Armenian_NU\nexport KEY_Armenian_NU\n\nconst KEY_Armenian_nu = detail.KEY_Armenian_nu\nexport KEY_Armenian_nu\n\nconst KEY_Armenian_SHA = detail.KEY_Armenian_SHA\nexport KEY_Armenian_SHA\n\nconst KEY_Armenian_sha = detail.KEY_Armenian_sha\nexport KEY_Armenian_sha\n\nconst KEY_Armenian_VO = detail.KEY_Armenian_VO\nexport KEY_Armenian_VO\n\nconst KEY_Armenian_vo = detail.KEY_Armenian_vo\nexport KEY_Armenian_vo\n\nconst KEY_Armenian_CHA = detail.KEY_Armenian_CHA\nexport KEY_Armenian_CHA\n\nconst KEY_Armenian_cha = detail.KEY_Armenian_cha\nexport KEY_Armenian_cha\n\nconst KEY_Armenian_PE = detail.KEY_Armenian_PE\nexport KEY_Armenian_PE\n\nconst KEY_Armenian_pe = detail.KEY_Armenian_pe\nexport KEY_Armenian_pe\n\nconst KEY_Armenian_JE = detail.KEY_Armenian_JE\nexport KEY_Armenian_JE\n\nconst KEY_Armenian_je = detail.KEY_Armenian_je\nexport KEY_Armenian_je\n\nconst KEY_Armenian_RA = detail.KEY_Armenian_RA\nexport KEY_Armenian_RA\n\nconst KEY_Armenian_ra = detail.KEY_Armenian_ra\nexport KEY_Armenian_ra\n\nconst KEY_Armenian_SE = detail.KEY_Armenian_SE\nexport KEY_Armenian_SE\n\nconst KEY_Armenian_se = detail.KEY_Armenian_se\nexport KEY_Armenian_se\n\nconst KEY_Armenian_VEV = detail.KEY_Armenian_VEV\nexport KEY_Armenian_VEV\n\nconst KEY_Armenian_vev = detail.KEY_Armenian_vev\nexport KEY_Armenian_vev\n\nconst KEY_Armenian_TYUN = detail.KEY_Armenian_TYUN\nexport KEY_Armenian_TYUN\n\nconst KEY_Armenian_tyun = detail.KEY_Armenian_tyun\nexport KEY_Armenian_tyun\n\nconst KEY_Armenian_RE = detail.KEY_Armenian_RE\nexport KEY_Armenian_RE\n\nconst KEY_Armenian_re = detail.KEY_Armenian_re\nexport KEY_Armenian_re\n\nconst KEY_Armenian_TSO = detail.KEY_Armenian_TSO\nexport KEY_Armenian_TSO\n\nconst KEY_Armenian_tso = detail.KEY_Armenian_tso\nexport KEY_Armenian_tso\n\nconst KEY_Armenian_VYUN = detail.KEY_Armenian_VYUN\nexport KEY_Armenian_VYUN\n\nconst KEY_Armenian_vyun = detail.KEY_Armenian_vyun\nexport KEY_Armenian_vyun\n\nconst KEY_Armenian_PYUR = detail.KEY_Armenian_PYUR\nexport KEY_Armenian_PYUR\n\nconst KEY_Armenian_pyur = detail.KEY_Armenian_pyur\nexport KEY_Armenian_pyur\n\nconst KEY_Armenian_KE = detail.KEY_Armenian_KE\nexport KEY_Armenian_KE\n\nconst KEY_Armenian_ke = detail.KEY_Armenian_ke\nexport KEY_Armenian_ke\n\nconst KEY_Armenian_O = detail.KEY_Armenian_O\nexport KEY_Armenian_O\n\nconst KEY_Armenian_o = detail.KEY_Armenian_o\nexport KEY_Armenian_o\n\nconst KEY_Armenian_FE = detail.KEY_Armenian_FE\nexport KEY_Armenian_FE\n\nconst KEY_Armenian_fe = detail.KEY_Armenian_fe\nexport KEY_Armenian_fe\n\nconst KEY_Armenian_apostrophe = detail.KEY_Armenian_apostrophe\nexport KEY_Armenian_apostrophe\n\nconst KEY_Georgian_an = detail.KEY_Georgian_an\nexport KEY_Georgian_an\n\nconst KEY_Georgian_ban = detail.KEY_Georgian_ban\nexport KEY_Georgian_ban\n\nconst KEY_Georgian_gan = detail.KEY_Georgian_gan\nexport KEY_Georgian_gan\n\nconst KEY_Georgian_don = detail.KEY_Georgian_don\nexport KEY_Georgian_don\n\nconst KEY_Georgian_en = detail.KEY_Georgian_en\nexport KEY_Georgian_en\n\nconst KEY_Georgian_vin = detail.KEY_Georgian_vin\nexport KEY_Georgian_vin\n\nconst KEY_Georgian_zen = detail.KEY_Georgian_zen\nexport KEY_Georgian_zen\n\nconst KEY_Georgian_tan = detail.KEY_Georgian_tan\nexport KEY_Georgian_tan\n\nconst KEY_Georgian_in = detail.KEY_Georgian_in\nexport KEY_Georgian_in\n\nconst KEY_Georgian_kan = detail.KEY_Georgian_kan\nexport KEY_Georgian_kan\n\nconst KEY_Georgian_las = detail.KEY_Georgian_las\nexport KEY_Georgian_las\n\nconst KEY_Georgian_man = detail.KEY_Georgian_man\nexport KEY_Georgian_man\n\nconst KEY_Georgian_nar = detail.KEY_Georgian_nar\nexport KEY_Georgian_nar\n\nconst KEY_Georgian_on = detail.KEY_Georgian_on\nexport KEY_Georgian_on\n\nconst KEY_Georgian_par = detail.KEY_Georgian_par\nexport KEY_Georgian_par\n\nconst KEY_Georgian_zhar = detail.KEY_Georgian_zhar\nexport KEY_Georgian_zhar\n\nconst KEY_Georgian_rae = detail.KEY_Georgian_rae\nexport KEY_Georgian_rae\n\nconst KEY_Georgian_san = detail.KEY_Georgian_san\nexport KEY_Georgian_san\n\nconst KEY_Georgian_tar = detail.KEY_Georgian_tar\nexport KEY_Georgian_tar\n\nconst KEY_Georgian_un = detail.KEY_Georgian_un\nexport KEY_Georgian_un\n\nconst KEY_Georgian_phar = detail.KEY_Georgian_phar\nexport KEY_Georgian_phar\n\nconst KEY_Georgian_khar = detail.KEY_Georgian_khar\nexport KEY_Georgian_khar\n\nconst KEY_Georgian_ghan = detail.KEY_Georgian_ghan\nexport KEY_Georgian_ghan\n\nconst KEY_Georgian_qar = detail.KEY_Georgian_qar\nexport KEY_Georgian_qar\n\nconst KEY_Georgian_shin = detail.KEY_Georgian_shin\nexport KEY_Georgian_shin\n\nconst KEY_Georgian_chin = detail.KEY_Georgian_chin\nexport KEY_Georgian_chin\n\nconst KEY_Georgian_can = detail.KEY_Georgian_can\nexport KEY_Georgian_can\n\nconst KEY_Georgian_jil = detail.KEY_Georgian_jil\nexport KEY_Georgian_jil\n\nconst KEY_Georgian_cil = detail.KEY_Georgian_cil\nexport KEY_Georgian_cil\n\nconst KEY_Georgian_char = detail.KEY_Georgian_char\nexport KEY_Georgian_char\n\nconst KEY_Georgian_xan = detail.KEY_Georgian_xan\nexport KEY_Georgian_xan\n\nconst KEY_Georgian_jhan = detail.KEY_Georgian_jhan\nexport KEY_Georgian_jhan\n\nconst KEY_Georgian_hae = detail.KEY_Georgian_hae\nexport KEY_Georgian_hae\n\nconst KEY_Georgian_he = detail.KEY_Georgian_he\nexport KEY_Georgian_he\n\nconst KEY_Georgian_hie = detail.KEY_Georgian_hie\nexport KEY_Georgian_hie\n\nconst KEY_Georgian_we = detail.KEY_Georgian_we\nexport KEY_Georgian_we\n\nconst KEY_Georgian_har = detail.KEY_Georgian_har\nexport KEY_Georgian_har\n\nconst KEY_Georgian_hoe = detail.KEY_Georgian_hoe\nexport KEY_Georgian_hoe\n\nconst KEY_Georgian_fi = detail.KEY_Georgian_fi\nexport KEY_Georgian_fi\n\nconst KEY_Xabovedot = detail.KEY_Xabovedot\nexport KEY_Xabovedot\n\nconst KEY_Ibreve = detail.KEY_Ibreve\nexport KEY_Ibreve\n\nconst KEY_Zstroke = detail.KEY_Zstroke\nexport KEY_Zstroke\n\nconst KEY_Gcaron = detail.KEY_Gcaron\nexport KEY_Gcaron\n\nconst KEY_Ocaron = detail.KEY_Ocaron\nexport KEY_Ocaron\n\nconst KEY_Obarred = detail.KEY_Obarred\nexport KEY_Obarred\n\nconst KEY_xabovedot = detail.KEY_xabovedot\nexport KEY_xabovedot\n\nconst KEY_ibreve = detail.KEY_ibreve\nexport KEY_ibreve\n\nconst KEY_zstroke = detail.KEY_zstroke\nexport KEY_zstroke\n\nconst KEY_gcaron = detail.KEY_gcaron\nexport KEY_gcaron\n\nconst KEY_ocaron = detail.KEY_ocaron\nexport KEY_ocaron\n\nconst KEY_obarred = detail.KEY_obarred\nexport KEY_obarred\n\nconst KEY_SCHWA = detail.KEY_SCHWA\nexport KEY_SCHWA\n\nconst KEY_schwa = detail.KEY_schwa\nexport KEY_schwa\n\nconst KEY_EZH = detail.KEY_EZH\nexport KEY_EZH\n\nconst KEY_ezh = detail.KEY_ezh\nexport KEY_ezh\n\nconst KEY_Lbelowdot = detail.KEY_Lbelowdot\nexport KEY_Lbelowdot\n\nconst KEY_lbelowdot = detail.KEY_lbelowdot\nexport KEY_lbelowdot\n\nconst KEY_Abelowdot = detail.KEY_Abelowdot\nexport KEY_Abelowdot\n\nconst KEY_abelowdot = detail.KEY_abelowdot\nexport KEY_abelowdot\n\nconst KEY_Ahook = detail.KEY_Ahook\nexport KEY_Ahook\n\nconst KEY_ahook = detail.KEY_ahook\nexport KEY_ahook\n\nconst KEY_Acircumflexacute = detail.KEY_Acircumflexacute\nexport KEY_Acircumflexacute\n\nconst KEY_acircumflexacute = detail.KEY_acircumflexacute\nexport KEY_acircumflexacute\n\nconst KEY_Acircumflexgrave = detail.KEY_Acircumflexgrave\nexport KEY_Acircumflexgrave\n\nconst KEY_acircumflexgrave = detail.KEY_acircumflexgrave\nexport KEY_acircumflexgrave\n\nconst KEY_Acircumflexhook = detail.KEY_Acircumflexhook\nexport KEY_Acircumflexhook\n\nconst KEY_acircumflexhook = detail.KEY_acircumflexhook\nexport KEY_acircumflexhook\n\nconst KEY_Acircumflextilde = detail.KEY_Acircumflextilde\nexport KEY_Acircumflextilde\n\nconst KEY_acircumflextilde = detail.KEY_acircumflextilde\nexport KEY_acircumflextilde\n\nconst KEY_Acircumflexbelowdot = detail.KEY_Acircumflexbelowdot\nexport KEY_Acircumflexbelowdot\n\nconst KEY_acircumflexbelowdot = detail.KEY_acircumflexbelowdot\nexport KEY_acircumflexbelowdot\n\nconst KEY_Abreveacute = detail.KEY_Abreveacute\nexport KEY_Abreveacute\n\nconst KEY_abreveacute = detail.KEY_abreveacute\nexport KEY_abreveacute\n\nconst KEY_Abrevegrave = detail.KEY_Abrevegrave\nexport KEY_Abrevegrave\n\nconst KEY_abrevegrave = detail.KEY_abrevegrave\nexport KEY_abrevegrave\n\nconst KEY_Abrevehook = detail.KEY_Abrevehook\nexport KEY_Abrevehook\n\nconst KEY_abrevehook = detail.KEY_abrevehook\nexport KEY_abrevehook\n\nconst KEY_Abrevetilde = detail.KEY_Abrevetilde\nexport KEY_Abrevetilde\n\nconst KEY_abrevetilde = detail.KEY_abrevetilde\nexport KEY_abrevetilde\n\nconst KEY_Abrevebelowdot = detail.KEY_Abrevebelowdot\nexport KEY_Abrevebelowdot\n\nconst KEY_abrevebelowdot = detail.KEY_abrevebelowdot\nexport KEY_abrevebelowdot\n\nconst KEY_Ebelowdot = detail.KEY_Ebelowdot\nexport KEY_Ebelowdot\n\nconst KEY_ebelowdot = detail.KEY_ebelowdot\nexport KEY_ebelowdot\n\nconst KEY_Ehook = detail.KEY_Ehook\nexport KEY_Ehook\n\nconst KEY_ehook = detail.KEY_ehook\nexport KEY_ehook\n\nconst KEY_Etilde = detail.KEY_Etilde\nexport KEY_Etilde\n\nconst KEY_etilde = detail.KEY_etilde\nexport KEY_etilde\n\nconst KEY_Ecircumflexacute = detail.KEY_Ecircumflexacute\nexport KEY_Ecircumflexacute\n\nconst KEY_ecircumflexacute = detail.KEY_ecircumflexacute\nexport KEY_ecircumflexacute\n\nconst KEY_Ecircumflexgrave = detail.KEY_Ecircumflexgrave\nexport KEY_Ecircumflexgrave\n\nconst KEY_ecircumflexgrave = detail.KEY_ecircumflexgrave\nexport KEY_ecircumflexgrave\n\nconst KEY_Ecircumflexhook = detail.KEY_Ecircumflexhook\nexport KEY_Ecircumflexhook\n\nconst KEY_ecircumflexhook = detail.KEY_ecircumflexhook\nexport KEY_ecircumflexhook\n\nconst KEY_Ecircumflextilde = detail.KEY_Ecircumflextilde\nexport KEY_Ecircumflextilde\n\nconst KEY_ecircumflextilde = detail.KEY_ecircumflextilde\nexport KEY_ecircumflextilde\n\nconst KEY_Ecircumflexbelowdot = detail.KEY_Ecircumflexbelowdot\nexport KEY_Ecircumflexbelowdot\n\nconst KEY_ecircumflexbelowdot = detail.KEY_ecircumflexbelowdot\nexport KEY_ecircumflexbelowdot\n\nconst KEY_Ihook = detail.KEY_Ihook\nexport KEY_Ihook\n\nconst KEY_ihook = detail.KEY_ihook\nexport KEY_ihook\n\nconst KEY_Ibelowdot = detail.KEY_Ibelowdot\nexport KEY_Ibelowdot\n\nconst KEY_ibelowdot = detail.KEY_ibelowdot\nexport KEY_ibelowdot\n\nconst KEY_Obelowdot = detail.KEY_Obelowdot\nexport KEY_Obelowdot\n\nconst KEY_obelowdot = detail.KEY_obelowdot\nexport KEY_obelowdot\n\nconst KEY_Ohook = detail.KEY_Ohook\nexport KEY_Ohook\n\nconst KEY_ohook = detail.KEY_ohook\nexport KEY_ohook\n\nconst KEY_Ocircumflexacute = detail.KEY_Ocircumflexacute\nexport KEY_Ocircumflexacute\n\nconst KEY_ocircumflexacute = detail.KEY_ocircumflexacute\nexport KEY_ocircumflexacute\n\nconst KEY_Ocircumflexgrave = detail.KEY_Ocircumflexgrave\nexport KEY_Ocircumflexgrave\n\nconst KEY_ocircumflexgrave = detail.KEY_ocircumflexgrave\nexport KEY_ocircumflexgrave\n\nconst KEY_Ocircumflexhook = detail.KEY_Ocircumflexhook\nexport KEY_Ocircumflexhook\n\nconst KEY_ocircumflexhook = detail.KEY_ocircumflexhook\nexport KEY_ocircumflexhook\n\nconst KEY_Ocircumflextilde = detail.KEY_Ocircumflextilde\nexport KEY_Ocircumflextilde\n\nconst KEY_ocircumflextilde = detail.KEY_ocircumflextilde\nexport KEY_ocircumflextilde\n\nconst KEY_Ocircumflexbelowdot = detail.KEY_Ocircumflexbelowdot\nexport KEY_Ocircumflexbelowdot\n\nconst KEY_ocircumflexbelowdot = detail.KEY_ocircumflexbelowdot\nexport KEY_ocircumflexbelowdot\n\nconst KEY_Ohornacute = detail.KEY_Ohornacute\nexport KEY_Ohornacute\n\nconst KEY_ohornacute = detail.KEY_ohornacute\nexport KEY_ohornacute\n\nconst KEY_Ohorngrave = detail.KEY_Ohorngrave\nexport KEY_Ohorngrave\n\nconst KEY_ohorngrave = detail.KEY_ohorngrave\nexport KEY_ohorngrave\n\nconst KEY_Ohornhook = detail.KEY_Ohornhook\nexport KEY_Ohornhook\n\nconst KEY_ohornhook = detail.KEY_ohornhook\nexport KEY_ohornhook\n\nconst KEY_Ohorntilde = detail.KEY_Ohorntilde\nexport KEY_Ohorntilde\n\nconst KEY_ohorntilde = detail.KEY_ohorntilde\nexport KEY_ohorntilde\n\nconst KEY_Ohornbelowdot = detail.KEY_Ohornbelowdot\nexport KEY_Ohornbelowdot\n\nconst KEY_ohornbelowdot = detail.KEY_ohornbelowdot\nexport KEY_ohornbelowdot\n\nconst KEY_Ubelowdot = detail.KEY_Ubelowdot\nexport KEY_Ubelowdot\n\nconst KEY_ubelowdot = detail.KEY_ubelowdot\nexport KEY_ubelowdot\n\nconst KEY_Uhook = detail.KEY_Uhook\nexport KEY_Uhook\n\nconst KEY_uhook = detail.KEY_uhook\nexport KEY_uhook\n\nconst KEY_Uhornacute = detail.KEY_Uhornacute\nexport KEY_Uhornacute\n\nconst KEY_uhornacute = detail.KEY_uhornacute\nexport KEY_uhornacute\n\nconst KEY_Uhorngrave = detail.KEY_Uhorngrave\nexport KEY_Uhorngrave\n\nconst KEY_uhorngrave = detail.KEY_uhorngrave\nexport KEY_uhorngrave\n\nconst KEY_Uhornhook = detail.KEY_Uhornhook\nexport KEY_Uhornhook\n\nconst KEY_uhornhook = detail.KEY_uhornhook\nexport KEY_uhornhook\n\nconst KEY_Uhorntilde = detail.KEY_Uhorntilde\nexport KEY_Uhorntilde\n\nconst KEY_uhorntilde = detail.KEY_uhorntilde\nexport KEY_uhorntilde\n\nconst KEY_Uhornbelowdot = detail.KEY_Uhornbelowdot\nexport KEY_Uhornbelowdot\n\nconst KEY_uhornbelowdot = detail.KEY_uhornbelowdot\nexport KEY_uhornbelowdot\n\nconst KEY_Ybelowdot = detail.KEY_Ybelowdot\nexport KEY_Ybelowdot\n\nconst KEY_ybelowdot = detail.KEY_ybelowdot\nexport KEY_ybelowdot\n\nconst KEY_Yhook = detail.KEY_Yhook\nexport KEY_Yhook\n\nconst KEY_yhook = detail.KEY_yhook\nexport KEY_yhook\n\nconst KEY_Ytilde = detail.KEY_Ytilde\nexport KEY_Ytilde\n\nconst KEY_ytilde = detail.KEY_ytilde\nexport KEY_ytilde\n\nconst KEY_Ohorn = detail.KEY_Ohorn\nexport KEY_Ohorn\n\nconst KEY_ohorn = detail.KEY_ohorn\nexport KEY_ohorn\n\nconst KEY_Uhorn = detail.KEY_Uhorn\nexport KEY_Uhorn\n\nconst KEY_uhorn = detail.KEY_uhorn\nexport KEY_uhorn\n\nconst KEY_EcuSign = detail.KEY_EcuSign\nexport KEY_EcuSign\n\nconst KEY_ColonSign = detail.KEY_ColonSign\nexport KEY_ColonSign\n\nconst KEY_CruzeiroSign = detail.KEY_CruzeiroSign\nexport KEY_CruzeiroSign\n\nconst KEY_FFrancSign = detail.KEY_FFrancSign\nexport KEY_FFrancSign\n\nconst KEY_LiraSign = detail.KEY_LiraSign\nexport KEY_LiraSign\n\nconst KEY_MillSign = detail.KEY_MillSign\nexport KEY_MillSign\n\nconst KEY_NairaSign = detail.KEY_NairaSign\nexport KEY_NairaSign\n\nconst KEY_PesetaSign = detail.KEY_PesetaSign\nexport KEY_PesetaSign\n\nconst KEY_RupeeSign = detail.KEY_RupeeSign\nexport KEY_RupeeSign\n\nconst KEY_WonSign = detail.KEY_WonSign\nexport KEY_WonSign\n\nconst KEY_NewSheqelSign = detail.KEY_NewSheqelSign\nexport KEY_NewSheqelSign\n\nconst KEY_DongSign = detail.KEY_DongSign\nexport KEY_DongSign\n\nconst KEY_EuroSign = detail.KEY_EuroSign\nexport KEY_EuroSign\n\nconst KEY_zerosuperior = detail.KEY_zerosuperior\nexport KEY_zerosuperior\n\nconst KEY_foursuperior = detail.KEY_foursuperior\nexport KEY_foursuperior\n\nconst KEY_fivesuperior = detail.KEY_fivesuperior\nexport KEY_fivesuperior\n\nconst KEY_sixsuperior = detail.KEY_sixsuperior\nexport KEY_sixsuperior\n\nconst KEY_sevensuperior = detail.KEY_sevensuperior\nexport KEY_sevensuperior\n\nconst KEY_eightsuperior = detail.KEY_eightsuperior\nexport KEY_eightsuperior\n\nconst KEY_ninesuperior = detail.KEY_ninesuperior\nexport KEY_ninesuperior\n\nconst KEY_zerosubscript = detail.KEY_zerosubscript\nexport KEY_zerosubscript\n\nconst KEY_onesubscript = detail.KEY_onesubscript\nexport KEY_onesubscript\n\nconst KEY_twosubscript = detail.KEY_twosubscript\nexport KEY_twosubscript\n\nconst KEY_threesubscript = detail.KEY_threesubscript\nexport KEY_threesubscript\n\nconst KEY_foursubscript = detail.KEY_foursubscript\nexport KEY_foursubscript\n\nconst KEY_fivesubscript = detail.KEY_fivesubscript\nexport KEY_fivesubscript\n\nconst KEY_sixsubscript = detail.KEY_sixsubscript\nexport KEY_sixsubscript\n\nconst KEY_sevensubscript = detail.KEY_sevensubscript\nexport KEY_sevensubscript\n\nconst KEY_eightsubscript = detail.KEY_eightsubscript\nexport KEY_eightsubscript\n\nconst KEY_ninesubscript = detail.KEY_ninesubscript\nexport KEY_ninesubscript\n\nconst KEY_partdifferential = detail.KEY_partdifferential\nexport KEY_partdifferential\n\nconst KEY_emptyset = detail.KEY_emptyset\nexport KEY_emptyset\n\nconst KEY_elementof = detail.KEY_elementof\nexport KEY_elementof\n\nconst KEY_notelementof = detail.KEY_notelementof\nexport KEY_notelementof\n\nconst KEY_containsas = detail.KEY_containsas\nexport KEY_containsas\n\nconst KEY_squareroot = detail.KEY_squareroot\nexport KEY_squareroot\n\nconst KEY_cuberoot = detail.KEY_cuberoot\nexport KEY_cuberoot\n\nconst KEY_fourthroot = detail.KEY_fourthroot\nexport KEY_fourthroot\n\nconst KEY_dintegral = detail.KEY_dintegral\nexport KEY_dintegral\n\nconst KEY_tintegral = detail.KEY_tintegral\nexport KEY_tintegral\n\nconst KEY_because = detail.KEY_because\nexport KEY_because\n\nconst KEY_approxeq = detail.KEY_approxeq\nexport KEY_approxeq\n\nconst KEY_notapproxeq = detail.KEY_notapproxeq\nexport KEY_notapproxeq\n\nconst KEY_notidentical = detail.KEY_notidentical\nexport KEY_notidentical\n\nconst KEY_stricteq = detail.KEY_stricteq\nexport KEY_stricteq\n\nconst KEY_braille_dot_1 = detail.KEY_braille_dot_1\nexport KEY_braille_dot_1\n\nconst KEY_braille_dot_2 = detail.KEY_braille_dot_2\nexport KEY_braille_dot_2\n\nconst KEY_braille_dot_3 = detail.KEY_braille_dot_3\nexport KEY_braille_dot_3\n\nconst KEY_braille_dot_4 = detail.KEY_braille_dot_4\nexport KEY_braille_dot_4\n\nconst KEY_braille_dot_5 = detail.KEY_braille_dot_5\nexport KEY_braille_dot_5\n\nconst KEY_braille_dot_6 = detail.KEY_braille_dot_6\nexport KEY_braille_dot_6\n\nconst KEY_braille_dot_7 = detail.KEY_braille_dot_7\nexport KEY_braille_dot_7\n\nconst KEY_braille_dot_8 = detail.KEY_braille_dot_8\nexport KEY_braille_dot_8\n\nconst KEY_braille_dot_9 = detail.KEY_braille_dot_9\nexport KEY_braille_dot_9\n\nconst KEY_braille_dot_10 = detail.KEY_braille_dot_10\nexport KEY_braille_dot_10\n\nconst KEY_braille_blank = detail.KEY_braille_blank\nexport KEY_braille_blank\n\nconst KEY_braille_dots_1 = detail.KEY_braille_dots_1\nexport KEY_braille_dots_1\n\nconst KEY_braille_dots_2 = detail.KEY_braille_dots_2\nexport KEY_braille_dots_2\n\nconst KEY_braille_dots_12 = detail.KEY_braille_dots_12\nexport KEY_braille_dots_12\n\nconst KEY_braille_dots_3 = detail.KEY_braille_dots_3\nexport KEY_braille_dots_3\n\nconst KEY_braille_dots_13 = detail.KEY_braille_dots_13\nexport KEY_braille_dots_13\n\nconst KEY_braille_dots_23 = detail.KEY_braille_dots_23\nexport KEY_braille_dots_23\n\nconst KEY_braille_dots_123 = detail.KEY_braille_dots_123\nexport KEY_braille_dots_123\n\nconst KEY_braille_dots_4 = detail.KEY_braille_dots_4\nexport KEY_braille_dots_4\n\nconst KEY_braille_dots_14 = detail.KEY_braille_dots_14\nexport KEY_braille_dots_14\n\nconst KEY_braille_dots_24 = detail.KEY_braille_dots_24\nexport KEY_braille_dots_24\n\nconst KEY_braille_dots_124 = detail.KEY_braille_dots_124\nexport KEY_braille_dots_124\n\nconst KEY_braille_dots_34 = detail.KEY_braille_dots_34\nexport KEY_braille_dots_34\n\nconst KEY_braille_dots_134 = detail.KEY_braille_dots_134\nexport KEY_braille_dots_134\n\nconst KEY_braille_dots_234 = detail.KEY_braille_dots_234\nexport KEY_braille_dots_234\n\nconst KEY_braille_dots_1234 = detail.KEY_braille_dots_1234\nexport KEY_braille_dots_1234\n\nconst KEY_braille_dots_5 = detail.KEY_braille_dots_5\nexport KEY_braille_dots_5\n\nconst KEY_braille_dots_15 = detail.KEY_braille_dots_15\nexport KEY_braille_dots_15\n\nconst KEY_braille_dots_25 = detail.KEY_braille_dots_25\nexport KEY_braille_dots_25\n\nconst KEY_braille_dots_125 = detail.KEY_braille_dots_125\nexport KEY_braille_dots_125\n\nconst KEY_braille_dots_35 = detail.KEY_braille_dots_35\nexport KEY_braille_dots_35\n\nconst KEY_braille_dots_135 = detail.KEY_braille_dots_135\nexport KEY_braille_dots_135\n\nconst KEY_braille_dots_235 = detail.KEY_braille_dots_235\nexport KEY_braille_dots_235\n\nconst KEY_braille_dots_1235 = detail.KEY_braille_dots_1235\nexport KEY_braille_dots_1235\n\nconst KEY_braille_dots_45 = detail.KEY_braille_dots_45\nexport KEY_braille_dots_45\n\nconst KEY_braille_dots_145 = detail.KEY_braille_dots_145\nexport KEY_braille_dots_145\n\nconst KEY_braille_dots_245 = detail.KEY_braille_dots_245\nexport KEY_braille_dots_245\n\nconst KEY_braille_dots_1245 = detail.KEY_braille_dots_1245\nexport KEY_braille_dots_1245\n\nconst KEY_braille_dots_345 = detail.KEY_braille_dots_345\nexport KEY_braille_dots_345\n\nconst KEY_braille_dots_1345 = detail.KEY_braille_dots_1345\nexport KEY_braille_dots_1345\n\nconst KEY_braille_dots_2345 = detail.KEY_braille_dots_2345\nexport KEY_braille_dots_2345\n\nconst KEY_braille_dots_12345 = detail.KEY_braille_dots_12345\nexport KEY_braille_dots_12345\n\nconst KEY_braille_dots_6 = detail.KEY_braille_dots_6\nexport KEY_braille_dots_6\n\nconst KEY_braille_dots_16 = detail.KEY_braille_dots_16\nexport KEY_braille_dots_16\n\nconst KEY_braille_dots_26 = detail.KEY_braille_dots_26\nexport KEY_braille_dots_26\n\nconst KEY_braille_dots_126 = detail.KEY_braille_dots_126\nexport KEY_braille_dots_126\n\nconst KEY_braille_dots_36 = detail.KEY_braille_dots_36\nexport KEY_braille_dots_36\n\nconst KEY_braille_dots_136 = detail.KEY_braille_dots_136\nexport KEY_braille_dots_136\n\nconst KEY_braille_dots_236 = detail.KEY_braille_dots_236\nexport KEY_braille_dots_236\n\nconst KEY_braille_dots_1236 = detail.KEY_braille_dots_1236\nexport KEY_braille_dots_1236\n\nconst KEY_braille_dots_46 = detail.KEY_braille_dots_46\nexport KEY_braille_dots_46\n\nconst KEY_braille_dots_146 = detail.KEY_braille_dots_146\nexport KEY_braille_dots_146\n\nconst KEY_braille_dots_246 = detail.KEY_braille_dots_246\nexport KEY_braille_dots_246\n\nconst KEY_braille_dots_1246 = detail.KEY_braille_dots_1246\nexport KEY_braille_dots_1246\n\nconst KEY_braille_dots_346 = detail.KEY_braille_dots_346\nexport KEY_braille_dots_346\n\nconst KEY_braille_dots_1346 = detail.KEY_braille_dots_1346\nexport KEY_braille_dots_1346\n\nconst KEY_braille_dots_2346 = detail.KEY_braille_dots_2346\nexport KEY_braille_dots_2346\n\nconst KEY_braille_dots_12346 = detail.KEY_braille_dots_12346\nexport KEY_braille_dots_12346\n\nconst KEY_braille_dots_56 = detail.KEY_braille_dots_56\nexport KEY_braille_dots_56\n\nconst KEY_braille_dots_156 = detail.KEY_braille_dots_156\nexport KEY_braille_dots_156\n\nconst KEY_braille_dots_256 = detail.KEY_braille_dots_256\nexport KEY_braille_dots_256\n\nconst KEY_braille_dots_1256 = detail.KEY_braille_dots_1256\nexport KEY_braille_dots_1256\n\nconst KEY_braille_dots_356 = detail.KEY_braille_dots_356\nexport KEY_braille_dots_356\n\nconst KEY_braille_dots_1356 = detail.KEY_braille_dots_1356\nexport KEY_braille_dots_1356\n\nconst KEY_braille_dots_2356 = detail.KEY_braille_dots_2356\nexport KEY_braille_dots_2356\n\nconst KEY_braille_dots_12356 = detail.KEY_braille_dots_12356\nexport KEY_braille_dots_12356\n\nconst KEY_braille_dots_456 = detail.KEY_braille_dots_456\nexport KEY_braille_dots_456\n\nconst KEY_braille_dots_1456 = detail.KEY_braille_dots_1456\nexport KEY_braille_dots_1456\n\nconst KEY_braille_dots_2456 = detail.KEY_braille_dots_2456\nexport KEY_braille_dots_2456\n\nconst KEY_braille_dots_12456 = detail.KEY_braille_dots_12456\nexport KEY_braille_dots_12456\n\nconst KEY_braille_dots_3456 = detail.KEY_braille_dots_3456\nexport KEY_braille_dots_3456\n\nconst KEY_braille_dots_13456 = detail.KEY_braille_dots_13456\nexport KEY_braille_dots_13456\n\nconst KEY_braille_dots_23456 = detail.KEY_braille_dots_23456\nexport KEY_braille_dots_23456\n\nconst KEY_braille_dots_123456 = detail.KEY_braille_dots_123456\nexport KEY_braille_dots_123456\n\nconst KEY_braille_dots_7 = detail.KEY_braille_dots_7\nexport KEY_braille_dots_7\n\nconst KEY_braille_dots_17 = detail.KEY_braille_dots_17\nexport KEY_braille_dots_17\n\nconst KEY_braille_dots_27 = detail.KEY_braille_dots_27\nexport KEY_braille_dots_27\n\nconst KEY_braille_dots_127 = detail.KEY_braille_dots_127\nexport KEY_braille_dots_127\n\nconst KEY_braille_dots_37 = detail.KEY_braille_dots_37\nexport KEY_braille_dots_37\n\nconst KEY_braille_dots_137 = detail.KEY_braille_dots_137\nexport KEY_braille_dots_137\n\nconst KEY_braille_dots_237 = detail.KEY_braille_dots_237\nexport KEY_braille_dots_237\n\nconst KEY_braille_dots_1237 = detail.KEY_braille_dots_1237\nexport KEY_braille_dots_1237\n\nconst KEY_braille_dots_47 = detail.KEY_braille_dots_47\nexport KEY_braille_dots_47\n\nconst KEY_braille_dots_147 = detail.KEY_braille_dots_147\nexport KEY_braille_dots_147\n\nconst KEY_braille_dots_247 = detail.KEY_braille_dots_247\nexport KEY_braille_dots_247\n\nconst KEY_braille_dots_1247 = detail.KEY_braille_dots_1247\nexport KEY_braille_dots_1247\n\nconst KEY_braille_dots_347 = detail.KEY_braille_dots_347\nexport KEY_braille_dots_347\n\nconst KEY_braille_dots_1347 = detail.KEY_braille_dots_1347\nexport KEY_braille_dots_1347\n\nconst KEY_braille_dots_2347 = detail.KEY_braille_dots_2347\nexport KEY_braille_dots_2347\n\nconst KEY_braille_dots_12347 = detail.KEY_braille_dots_12347\nexport KEY_braille_dots_12347\n\nconst KEY_braille_dots_57 = detail.KEY_braille_dots_57\nexport KEY_braille_dots_57\n\nconst KEY_braille_dots_157 = detail.KEY_braille_dots_157\nexport KEY_braille_dots_157\n\nconst KEY_braille_dots_257 = detail.KEY_braille_dots_257\nexport KEY_braille_dots_257\n\nconst KEY_braille_dots_1257 = detail.KEY_braille_dots_1257\nexport KEY_braille_dots_1257\n\nconst KEY_braille_dots_357 = detail.KEY_braille_dots_357\nexport KEY_braille_dots_357\n\nconst KEY_braille_dots_1357 = detail.KEY_braille_dots_1357\nexport KEY_braille_dots_1357\n\nconst KEY_braille_dots_2357 = detail.KEY_braille_dots_2357\nexport KEY_braille_dots_2357\n\nconst KEY_braille_dots_12357 = detail.KEY_braille_dots_12357\nexport KEY_braille_dots_12357\n\nconst KEY_braille_dots_457 = detail.KEY_braille_dots_457\nexport KEY_braille_dots_457\n\nconst KEY_braille_dots_1457 = detail.KEY_braille_dots_1457\nexport KEY_braille_dots_1457\n\nconst KEY_braille_dots_2457 = detail.KEY_braille_dots_2457\nexport KEY_braille_dots_2457\n\nconst KEY_braille_dots_12457 = detail.KEY_braille_dots_12457\nexport KEY_braille_dots_12457\n\nconst KEY_braille_dots_3457 = detail.KEY_braille_dots_3457\nexport KEY_braille_dots_3457\n\nconst KEY_braille_dots_13457 = detail.KEY_braille_dots_13457\nexport KEY_braille_dots_13457\n\nconst KEY_braille_dots_23457 = detail.KEY_braille_dots_23457\nexport KEY_braille_dots_23457\n\nconst KEY_braille_dots_123457 = detail.KEY_braille_dots_123457\nexport KEY_braille_dots_123457\n\nconst KEY_braille_dots_67 = detail.KEY_braille_dots_67\nexport KEY_braille_dots_67\n\nconst KEY_braille_dots_167 = detail.KEY_braille_dots_167\nexport KEY_braille_dots_167\n\nconst KEY_braille_dots_267 = detail.KEY_braille_dots_267\nexport KEY_braille_dots_267\n\nconst KEY_braille_dots_1267 = detail.KEY_braille_dots_1267\nexport KEY_braille_dots_1267\n\nconst KEY_braille_dots_367 = detail.KEY_braille_dots_367\nexport KEY_braille_dots_367\n\nconst KEY_braille_dots_1367 = detail.KEY_braille_dots_1367\nexport KEY_braille_dots_1367\n\nconst KEY_braille_dots_2367 = detail.KEY_braille_dots_2367\nexport KEY_braille_dots_2367\n\nconst KEY_braille_dots_12367 = detail.KEY_braille_dots_12367\nexport KEY_braille_dots_12367\n\nconst KEY_braille_dots_467 = detail.KEY_braille_dots_467\nexport KEY_braille_dots_467\n\nconst KEY_braille_dots_1467 = detail.KEY_braille_dots_1467\nexport KEY_braille_dots_1467\n\nconst KEY_braille_dots_2467 = detail.KEY_braille_dots_2467\nexport KEY_braille_dots_2467\n\nconst KEY_braille_dots_12467 = detail.KEY_braille_dots_12467\nexport KEY_braille_dots_12467\n\nconst KEY_braille_dots_3467 = detail.KEY_braille_dots_3467\nexport KEY_braille_dots_3467\n\nconst KEY_braille_dots_13467 = detail.KEY_braille_dots_13467\nexport KEY_braille_dots_13467\n\nconst KEY_braille_dots_23467 = detail.KEY_braille_dots_23467\nexport KEY_braille_dots_23467\n\nconst KEY_braille_dots_123467 = detail.KEY_braille_dots_123467\nexport KEY_braille_dots_123467\n\nconst KEY_braille_dots_567 = detail.KEY_braille_dots_567\nexport KEY_braille_dots_567\n\nconst KEY_braille_dots_1567 = detail.KEY_braille_dots_1567\nexport KEY_braille_dots_1567\n\nconst KEY_braille_dots_2567 = detail.KEY_braille_dots_2567\nexport KEY_braille_dots_2567\n\nconst KEY_braille_dots_12567 = detail.KEY_braille_dots_12567\nexport KEY_braille_dots_12567\n\nconst KEY_braille_dots_3567 = detail.KEY_braille_dots_3567\nexport KEY_braille_dots_3567\n\nconst KEY_braille_dots_13567 = detail.KEY_braille_dots_13567\nexport KEY_braille_dots_13567\n\nconst KEY_braille_dots_23567 = detail.KEY_braille_dots_23567\nexport KEY_braille_dots_23567\n\nconst KEY_braille_dots_123567 = detail.KEY_braille_dots_123567\nexport KEY_braille_dots_123567\n\nconst KEY_braille_dots_4567 = detail.KEY_braille_dots_4567\nexport KEY_braille_dots_4567\n\nconst KEY_braille_dots_14567 = detail.KEY_braille_dots_14567\nexport KEY_braille_dots_14567\n\nconst KEY_braille_dots_24567 = detail.KEY_braille_dots_24567\nexport KEY_braille_dots_24567\n\nconst KEY_braille_dots_124567 = detail.KEY_braille_dots_124567\nexport KEY_braille_dots_124567\n\nconst KEY_braille_dots_34567 = detail.KEY_braille_dots_34567\nexport KEY_braille_dots_34567\n\nconst KEY_braille_dots_134567 = detail.KEY_braille_dots_134567\nexport KEY_braille_dots_134567\n\nconst KEY_braille_dots_234567 = detail.KEY_braille_dots_234567\nexport KEY_braille_dots_234567\n\nconst KEY_braille_dots_1234567 = detail.KEY_braille_dots_1234567\nexport KEY_braille_dots_1234567\n\nconst KEY_braille_dots_8 = detail.KEY_braille_dots_8\nexport KEY_braille_dots_8\n\nconst KEY_braille_dots_18 = detail.KEY_braille_dots_18\nexport KEY_braille_dots_18\n\nconst KEY_braille_dots_28 = detail.KEY_braille_dots_28\nexport KEY_braille_dots_28\n\nconst KEY_braille_dots_128 = detail.KEY_braille_dots_128\nexport KEY_braille_dots_128\n\nconst KEY_braille_dots_38 = detail.KEY_braille_dots_38\nexport KEY_braille_dots_38\n\nconst KEY_braille_dots_138 = detail.KEY_braille_dots_138\nexport KEY_braille_dots_138\n\nconst KEY_braille_dots_238 = detail.KEY_braille_dots_238\nexport KEY_braille_dots_238\n\nconst KEY_braille_dots_1238 = detail.KEY_braille_dots_1238\nexport KEY_braille_dots_1238\n\nconst KEY_braille_dots_48 = detail.KEY_braille_dots_48\nexport KEY_braille_dots_48\n\nconst KEY_braille_dots_148 = detail.KEY_braille_dots_148\nexport KEY_braille_dots_148\n\nconst KEY_braille_dots_248 = detail.KEY_braille_dots_248\nexport KEY_braille_dots_248\n\nconst KEY_braille_dots_1248 = detail.KEY_braille_dots_1248\nexport KEY_braille_dots_1248\n\nconst KEY_braille_dots_348 = detail.KEY_braille_dots_348\nexport KEY_braille_dots_348\n\nconst KEY_braille_dots_1348 = detail.KEY_braille_dots_1348\nexport KEY_braille_dots_1348\n\nconst KEY_braille_dots_2348 = detail.KEY_braille_dots_2348\nexport KEY_braille_dots_2348\n\nconst KEY_braille_dots_12348 = detail.KEY_braille_dots_12348\nexport KEY_braille_dots_12348\n\nconst KEY_braille_dots_58 = detail.KEY_braille_dots_58\nexport KEY_braille_dots_58\n\nconst KEY_braille_dots_158 = detail.KEY_braille_dots_158\nexport KEY_braille_dots_158\n\nconst KEY_braille_dots_258 = detail.KEY_braille_dots_258\nexport KEY_braille_dots_258\n\nconst KEY_braille_dots_1258 = detail.KEY_braille_dots_1258\nexport KEY_braille_dots_1258\n\nconst KEY_braille_dots_358 = detail.KEY_braille_dots_358\nexport KEY_braille_dots_358\n\nconst KEY_braille_dots_1358 = detail.KEY_braille_dots_1358\nexport KEY_braille_dots_1358\n\nconst KEY_braille_dots_2358 = detail.KEY_braille_dots_2358\nexport KEY_braille_dots_2358\n\nconst KEY_braille_dots_12358 = detail.KEY_braille_dots_12358\nexport KEY_braille_dots_12358\n\nconst KEY_braille_dots_458 = detail.KEY_braille_dots_458\nexport KEY_braille_dots_458\n\nconst KEY_braille_dots_1458 = detail.KEY_braille_dots_1458\nexport KEY_braille_dots_1458\n\nconst KEY_braille_dots_2458 = detail.KEY_braille_dots_2458\nexport KEY_braille_dots_2458\n\nconst KEY_braille_dots_12458 = detail.KEY_braille_dots_12458\nexport KEY_braille_dots_12458\n\nconst KEY_braille_dots_3458 = detail.KEY_braille_dots_3458\nexport KEY_braille_dots_3458\n\nconst KEY_braille_dots_13458 = detail.KEY_braille_dots_13458\nexport KEY_braille_dots_13458\n\nconst KEY_braille_dots_23458 = detail.KEY_braille_dots_23458\nexport KEY_braille_dots_23458\n\nconst KEY_braille_dots_123458 = detail.KEY_braille_dots_123458\nexport KEY_braille_dots_123458\n\nconst KEY_braille_dots_68 = detail.KEY_braille_dots_68\nexport KEY_braille_dots_68\n\nconst KEY_braille_dots_168 = detail.KEY_braille_dots_168\nexport KEY_braille_dots_168\n\nconst KEY_braille_dots_268 = detail.KEY_braille_dots_268\nexport KEY_braille_dots_268\n\nconst KEY_braille_dots_1268 = detail.KEY_braille_dots_1268\nexport KEY_braille_dots_1268\n\nconst KEY_braille_dots_368 = detail.KEY_braille_dots_368\nexport KEY_braille_dots_368\n\nconst KEY_braille_dots_1368 = detail.KEY_braille_dots_1368\nexport KEY_braille_dots_1368\n\nconst KEY_braille_dots_2368 = detail.KEY_braille_dots_2368\nexport KEY_braille_dots_2368\n\nconst KEY_braille_dots_12368 = detail.KEY_braille_dots_12368\nexport KEY_braille_dots_12368\n\nconst KEY_braille_dots_468 = detail.KEY_braille_dots_468\nexport KEY_braille_dots_468\n\nconst KEY_braille_dots_1468 = detail.KEY_braille_dots_1468\nexport KEY_braille_dots_1468\n\nconst KEY_braille_dots_2468 = detail.KEY_braille_dots_2468\nexport KEY_braille_dots_2468\n\nconst KEY_braille_dots_12468 = detail.KEY_braille_dots_12468\nexport KEY_braille_dots_12468\n\nconst KEY_braille_dots_3468 = detail.KEY_braille_dots_3468\nexport KEY_braille_dots_3468\n\nconst KEY_braille_dots_13468 = detail.KEY_braille_dots_13468\nexport KEY_braille_dots_13468\n\nconst KEY_braille_dots_23468 = detail.KEY_braille_dots_23468\nexport KEY_braille_dots_23468\n\nconst KEY_braille_dots_123468 = detail.KEY_braille_dots_123468\nexport KEY_braille_dots_123468\n\nconst KEY_braille_dots_568 = detail.KEY_braille_dots_568\nexport KEY_braille_dots_568\n\nconst KEY_braille_dots_1568 = detail.KEY_braille_dots_1568\nexport KEY_braille_dots_1568\n\nconst KEY_braille_dots_2568 = detail.KEY_braille_dots_2568\nexport KEY_braille_dots_2568\n\nconst KEY_braille_dots_12568 = detail.KEY_braille_dots_12568\nexport KEY_braille_dots_12568\n\nconst KEY_braille_dots_3568 = detail.KEY_braille_dots_3568\nexport KEY_braille_dots_3568\n\nconst KEY_braille_dots_13568 = detail.KEY_braille_dots_13568\nexport KEY_braille_dots_13568\n\nconst KEY_braille_dots_23568 = detail.KEY_braille_dots_23568\nexport KEY_braille_dots_23568\n\nconst KEY_braille_dots_123568 = detail.KEY_braille_dots_123568\nexport KEY_braille_dots_123568\n\nconst KEY_braille_dots_4568 = detail.KEY_braille_dots_4568\nexport KEY_braille_dots_4568\n\nconst KEY_braille_dots_14568 = detail.KEY_braille_dots_14568\nexport KEY_braille_dots_14568\n\nconst KEY_braille_dots_24568 = detail.KEY_braille_dots_24568\nexport KEY_braille_dots_24568\n\nconst KEY_braille_dots_124568 = detail.KEY_braille_dots_124568\nexport KEY_braille_dots_124568\n\nconst KEY_braille_dots_34568 = detail.KEY_braille_dots_34568\nexport KEY_braille_dots_34568\n\nconst KEY_braille_dots_134568 = detail.KEY_braille_dots_134568\nexport KEY_braille_dots_134568\n\nconst KEY_braille_dots_234568 = detail.KEY_braille_dots_234568\nexport KEY_braille_dots_234568\n\nconst KEY_braille_dots_1234568 = detail.KEY_braille_dots_1234568\nexport KEY_braille_dots_1234568\n\nconst KEY_braille_dots_78 = detail.KEY_braille_dots_78\nexport KEY_braille_dots_78\n\nconst KEY_braille_dots_178 = detail.KEY_braille_dots_178\nexport KEY_braille_dots_178\n\nconst KEY_braille_dots_278 = detail.KEY_braille_dots_278\nexport KEY_braille_dots_278\n\nconst KEY_braille_dots_1278 = detail.KEY_braille_dots_1278\nexport KEY_braille_dots_1278\n\nconst KEY_braille_dots_378 = detail.KEY_braille_dots_378\nexport KEY_braille_dots_378\n\nconst KEY_braille_dots_1378 = detail.KEY_braille_dots_1378\nexport KEY_braille_dots_1378\n\nconst KEY_braille_dots_2378 = detail.KEY_braille_dots_2378\nexport KEY_braille_dots_2378\n\nconst KEY_braille_dots_12378 = detail.KEY_braille_dots_12378\nexport KEY_braille_dots_12378\n\nconst KEY_braille_dots_478 = detail.KEY_braille_dots_478\nexport KEY_braille_dots_478\n\nconst KEY_braille_dots_1478 = detail.KEY_braille_dots_1478\nexport KEY_braille_dots_1478\n\nconst KEY_braille_dots_2478 = detail.KEY_braille_dots_2478\nexport KEY_braille_dots_2478\n\nconst KEY_braille_dots_12478 = detail.KEY_braille_dots_12478\nexport KEY_braille_dots_12478\n\nconst KEY_braille_dots_3478 = detail.KEY_braille_dots_3478\nexport KEY_braille_dots_3478\n\nconst KEY_braille_dots_13478 = detail.KEY_braille_dots_13478\nexport KEY_braille_dots_13478\n\nconst KEY_braille_dots_23478 = detail.KEY_braille_dots_23478\nexport KEY_braille_dots_23478\n\nconst KEY_braille_dots_123478 = detail.KEY_braille_dots_123478\nexport KEY_braille_dots_123478\n\nconst KEY_braille_dots_578 = detail.KEY_braille_dots_578\nexport KEY_braille_dots_578\n\nconst KEY_braille_dots_1578 = detail.KEY_braille_dots_1578\nexport KEY_braille_dots_1578\n\nconst KEY_braille_dots_2578 = detail.KEY_braille_dots_2578\nexport KEY_braille_dots_2578\n\nconst KEY_braille_dots_12578 = detail.KEY_braille_dots_12578\nexport KEY_braille_dots_12578\n\nconst KEY_braille_dots_3578 = detail.KEY_braille_dots_3578\nexport KEY_braille_dots_3578\n\nconst KEY_braille_dots_13578 = detail.KEY_braille_dots_13578\nexport KEY_braille_dots_13578\n\nconst KEY_braille_dots_23578 = detail.KEY_braille_dots_23578\nexport KEY_braille_dots_23578\n\nconst KEY_braille_dots_123578 = detail.KEY_braille_dots_123578\nexport KEY_braille_dots_123578\n\nconst KEY_braille_dots_4578 = detail.KEY_braille_dots_4578\nexport KEY_braille_dots_4578\n\nconst KEY_braille_dots_14578 = detail.KEY_braille_dots_14578\nexport KEY_braille_dots_14578\n\nconst KEY_braille_dots_24578 = detail.KEY_braille_dots_24578\nexport KEY_braille_dots_24578\n\nconst KEY_braille_dots_124578 = detail.KEY_braille_dots_124578\nexport KEY_braille_dots_124578\n\nconst KEY_braille_dots_34578 = detail.KEY_braille_dots_34578\nexport KEY_braille_dots_34578\n\nconst KEY_braille_dots_134578 = detail.KEY_braille_dots_134578\nexport KEY_braille_dots_134578\n\nconst KEY_braille_dots_234578 = detail.KEY_braille_dots_234578\nexport KEY_braille_dots_234578\n\nconst KEY_braille_dots_1234578 = detail.KEY_braille_dots_1234578\nexport KEY_braille_dots_1234578\n\nconst KEY_braille_dots_678 = detail.KEY_braille_dots_678\nexport KEY_braille_dots_678\n\nconst KEY_braille_dots_1678 = detail.KEY_braille_dots_1678\nexport KEY_braille_dots_1678\n\nconst KEY_braille_dots_2678 = detail.KEY_braille_dots_2678\nexport KEY_braille_dots_2678\n\nconst KEY_braille_dots_12678 = detail.KEY_braille_dots_12678\nexport KEY_braille_dots_12678\n\nconst KEY_braille_dots_3678 = detail.KEY_braille_dots_3678\nexport KEY_braille_dots_3678\n\nconst KEY_braille_dots_13678 = detail.KEY_braille_dots_13678\nexport KEY_braille_dots_13678\n\nconst KEY_braille_dots_23678 = detail.KEY_braille_dots_23678\nexport KEY_braille_dots_23678\n\nconst KEY_braille_dots_123678 = detail.KEY_braille_dots_123678\nexport KEY_braille_dots_123678\n\nconst KEY_braille_dots_4678 = detail.KEY_braille_dots_4678\nexport KEY_braille_dots_4678\n\nconst KEY_braille_dots_14678 = detail.KEY_braille_dots_14678\nexport KEY_braille_dots_14678\n\nconst KEY_braille_dots_24678 = detail.KEY_braille_dots_24678\nexport KEY_braille_dots_24678\n\nconst KEY_braille_dots_124678 = detail.KEY_braille_dots_124678\nexport KEY_braille_dots_124678\n\nconst KEY_braille_dots_34678 = detail.KEY_braille_dots_34678\nexport KEY_braille_dots_34678\n\nconst KEY_braille_dots_134678 = detail.KEY_braille_dots_134678\nexport KEY_braille_dots_134678\n\nconst KEY_braille_dots_234678 = detail.KEY_braille_dots_234678\nexport KEY_braille_dots_234678\n\nconst KEY_braille_dots_1234678 = detail.KEY_braille_dots_1234678\nexport KEY_braille_dots_1234678\n\nconst KEY_braille_dots_5678 = detail.KEY_braille_dots_5678\nexport KEY_braille_dots_5678\n\nconst KEY_braille_dots_15678 = detail.KEY_braille_dots_15678\nexport KEY_braille_dots_15678\n\nconst KEY_braille_dots_25678 = detail.KEY_braille_dots_25678\nexport KEY_braille_dots_25678\n\nconst KEY_braille_dots_125678 = detail.KEY_braille_dots_125678\nexport KEY_braille_dots_125678\n\nconst KEY_braille_dots_35678 = detail.KEY_braille_dots_35678\nexport KEY_braille_dots_35678\n\nconst KEY_braille_dots_135678 = detail.KEY_braille_dots_135678\nexport KEY_braille_dots_135678\n\nconst KEY_braille_dots_235678 = detail.KEY_braille_dots_235678\nexport KEY_braille_dots_235678\n\nconst KEY_braille_dots_1235678 = detail.KEY_braille_dots_1235678\nexport KEY_braille_dots_1235678\n\nconst KEY_braille_dots_45678 = detail.KEY_braille_dots_45678\nexport KEY_braille_dots_45678\n\nconst KEY_braille_dots_145678 = detail.KEY_braille_dots_145678\nexport KEY_braille_dots_145678\n\nconst KEY_braille_dots_245678 = detail.KEY_braille_dots_245678\nexport KEY_braille_dots_245678\n\nconst KEY_braille_dots_1245678 = detail.KEY_braille_dots_1245678\nexport KEY_braille_dots_1245678\n\nconst KEY_braille_dots_345678 = detail.KEY_braille_dots_345678\nexport KEY_braille_dots_345678\n\nconst KEY_braille_dots_1345678 = detail.KEY_braille_dots_1345678\nexport KEY_braille_dots_1345678\n\nconst KEY_braille_dots_2345678 = detail.KEY_braille_dots_2345678\nexport KEY_braille_dots_2345678\n\nconst KEY_braille_dots_12345678 = detail.KEY_braille_dots_12345678\nexport KEY_braille_dots_12345678\n\nconst KEY_Sinh_ng = detail.KEY_Sinh_ng\nexport KEY_Sinh_ng\n\nconst KEY_Sinh_h2 = detail.KEY_Sinh_h2\nexport KEY_Sinh_h2\n\nconst KEY_Sinh_a = detail.KEY_Sinh_a\nexport KEY_Sinh_a\n\nconst KEY_Sinh_aa = detail.KEY_Sinh_aa\nexport KEY_Sinh_aa\n\nconst KEY_Sinh_ae = detail.KEY_Sinh_ae\nexport KEY_Sinh_ae\n\nconst KEY_Sinh_aee = detail.KEY_Sinh_aee\nexport KEY_Sinh_aee\n\nconst KEY_Sinh_i = detail.KEY_Sinh_i\nexport KEY_Sinh_i\n\nconst KEY_Sinh_ii = detail.KEY_Sinh_ii\nexport KEY_Sinh_ii\n\nconst KEY_Sinh_u = detail.KEY_Sinh_u\nexport KEY_Sinh_u\n\nconst KEY_Sinh_uu = detail.KEY_Sinh_uu\nexport KEY_Sinh_uu\n\nconst KEY_Sinh_ri = detail.KEY_Sinh_ri\nexport KEY_Sinh_ri\n\nconst KEY_Sinh_rii = detail.KEY_Sinh_rii\nexport KEY_Sinh_rii\n\nconst KEY_Sinh_lu = detail.KEY_Sinh_lu\nexport KEY_Sinh_lu\n\nconst KEY_Sinh_luu = detail.KEY_Sinh_luu\nexport KEY_Sinh_luu\n\nconst KEY_Sinh_e = detail.KEY_Sinh_e\nexport KEY_Sinh_e\n\nconst KEY_Sinh_ee = detail.KEY_Sinh_ee\nexport KEY_Sinh_ee\n\nconst KEY_Sinh_ai = detail.KEY_Sinh_ai\nexport KEY_Sinh_ai\n\nconst KEY_Sinh_o = detail.KEY_Sinh_o\nexport KEY_Sinh_o\n\nconst KEY_Sinh_oo = detail.KEY_Sinh_oo\nexport KEY_Sinh_oo\n\nconst KEY_Sinh_au = detail.KEY_Sinh_au\nexport KEY_Sinh_au\n\nconst KEY_Sinh_ka = detail.KEY_Sinh_ka\nexport KEY_Sinh_ka\n\nconst KEY_Sinh_kha = detail.KEY_Sinh_kha\nexport KEY_Sinh_kha\n\nconst KEY_Sinh_ga = detail.KEY_Sinh_ga\nexport KEY_Sinh_ga\n\nconst KEY_Sinh_gha = detail.KEY_Sinh_gha\nexport KEY_Sinh_gha\n\nconst KEY_Sinh_ng2 = detail.KEY_Sinh_ng2\nexport KEY_Sinh_ng2\n\nconst KEY_Sinh_nga = detail.KEY_Sinh_nga\nexport KEY_Sinh_nga\n\nconst KEY_Sinh_ca = detail.KEY_Sinh_ca\nexport KEY_Sinh_ca\n\nconst KEY_Sinh_cha = detail.KEY_Sinh_cha\nexport KEY_Sinh_cha\n\nconst KEY_Sinh_ja = detail.KEY_Sinh_ja\nexport KEY_Sinh_ja\n\nconst KEY_Sinh_jha = detail.KEY_Sinh_jha\nexport KEY_Sinh_jha\n\nconst KEY_Sinh_nya = detail.KEY_Sinh_nya\nexport KEY_Sinh_nya\n\nconst KEY_Sinh_jnya = detail.KEY_Sinh_jnya\nexport KEY_Sinh_jnya\n\nconst KEY_Sinh_nja = detail.KEY_Sinh_nja\nexport KEY_Sinh_nja\n\nconst KEY_Sinh_tta = detail.KEY_Sinh_tta\nexport KEY_Sinh_tta\n\nconst KEY_Sinh_ttha = detail.KEY_Sinh_ttha\nexport KEY_Sinh_ttha\n\nconst KEY_Sinh_dda = detail.KEY_Sinh_dda\nexport KEY_Sinh_dda\n\nconst KEY_Sinh_ddha = detail.KEY_Sinh_ddha\nexport KEY_Sinh_ddha\n\nconst KEY_Sinh_nna = detail.KEY_Sinh_nna\nexport KEY_Sinh_nna\n\nconst KEY_Sinh_ndda = detail.KEY_Sinh_ndda\nexport KEY_Sinh_ndda\n\nconst KEY_Sinh_tha = detail.KEY_Sinh_tha\nexport KEY_Sinh_tha\n\nconst KEY_Sinh_thha = detail.KEY_Sinh_thha\nexport KEY_Sinh_thha\n\nconst KEY_Sinh_dha = detail.KEY_Sinh_dha\nexport KEY_Sinh_dha\n\nconst KEY_Sinh_dhha = detail.KEY_Sinh_dhha\nexport KEY_Sinh_dhha\n\nconst KEY_Sinh_na = detail.KEY_Sinh_na\nexport KEY_Sinh_na\n\nconst KEY_Sinh_ndha = detail.KEY_Sinh_ndha\nexport KEY_Sinh_ndha\n\nconst KEY_Sinh_pa = detail.KEY_Sinh_pa\nexport KEY_Sinh_pa\n\nconst KEY_Sinh_pha = detail.KEY_Sinh_pha\nexport KEY_Sinh_pha\n\nconst KEY_Sinh_ba = detail.KEY_Sinh_ba\nexport KEY_Sinh_ba\n\nconst KEY_Sinh_bha = detail.KEY_Sinh_bha\nexport KEY_Sinh_bha\n\nconst KEY_Sinh_ma = detail.KEY_Sinh_ma\nexport KEY_Sinh_ma\n\nconst KEY_Sinh_mba = detail.KEY_Sinh_mba\nexport KEY_Sinh_mba\n\nconst KEY_Sinh_ya = detail.KEY_Sinh_ya\nexport KEY_Sinh_ya\n\nconst KEY_Sinh_ra = detail.KEY_Sinh_ra\nexport KEY_Sinh_ra\n\nconst KEY_Sinh_la = detail.KEY_Sinh_la\nexport KEY_Sinh_la\n\nconst KEY_Sinh_va = detail.KEY_Sinh_va\nexport KEY_Sinh_va\n\nconst KEY_Sinh_sha = detail.KEY_Sinh_sha\nexport KEY_Sinh_sha\n\nconst KEY_Sinh_ssha = detail.KEY_Sinh_ssha\nexport KEY_Sinh_ssha\n\nconst KEY_Sinh_sa = detail.KEY_Sinh_sa\nexport KEY_Sinh_sa\n\nconst KEY_Sinh_ha = detail.KEY_Sinh_ha\nexport KEY_Sinh_ha\n\nconst KEY_Sinh_lla = detail.KEY_Sinh_lla\nexport KEY_Sinh_lla\n\nconst KEY_Sinh_fa = detail.KEY_Sinh_fa\nexport KEY_Sinh_fa\n\nconst KEY_Sinh_al = detail.KEY_Sinh_al\nexport KEY_Sinh_al\n\nconst KEY_Sinh_aa2 = detail.KEY_Sinh_aa2\nexport KEY_Sinh_aa2\n\nconst KEY_Sinh_ae2 = detail.KEY_Sinh_ae2\nexport KEY_Sinh_ae2\n\nconst KEY_Sinh_aee2 = detail.KEY_Sinh_aee2\nexport KEY_Sinh_aee2\n\nconst KEY_Sinh_i2 = detail.KEY_Sinh_i2\nexport KEY_Sinh_i2\n\nconst KEY_Sinh_ii2 = detail.KEY_Sinh_ii2\nexport KEY_Sinh_ii2\n\nconst KEY_Sinh_u2 = detail.KEY_Sinh_u2\nexport KEY_Sinh_u2\n\nconst KEY_Sinh_uu2 = detail.KEY_Sinh_uu2\nexport KEY_Sinh_uu2\n\nconst KEY_Sinh_ru2 = detail.KEY_Sinh_ru2\nexport KEY_Sinh_ru2\n\nconst KEY_Sinh_e2 = detail.KEY_Sinh_e2\nexport KEY_Sinh_e2\n\nconst KEY_Sinh_ee2 = detail.KEY_Sinh_ee2\nexport KEY_Sinh_ee2\n\nconst KEY_Sinh_ai2 = detail.KEY_Sinh_ai2\nexport KEY_Sinh_ai2\n\nconst KEY_Sinh_o2 = detail.KEY_Sinh_o2\nexport KEY_Sinh_o2\n\nconst KEY_Sinh_oo2 = detail.KEY_Sinh_oo2\nexport KEY_Sinh_oo2\n\nconst KEY_Sinh_au2 = detail.KEY_Sinh_au2\nexport KEY_Sinh_au2\n\nconst KEY_Sinh_lu2 = detail.KEY_Sinh_lu2\nexport KEY_Sinh_lu2\n\nconst KEY_Sinh_ruu2 = detail.KEY_Sinh_ruu2\nexport KEY_Sinh_ruu2\n\nconst KEY_Sinh_luu2 = detail.KEY_Sinh_luu2\nexport KEY_Sinh_luu2\n\nconst KEY_Sinh_kunddaliya = detail.KEY_Sinh_kunddaliya\nexport KEY_Sinh_kunddaliya\n\nconst KEY_ModeLock = detail.KEY_ModeLock\nexport KEY_ModeLock\n\nconst KEY_MonBrightnessUp = detail.KEY_MonBrightnessUp\nexport KEY_MonBrightnessUp\n\nconst KEY_MonBrightnessDown = detail.KEY_MonBrightnessDown\nexport KEY_MonBrightnessDown\n\nconst KEY_KbdLightOnOff = detail.KEY_KbdLightOnOff\nexport KEY_KbdLightOnOff\n\nconst KEY_KbdBrightnessUp = detail.KEY_KbdBrightnessUp\nexport KEY_KbdBrightnessUp\n\nconst KEY_KbdBrightnessDown = detail.KEY_KbdBrightnessDown\nexport KEY_KbdBrightnessDown\n\nconst KEY_Standby = detail.KEY_Standby\nexport KEY_Standby\n\nconst KEY_AudioLowerVolume = detail.KEY_AudioLowerVolume\nexport KEY_AudioLowerVolume\n\nconst KEY_AudioMute = detail.KEY_AudioMute\nexport KEY_AudioMute\n\nconst KEY_AudioRaiseVolume = detail.KEY_AudioRaiseVolume\nexport KEY_AudioRaiseVolume\n\nconst KEY_AudioPlay = detail.KEY_AudioPlay\nexport KEY_AudioPlay\n\nconst KEY_AudioStop = detail.KEY_AudioStop\nexport KEY_AudioStop\n\nconst KEY_AudioPrev = detail.KEY_AudioPrev\nexport KEY_AudioPrev\n\nconst KEY_AudioNext = detail.KEY_AudioNext\nexport KEY_AudioNext\n\nconst KEY_HomePage = detail.KEY_HomePage\nexport KEY_HomePage\n\nconst KEY_Mail = detail.KEY_Mail\nexport KEY_Mail\n\nconst KEY_Start = detail.KEY_Start\nexport KEY_Start\n\nconst KEY_Search = detail.KEY_Search\nexport KEY_Search\n\nconst KEY_AudioRecord = detail.KEY_AudioRecord\nexport KEY_AudioRecord\n\nconst KEY_Calculator = detail.KEY_Calculator\nexport KEY_Calculator\n\nconst KEY_Memo = detail.KEY_Memo\nexport KEY_Memo\n\nconst KEY_ToDoList = detail.KEY_ToDoList\nexport KEY_ToDoList\n\nconst KEY_Calendar = detail.KEY_Calendar\nexport KEY_Calendar\n\nconst KEY_PowerDown = detail.KEY_PowerDown\nexport KEY_PowerDown\n\nconst KEY_ContrastAdjust = detail.KEY_ContrastAdjust\nexport KEY_ContrastAdjust\n\nconst KEY_RockerUp = detail.KEY_RockerUp\nexport KEY_RockerUp\n\nconst KEY_RockerDown = detail.KEY_RockerDown\nexport KEY_RockerDown\n\nconst KEY_RockerEnter = detail.KEY_RockerEnter\nexport KEY_RockerEnter\n\nconst KEY_Back = detail.KEY_Back\nexport KEY_Back\n\nconst KEY_Forward = detail.KEY_Forward\nexport KEY_Forward\n\nconst KEY_Stop = detail.KEY_Stop\nexport KEY_Stop\n\nconst KEY_Refresh = detail.KEY_Refresh\nexport KEY_Refresh\n\nconst KEY_PowerOff = detail.KEY_PowerOff\nexport KEY_PowerOff\n\nconst KEY_WakeUp = detail.KEY_WakeUp\nexport KEY_WakeUp\n\nconst KEY_Eject = detail.KEY_Eject\nexport KEY_Eject\n\nconst KEY_ScreenSaver = detail.KEY_ScreenSaver\nexport KEY_ScreenSaver\n\nconst KEY_WWW = detail.KEY_WWW\nexport KEY_WWW\n\nconst KEY_Sleep = detail.KEY_Sleep\nexport KEY_Sleep\n\nconst KEY_Favorites = detail.KEY_Favorites\nexport KEY_Favorites\n\nconst KEY_AudioPause = detail.KEY_AudioPause\nexport KEY_AudioPause\n\nconst KEY_AudioMedia = detail.KEY_AudioMedia\nexport KEY_AudioMedia\n\nconst KEY_MyComputer = detail.KEY_MyComputer\nexport KEY_MyComputer\n\nconst KEY_VendorHome = detail.KEY_VendorHome\nexport KEY_VendorHome\n\nconst KEY_LightBulb = detail.KEY_LightBulb\nexport KEY_LightBulb\n\nconst KEY_Shop = detail.KEY_Shop\nexport KEY_Shop\n\nconst KEY_History = detail.KEY_History\nexport KEY_History\n\nconst KEY_OpenURL = detail.KEY_OpenURL\nexport KEY_OpenURL\n\nconst KEY_AddFavorite = detail.KEY_AddFavorite\nexport KEY_AddFavorite\n\nconst KEY_HotLinks = detail.KEY_HotLinks\nexport KEY_HotLinks\n\nconst KEY_BrightnessAdjust = detail.KEY_BrightnessAdjust\nexport KEY_BrightnessAdjust\n\nconst KEY_Finance = detail.KEY_Finance\nexport KEY_Finance\n\nconst KEY_Community = detail.KEY_Community\nexport KEY_Community\n\nconst KEY_AudioRewind = detail.KEY_AudioRewind\nexport KEY_AudioRewind\n\nconst KEY_BackForward = detail.KEY_BackForward\nexport KEY_BackForward\n\nconst KEY_Launch0 = detail.KEY_Launch0\nexport KEY_Launch0\n\nconst KEY_Launch1 = detail.KEY_Launch1\nexport KEY_Launch1\n\nconst KEY_Launch2 = detail.KEY_Launch2\nexport KEY_Launch2\n\nconst KEY_Launch3 = detail.KEY_Launch3\nexport KEY_Launch3\n\nconst KEY_Launch4 = detail.KEY_Launch4\nexport KEY_Launch4\n\nconst KEY_Launch5 = detail.KEY_Launch5\nexport KEY_Launch5\n\nconst KEY_Launch6 = detail.KEY_Launch6\nexport KEY_Launch6\n\nconst KEY_Launch7 = detail.KEY_Launch7\nexport KEY_Launch7\n\nconst KEY_Launch8 = detail.KEY_Launch8\nexport KEY_Launch8\n\nconst KEY_Launch9 = detail.KEY_Launch9\nexport KEY_Launch9\n\nconst KEY_LaunchA = detail.KEY_LaunchA\nexport KEY_LaunchA\n\nconst KEY_LaunchB = detail.KEY_LaunchB\nexport KEY_LaunchB\n\nconst KEY_LaunchC = detail.KEY_LaunchC\nexport KEY_LaunchC\n\nconst KEY_LaunchD = detail.KEY_LaunchD\nexport KEY_LaunchD\n\nconst KEY_LaunchE = detail.KEY_LaunchE\nexport KEY_LaunchE\n\nconst KEY_LaunchF = detail.KEY_LaunchF\nexport KEY_LaunchF\n\nconst KEY_ApplicationLeft = detail.KEY_ApplicationLeft\nexport KEY_ApplicationLeft\n\nconst KEY_ApplicationRight = detail.KEY_ApplicationRight\nexport KEY_ApplicationRight\n\nconst KEY_Book = detail.KEY_Book\nexport KEY_Book\n\nconst KEY_CD = detail.KEY_CD\nexport KEY_CD\n\nconst KEY_WindowClear = detail.KEY_WindowClear\nexport KEY_WindowClear\n\nconst KEY_Close = detail.KEY_Close\nexport KEY_Close\n\nconst KEY_Copy = detail.KEY_Copy\nexport KEY_Copy\n\nconst KEY_Cut = detail.KEY_Cut\nexport KEY_Cut\n\nconst KEY_Display = detail.KEY_Display\nexport KEY_Display\n\nconst KEY_DOS = detail.KEY_DOS\nexport KEY_DOS\n\nconst KEY_Documents = detail.KEY_Documents\nexport KEY_Documents\n\nconst KEY_Excel = detail.KEY_Excel\nexport KEY_Excel\n\nconst KEY_Explorer = detail.KEY_Explorer\nexport KEY_Explorer\n\nconst KEY_Game = detail.KEY_Game\nexport KEY_Game\n\nconst KEY_Go = detail.KEY_Go\nexport KEY_Go\n\nconst KEY_iTouch = detail.KEY_iTouch\nexport KEY_iTouch\n\nconst KEY_LogOff = detail.KEY_LogOff\nexport KEY_LogOff\n\nconst KEY_Market = detail.KEY_Market\nexport KEY_Market\n\nconst KEY_Meeting = detail.KEY_Meeting\nexport KEY_Meeting\n\nconst KEY_MenuKB = detail.KEY_MenuKB\nexport KEY_MenuKB\n\nconst KEY_MenuPB = detail.KEY_MenuPB\nexport KEY_MenuPB\n\nconst KEY_MySites = detail.KEY_MySites\nexport KEY_MySites\n\nconst KEY_New = detail.KEY_New\nexport KEY_New\n\nconst KEY_News = detail.KEY_News\nexport KEY_News\n\nconst KEY_OfficeHome = detail.KEY_OfficeHome\nexport KEY_OfficeHome\n\nconst KEY_Open = detail.KEY_Open\nexport KEY_Open\n\nconst KEY_Option = detail.KEY_Option\nexport KEY_Option\n\nconst KEY_Paste = detail.KEY_Paste\nexport KEY_Paste\n\nconst KEY_Phone = detail.KEY_Phone\nexport KEY_Phone\n\nconst KEY_Reply = detail.KEY_Reply\nexport KEY_Reply\n\nconst KEY_Reload = detail.KEY_Reload\nexport KEY_Reload\n\nconst KEY_RotateWindows = detail.KEY_RotateWindows\nexport KEY_RotateWindows\n\nconst KEY_RotationPB = detail.KEY_RotationPB\nexport KEY_RotationPB\n\nconst KEY_RotationKB = detail.KEY_RotationKB\nexport KEY_RotationKB\n\nconst KEY_Save = detail.KEY_Save\nexport KEY_Save\n\nconst KEY_ScrollUp = detail.KEY_ScrollUp\nexport KEY_ScrollUp\n\nconst KEY_ScrollDown = detail.KEY_ScrollDown\nexport KEY_ScrollDown\n\nconst KEY_ScrollClick = detail.KEY_ScrollClick\nexport KEY_ScrollClick\n\nconst KEY_Send = detail.KEY_Send\nexport KEY_Send\n\nconst KEY_Spell = detail.KEY_Spell\nexport KEY_Spell\n\nconst KEY_SplitScreen = detail.KEY_SplitScreen\nexport KEY_SplitScreen\n\nconst KEY_Support = detail.KEY_Support\nexport KEY_Support\n\nconst KEY_TaskPane = detail.KEY_TaskPane\nexport KEY_TaskPane\n\nconst KEY_Terminal = detail.KEY_Terminal\nexport KEY_Terminal\n\nconst KEY_Tools = detail.KEY_Tools\nexport KEY_Tools\n\nconst KEY_Travel = detail.KEY_Travel\nexport KEY_Travel\n\nconst KEY_UserPB = detail.KEY_UserPB\nexport KEY_UserPB\n\nconst KEY_User1KB = detail.KEY_User1KB\nexport KEY_User1KB\n\nconst KEY_User2KB = detail.KEY_User2KB\nexport KEY_User2KB\n\nconst KEY_Video = detail.KEY_Video\nexport KEY_Video\n\nconst KEY_WheelButton = detail.KEY_WheelButton\nexport KEY_WheelButton\n\nconst KEY_Word = detail.KEY_Word\nexport KEY_Word\n\nconst KEY_Xfer = detail.KEY_Xfer\nexport KEY_Xfer\n\nconst KEY_ZoomIn = detail.KEY_ZoomIn\nexport KEY_ZoomIn\n\nconst KEY_ZoomOut = detail.KEY_ZoomOut\nexport KEY_ZoomOut\n\nconst KEY_Away = detail.KEY_Away\nexport KEY_Away\n\nconst KEY_Messenger = detail.KEY_Messenger\nexport KEY_Messenger\n\nconst KEY_WebCam = detail.KEY_WebCam\nexport KEY_WebCam\n\nconst KEY_MailForward = detail.KEY_MailForward\nexport KEY_MailForward\n\nconst KEY_Pictures = detail.KEY_Pictures\nexport KEY_Pictures\n\nconst KEY_Music = detail.KEY_Music\nexport KEY_Music\n\nconst KEY_Battery = detail.KEY_Battery\nexport KEY_Battery\n\nconst KEY_Bluetooth = detail.KEY_Bluetooth\nexport KEY_Bluetooth\n\nconst KEY_WLAN = detail.KEY_WLAN\nexport KEY_WLAN\n\nconst KEY_UWB = detail.KEY_UWB\nexport KEY_UWB\n\nconst KEY_AudioForward = detail.KEY_AudioForward\nexport KEY_AudioForward\n\nconst KEY_AudioRepeat = detail.KEY_AudioRepeat\nexport KEY_AudioRepeat\n\nconst KEY_AudioRandomPlay = detail.KEY_AudioRandomPlay\nexport KEY_AudioRandomPlay\n\nconst KEY_Subtitle = detail.KEY_Subtitle\nexport KEY_Subtitle\n\nconst KEY_AudioCycleTrack = detail.KEY_AudioCycleTrack\nexport KEY_AudioCycleTrack\n\nconst KEY_CycleAngle = detail.KEY_CycleAngle\nexport KEY_CycleAngle\n\nconst KEY_FrameBack = detail.KEY_FrameBack\nexport KEY_FrameBack\n\nconst KEY_FrameForward = detail.KEY_FrameForward\nexport KEY_FrameForward\n\nconst KEY_Time = detail.KEY_Time\nexport KEY_Time\n\nconst KEY_SelectButton = detail.KEY_SelectButton\nexport KEY_SelectButton\n\nconst KEY_View = detail.KEY_View\nexport KEY_View\n\nconst KEY_TopMenu = detail.KEY_TopMenu\nexport KEY_TopMenu\n\nconst KEY_Red = detail.KEY_Red\nexport KEY_Red\n\nconst KEY_Green = detail.KEY_Green\nexport KEY_Green\n\nconst KEY_Yellow = detail.KEY_Yellow\nexport KEY_Yellow\n\nconst KEY_Blue = detail.KEY_Blue\nexport KEY_Blue\n\nconst KEY_Suspend = detail.KEY_Suspend\nexport KEY_Suspend\n\nconst KEY_Hibernate = detail.KEY_Hibernate\nexport KEY_Hibernate\n\nconst KEY_TouchpadToggle = detail.KEY_TouchpadToggle\nexport KEY_TouchpadToggle\n\nconst KEY_TouchpadOn = detail.KEY_TouchpadOn\nexport KEY_TouchpadOn\n\nconst KEY_TouchpadOff = detail.KEY_TouchpadOff\nexport KEY_TouchpadOff\n\nconst KEY_AudioMicMute = detail.KEY_AudioMicMute\nexport KEY_AudioMicMute\n\nconst KEY_Keyboard = detail.KEY_Keyboard\nexport KEY_Keyboard\n\nconst KEY_WWAN = detail.KEY_WWAN\nexport KEY_WWAN\n\nconst KEY_RFKill = detail.KEY_RFKill\nexport KEY_RFKill\n\nconst KEY_AudioPreset = detail.KEY_AudioPreset\nexport KEY_AudioPreset\n\nconst KEY_Switch_VT_1 = detail.KEY_Switch_VT_1\nexport KEY_Switch_VT_1\n\nconst KEY_Switch_VT_2 = detail.KEY_Switch_VT_2\nexport KEY_Switch_VT_2\n\nconst KEY_Switch_VT_3 = detail.KEY_Switch_VT_3\nexport KEY_Switch_VT_3\n\nconst KEY_Switch_VT_4 = detail.KEY_Switch_VT_4\nexport KEY_Switch_VT_4\n\nconst KEY_Switch_VT_5 = detail.KEY_Switch_VT_5\nexport KEY_Switch_VT_5\n\nconst KEY_Switch_VT_6 = detail.KEY_Switch_VT_6\nexport KEY_Switch_VT_6\n\nconst KEY_Switch_VT_7 = detail.KEY_Switch_VT_7\nexport KEY_Switch_VT_7\n\nconst KEY_Switch_VT_8 = detail.KEY_Switch_VT_8\nexport KEY_Switch_VT_8\n\nconst KEY_Switch_VT_9 = detail.KEY_Switch_VT_9\nexport KEY_Switch_VT_9\n\nconst KEY_Switch_VT_10 = detail.KEY_Switch_VT_10\nexport KEY_Switch_VT_10\n\nconst KEY_Switch_VT_11 = detail.KEY_Switch_VT_11\nexport KEY_Switch_VT_11\n\nconst KEY_Switch_VT_12 = detail.KEY_Switch_VT_12\nexport KEY_Switch_VT_12\n\nconst KEY_Ungrab = detail.KEY_Ungrab\nexport KEY_Ungrab\n\nconst KEY_ClearGrab = detail.KEY_ClearGrab\nexport KEY_ClearGrab\n\nconst KEY_Next_VMode = detail.KEY_Next_VMode\nexport KEY_Next_VMode\n\nconst KEY_Prev_VMode = detail.KEY_Prev_VMode\nexport KEY_Prev_VMode\n\nconst KEY_LogWindowTree = detail.KEY_LogWindowTree\nexport KEY_LogWindowTree\n\nconst KEY_LogGrabInfo = detail.KEY_LogGrabInfo\nexport KEY_LogGrabInfo\n\n@do_not_compile const to_generate = quote\n    \n    file = open(\"in.txt\") \n\n    cpp_out = open(\"out.cpp\", \"w\")\n    jl_binding_out = open(\"jl_binding.cpp\", \"w\")\n    jl_out = open(\"out.jl\", \"w\")\n\n    lines = String[]\n    while !eof(file)\n        push!(lines, readline(file))\n    end\n\n    for i in 1:length(lines)\n\n        new_name = \"\"\n\n        first = true\n        previous_char_is_lowercase = false\n        for c in split(lines[i][1:(end-1)], \"GDK_KEY_\")[2]\n            \n            new_name *= c\n            previous_char_is_lowercase = islowercase(c)\n            first = false\n        end\n        \n        name = \"KEY_\" * (new_name)\n\n        write(cpp_out, replace(lines[i], \"TODO\" => name) * \"\\n\")\n        write(jl_binding_out, \"module.set_const(\\\"$name\\\", (guint) mousetrap::$name);\\n\")\n        write(jl_out, \"const $name = detail.$name\\nexport $name\\n\")\n    end\nend"
  },
  {
    "path": "test/example.jl",
    "content": "# File used for debugging and for dosc examples, why are you snooping through this file?\n\n# File used for debugging and for dosc examples, why are you snooping through this file?\nusing Mousetrap\n\nmain() do app::Application\n    window = Window(app)\n\n    column_view = ColumnView()\n\n    row_index = push_back_column!(column_view, \" \")\n    count_column = push_back_column!(column_view, \"#\")\n    name_column = push_back_column!(column_view, \"Name\")\n    weigt_column = push_back_column!(column_view, \"Weight\")\n    unit_column = push_back_column!(column_view, \"Units\")\n\n    # fill columns with example text\n    for i in 1:100\n        push_front_row!(column_view,\n            Label(string(i)),           # row index\n            Label(string(rand(0:99))),  # count\n            Label(rand([\"Apple\", \"Orange\", \"Banana\", \"Kumquat\", \"Durian\", \"Mangosteen\"])), # name\n            Label(string(rand(0:100))), # weight\n            Label(string(rand([\"mg\", \"g\", \"kg\", \"ton\"]))) # unit\n        )\n    end\n\n    scrolled_viewport = Viewport()\n    set_child!(scrolled_viewport, column_view)\n    set_child!(window, scrolled_viewport)\n    present!(window)\nend\n\nif false\n\nadd_css!(\"\"\"\n@keyframes spin-animation {\n    0%   { transform: rotate(0turn)   scale(1); }\n    50%  { transform: rotate(0.5turn) scale(2); }\n    100% { transform: rotate(1turn)   scale(1); }\n}\n\n.spinning {\n    animation: spin-animation;\n    animation-duration: 1s;\n    animation-iteration-count: infinite;\n    animation-timing-function: ease-in-out;\n}\n\n.monospaced {\n    font-family: monospace;\n}\n\"\"\")\n\nmain() do app::Application\n    window = Window(app)\n    set_title!(window, \"mousesnap.jl\")\n\n    button = Button()\n    add_css_class!(button, \"spinning\")\n\n    frame = AspectFrame(1.0, button)\n    set_margin!(frame, 10)\n\n    #set_child!(window, frame)\n\n    text_view = TextView();\n    add_css_class!(text_view, \"monospaced\")\n    set_child!(window, text_view)\n    present!(window)\nend\n\nexit(0)\n\nadd_css!(\"\"\"\n.custom {\n    background-color: hotpink;\n    border-color: darker(hotpink);\n    font-family: monospace;\n    border-radius: 0%;\n}\n\"\"\")\n\nfunction set_accent_color!(color::RGBA) \n    add_css!(\"\"\"\n        @define-color accent_bg_color $(serialize(color));\n    \"\"\")\nend\n\nmain() do app::Application\n\n    window = Window(app)\n    set_title!(window, \"mousetrap.jl\")\n    \n    box = CenterBox(ORIENTATION_VERTICAL)\n    \n    check_button = CheckButton()\n    set_alignment!(check_button, ALIGNMENT_CENTER)\n    set_expand!(check_button, false)\n    set_start_child!(box, check_button)\n\n    scale = Scale(0, 1, 0.01)\n    set_expand!(scale, true)\n    set_center_child!(box, scale)\n\n    switch = Switch()\n    set_expand!(switch, false)\n    set_alignment!(switch, ALIGNMENT_CENTER)\n    set_end_child!(box, switch)\n\n    action = Action(\"change_accent_color\", app) do self\n        set_accent_color!(RGBA(1, 0, 1, 1))\n    end\n    add_shortcut!(action, \"<Control>e\")\n    set_listens_for_shortcut_action!(scale, action)\n\n    set_expand!(box, true)\n    set_margin!(box, 10)\n    set_child!(window, box)\n    present!(window)\nend\n\nexit(0)\n\n# define widget colors\nconst WidgetColor = String\nconst WIDGET_COLOR_DEFAULT = \"default\"\nconst WIDGET_COLOR_ACCENT = \"accent\"\nconst WIDGET_COLOR_SUCCESS = \"success\"\nconst WIDGET_COLOR_WARNING = \"warning\"\nconst WIDGET_COLOR_ERROR = \"error\"\n\n# create a CSS classes for each and load it into the global theme\nfor name in [WIDGET_COLOR_DEFAULT, WIDGET_COLOR_ACCENT, WIDGET_COLOR_SUCCESS, WIDGET_COLOR_WARNING, WIDGET_COLOR_ERROR]\n    add_css!(\"\"\"\n    $name:not(.opaque) {\n        background-color: @$(name)_fg_color;\n    }\n    .$name.opaque {\n        background-color: @$(name)_bg_color;\n        color: @$(name)_fg_color;\n    }\n    \"\"\")\nend\n\n\"\"\"\n```\nset_color!(::Widget, ::WidgetColor) -> Nothing\n```\n\"\"\"\nfunction set_accent_color!(widget::Widget, color, opaque = true)\n    if !(color in [WIDGET_COLOR_DEFAULT, WIDGET_COLOR_ACCENT, WIDGET_COLOR_SUCCESS, WIDGET_COLOR_WARNING, WIDGET_COLOR_ERROR])\n        log_critical(\"In set_color!: Color ID `\" * color * \"` is not supported\")\n    end    \n    add_css_class!(widget, color)\n    if opaque\n        add_css_class!(widget, \"opaque\")\n    end\nend\n\nmain() do app::Application\n    window = Window(app)\n\n    set_title!(window, \"mousetrap.jl\")\n    function create_widget() \n        return Button(Label(\"TEST\"))\n    end\n    \n    column_view = ColumnView()\n    column = push_back_column!(column_view, \" \")\n    set_widget_at!(column_view, column, 1, Label(\"<small>!opaque</small>\"))\n    set_widget_at!(column_view, column, 2, Label(\"<small>opaque</small>\"))\n\n    for color in [WIDGET_COLOR_DEFAULT, WIDGET_COLOR_ACCENT, WIDGET_COLOR_SUCCESS, WIDGET_COLOR_WARNING, WIDGET_COLOR_ERROR]\n        column = push_back_column!(column_view, color)\n        \n        # non-opaque version\n        widget = create_widget()\n        set_accent_color!(widget, color, false)\n        set_widget_at!(column_view, column, 1, widget)\n\n        # opaque version\n        widget = create_widget()\n        set_accent_color!(widget, color, true)\n        set_widget_at!(column_view, column, 2, widget)\n    end\n\n    set_child!(window, column_view)\n    present!(window)\nend\nexit(0)\n\nusing Mousetrap\nmain() do app::Application\n    window = Window(app)\n    widget = Button()\n\n    box = Box(ORIENTATION_HORIZONTAL)\n    for name in [\"accent\", \"success\", \"warning\", \"error\"]\n        add_css!(\"\"\"\n        $name:not(.opaque) {\n            background-color: @$(name)_fg_color;\n        }\n        .$name.opaque {\n            background-color: @$(name)_bg_color;\n            color: @$(name)_fg_color;\n        }\n        \"\"\")\n    end\n\n    names = [\"default\", \"accent\", \"success\", \"warning\", \"error\"]\n\n\n    column_view = ColumnView()\n    \n    first_column = push_back_column!(column_view, \" \")\n    \n\n    for name in names\n        push_back_column!(column_view, name)\n    end\n\n    #, Switch, CheckButton, ProgressBar, Spinner, Scale, SpinButton, Entry, Separator\n\n    j = 1\n    for create_widget in [\n        () -> Button(Label(\"TEST\")), \n        () -> Switch(),\n        () -> ProgressBar(),\n        () -> Spinner(),\n        () -> LevelBar(0, 1),\n        () -> Scale(0, 1, 1),\n        () -> Entry(),\n        () -> Separator()\n    ]\n        local default_label = Label(\"default\")\n        local opaque_label = Label(\"opaque\")\n\n        for label in [default_label, opaque_label]\n            add_css_class!(label, \"dimmed\")\n            add_css_class!(label, \"caption\")\n        end\n\n        set_widget_at!(column_view, first_column, j, default_label)\n        set_widget_at!(column_view, first_column, j + 1, opaque_label)\n\n        for name in names\n            i = j \n            column = get_column_with_title(column_view, name)\n\n            local non_opaque = create_widget()\n            add_css_class!(non_opaque, name)\n            set_widget_at!(column_view, column, i, non_opaque)\n\n            try\n                set_child!(non_opaque, Label(\"TEST\"))\n            catch end\n\n            i = i + 1\n\n            local opaque = create_widget()\n            add_css_class!(opaque, name)\n            add_css_class!(opaque, \"opaque\")\n            set_widget_at!(column_view, column, i, opaque)\n\n            try\n                set_child!(opaque, Label(\"TEST\"))\n            catch end\n\n            for widget in [non_opaque, opaque]\n                #set_alignment!(widget, ALIGNMENT_CENTER)\n                #set_expand!(widget, false)\n            end\n        end\n        j = j + 2\n    end\n\n    set_child!(window, Viewport(column_view))\n    present!(window)\nend\n\nexit(0)\n\nmain() do app::Application\n    window = Window(app)\n    set_title!(window, \"mousetrap.jl\")\n\n    # animate a gradual fade-out\n    button = Button(Label(\"SPIN\"))\n    aspect_frame = AspectFrame(1.0, button)\n    set_margin!(aspect_frame, 10)\n\n    transform_bin = TransformBin()\n    set_child!(transform_bin, aspect_frame)\n\n    animation = Animation(transform_bin, seconds(1))\n    on_tick!(animation, transform_bin) do self::Animation, value::AbstractFloat, transform_bin::TransformBin\n        reset!(transform_bin)\n        rotate!(transform_bin, degrees(value * 360))\n        scale!(transform_bin, 1 + value, 1 + value)\n    end\n\n    on_done!(animation, transform_bin) do self::Animation, transform_bin::TransformBin\n        reset!(transform_bin)\n    end\n\n    # start animation when button is clicked\n    connect_signal_clicked!(button, animation) do self::Button, animation::Animation\n        play!(animation)\n    end\n   \n    set_child!(window, transform_bin)\n    present!(window)\nend\n\n\n#=\n@static if false\n\nstruct TexturePage <: Widget\n    center_box::CenterBox\n    label::Label\n    render_area::RenderArea\n    texture::Texture\n    shape::Shape\n\n    function TexturePage(label::String, image::Image, wrap_mode::TextureWrapMode)\n        out = new(\n            CenterBox(ORIENTATION_VERTICAL),\n            Label(\"<tt>\" * label * \"</tt>\"),\n            RenderArea(),\n            Texture(),\n            Rectangle(Vector2f(-1, 1), Vector2f(2, 2))\n        )\n\n        set_expand!(out.render_area, true)\n        set_size_request!(out.render_area, Vector2f(150, 150))    \n\n        set_start_child!(out.center_box, AspectFrame(1.0, Frame(out.render_area)))\n        set_end_child!(out.center_box, out.label)\n        set_margin!(out.label, 10)\n\n        create_from_image!(out.texture, image)\n        set_wrap_mode!(out.texture, wrap_mode)\n        \n        set_texture!(out.shape, out.texture)\n        set_vertex_texture_coordinate!(out.shape, 1, Vector2f(-1, -1))\n        set_vertex_texture_coordinate!(out.shape, 2, Vector2f(2, -1))\n        set_vertex_texture_coordinate!(out.shape, 3, Vector2f(2, 2))\n        set_vertex_texture_coordinate!(out.shape, 4, Vector2f(-1, 2))\n\n        add_render_task!(out.render_area, RenderTask(out.shape))\n        return out\n    end\nend\nMousetrap.get_top_level_widget(x::TexturePage) = x.center_box\n\n\nmain() do app::Application\n    window = Window(app)\n    set_title!(window, \"Mousetrap.jl\")\n\n    render_area = RenderArea()\n   \n    image = Image()\n    create_from_file!(image, \"docs/src/assets/logo.png\")\n\n    size = get_size(image)\n    hue_step = 1 / size.x\n    for i in 1:size.y\n        for j in 1:size.x\n            if get_pixel(image, i, j).a == 0\n                set_pixel!(image, i, j, HSVA(j * hue_step, 1, 1, 1))\n            end\n        end\n    end\n\n    box = Box(ORIENTATION_HORIZONTAL)\n    set_spacing!(box, 10)\n    set_margin!(box, 10)\n\n    push_back!(box, TexturePage(\"ZERO\", image, TEXTURE_WRAP_MODE_ZERO))\n    push_back!(box, TexturePage(\"ONE\", image, TEXTURE_WRAP_MODE_ONE))\n    push_back!(box, TexturePage(\"STRETCH\", image, TEXTURE_WRAP_MODE_STRETCH))\n    push_back!(box, TexturePage(\"REPEAT\", image, TEXTURE_WRAP_MODE_REPEAT))\n    push_back!(box, TexturePage(\"MIRROR\", image, TEXTURE_WRAP_MODE_MIRROR))\n\n    set_child!(window, box)\n    present!(window)\nend\n\nend\n\n# compound widget that is an entire stack page, render area with the shape + a label below\nstruct ShapePage <: Widget\n\n    separator::Separator\n    render_area::RenderArea\n    overlay::Overlay\n    frame::Frame\n    aspect_frame::AspectFrame\n    label::Label\n    center_box::CenterBox\n\n    function ShapePage(title::String, shape::Shape)\n\n        out = new(\n            Separator(),\n            RenderArea(),\n            Overlay(),\n            Frame(),\n            AspectFrame(1.0),\n            Label(title),\n            CenterBox(ORIENTATION_VERTICAL)\n        )\n\n        set_child!(out.overlay, out.separator)\n        add_overlay!(out.overlay, out.render_area)\n        set_child!(out.frame, out.overlay)\n        set_child!(out.aspect_frame, out.frame)\n    \n        set_center_child!(out.center_box, out.aspect_frame)\n        set_end_child!(out.center_box, out.label)\n\n        set_size_request!(out.aspect_frame, Vector2f(150, 150))\n        set_expand!(out.aspect_frame, true)\n\n        set_margin!(out.aspect_frame, 10)\n        set_margin!(out.label, 10)\n\n        add_render_task!(out.render_area, RenderTask(shape))\n\n        radius = 0.001\n        n_vertices = get_n_vertices(shape)\n        for i in 1:n_vertices\n            pos = get_vertex_position(shape, i)\n            to_add = Circle(Vector2f(pos.x, pos.y), radius, 16)\n            set_color!(to_add, HSVA(i / n_vertices, 1, 1, 1))\n            add_render_task!(out.render_area, RenderTask(to_add))\n        end\n\n        # Widget hierarchy for clarity:\n        # \n        # CenterBox \\\n        #   AspectFrame \\\n        #       Frame \\\n        #           Overlay \\\n        #               RenderArea \n        #               Separator\n        #   Label\n\n        return out\n    end\nend\nMousetrap.get_top_level_widget(x::ShapePage) = x.center_box\n\nmain() do app::Application\n\n    window = Window(app)\n    set_title!(window, \"Mousetrap.jl\")\n\n    shapes = [\n        \"Point\" => Point(\n            Vector2f(0, 0)\n        ),\n        \"Points\" => Points([\n            Vector2f(-0.5, 0.5), \n            Vector2f(0.5, 0.5), \n            Vector2f(0.0, -0.5)\n        ]),\n        \"Line\" => Line(\n            Vector2f(-0.5, +0.5), \n            Vector2f(+0.5, -0.5)\n        ),\n        \"Lines\" =>Lines([\n            Vector2f(-0.5, 0.5) => Vector2f(0.5, -0.5),\n            Vector2f(-0.5, -0.5) => Vector2f(0.5, 0.5)\n        ]),\n        \"LineStrip\" =>  LineStrip([\n            Vector2f(-0.5, +0.5),\n            Vector2f(+0.5, +0.5),\n            Vector2f(+0.5, -0.5),\n            Vector2f(-0.5, -0.5)\n        ]),\n        \"Wireframe\" => Wireframe([\n            Vector2f(-0.5, +0.5),\n            Vector2f(+0.5, +0.5),\n            Vector2f(+0.5, -0.5),\n            Vector2f(-0.5, -0.5)\n        ]),\n        \"Triangle\" => Triangle(\n            Vector2f(-0.5, 0.5),\n            Vector2f(+0.5, 0.5),\n            Vector2f(0.0, -0.5)\n        ),\n        \"Rectangle\" => Rectangle(\n            Vector2f(-0.5, 0.5),\n            Vector2f(1, 1)\n        ),\n        \"Circle\" => Circle(\n            Vector2f(0, 0),\n            0.5, \n            32\n        ),\n        \"Ellipse\" => Ellipse(\n            Vector2f(0, 0),\n            0.6, \n            0.4, \n            32\n        ),\n        \"Polygon\" => Polygon([\n            Vector2f(0.0, 0.75),\n            Vector2f(0.75, 0.25),\n            Vector2f(0.5, -0.75),\n            Vector2f(-0.5, -0.5),\n            Vector2f(-0.75, 0.0)\n        ]),\n        \"RectangularFrame\" => RectangularFrame(\n            Vector2f(-0.5, 0.5),\n            Vector2f(1, 1),\n            0.15,\n            0.15,\n        ),\n        \"CircularRing\" =>  CircularRing(\n            Vector2f(0, 0),\n            0.5, \n            0.15,\n            32\n        ),\n        \"EllipticalRing\" =>  EllipticalRing(\n            Vector2f(0, 0),\n            0.6,\n            0.4,\n            0.15, \n            0.15,\n            32\n        )\n    ]\n\n    # add button that allow switching between light and dark theme\n    button = Button()\n    connect_signal_clicked!(button, app) do self::Button, app::Application\n\n        current = get_current_theme(app)\n\n        # swap light to dark, dark to light\n        if current == THEME_DEFAULT_DARK\n            next = THEME_DEFAULT_LIGHT\n        elseif current == THEME_DEFAULT_LIGHT\n            next = THEME_DEFAULT_DARK\n        elseif current == THEME_HIGH_CONTRAST_DARK\n            next = THEME_HIGH_CONTRAST_LIGHT\n        elseif current == THEME_HIGH_CONTRAST_LIGHT\n            next = THEME_HIGH_CONTRAST_DARK\n        end\n\n        set_current_theme!(app, next)\n\n        # also swap colors to contrast well\n        for pair in shapes\n            shape = pair[2]\n            if next == THEME_DEFAULT_LIGHT || next == THEME_HIGH_CONTRAST_LIGHT\n                set_color!(shape, RGBA(0, 0, 0, 1))\n            else\n                set_color!(shape, RGBA(1, 1, 1, 1))\n            end\n        end\n    end\n    set_tooltip_text!(button, \"Click to Swap UI Theme\")\n    set_has_frame!(button, false)\n    push_front!(get_header_bar(window), button)\n\n    # add outline shapes for shapes that have a volume\n    for name in [\"Triangle\", \"Rectangle\", \"Circle\", \"Ellipse\", \"Polygon\", \"RectangularFrame\", \"CircularRing\", \"EllipticalRing\"]\n        \n        # get shape of pairs whos first element is equal to `name`\n        shape = (shapes[findfirst(x -> x[1] == name, shapes)]).second\n\n        # add the new outline to shapes\n        push!(shapes, (name * \" (Outline)\" => Outline(shape)))\n    end\n\n    # create the stack and fill it with pages, in order\n    stack = Stack()\n    for (name, shape) in shapes\n        add_child!(stack, ShapePage(name, shape), name)\n    end\n\n    # create side bar to be able to pick the stack page\n    viewport = Viewport(StackSidebar(stack))\n    set_propagate_natural_width!(viewport, true)\n    set_horizontal_scrollbar_policy!(viewport, SCROLLBAR_VISIBILITY_POLICY_NEVER) \n        # forces witdh of viewport to be equal to width of stack side bar at all time\n\n    # make it so stack page expands instead of side-bar\n    set_expand_horizontally!(stack, true)\n    set_expand_horizontally!(viewport, false)\n\n    # add a revealer that can hide the side bar for screenshots\n    revealer = Revealer(viewport)\n    set_transition_type!(revealer, REVEALER_TRANSITION_TYPE_SLIDE_RIGHT)\n\n    # Allow hiding / showing the sidebar by pressing `Control + H`\n    # To do this, we create an action that triggers the revealer, then add a shortcut\n    revealer_action = Action(\"trigger_revealer\", app)\n    set_function!(revealer_action, revealer) do self::Action, revealer::Revealer\n        set_is_revealed!(revealer, !get_is_revealed(revealer))\n    end\n    add_shortcut!(revealer_action, \"<Control>h\");\n    set_listens_for_shortcut_action!(window, revealer_action)\n    set_tooltip_text!(revealer, \"press <tt>Control + H</tt> to hide this element.\")\n\n    key_controller = KeyEventController()\n    connect_signal_key_released!(key_controller, stack) do _::KeyEventController, code::KeyCode, modifiers::ModifierState, stack::Stack\n        \n        stack_model = get_selection_model(stack)\n        current = get_selection(stack_model)[1]\n\n        if code == KEY_Left || code == KEY_Up && (current > 1)\n            select!(stack_model, current - 1)\n        elseif code == KEY_Right || code == KEY_Down && (current < get_n_items(stack_model))\n            select!(stack_model, current + 1)\n        end\n    end\n    add_controller!(button, key_controller)\n\n    # main layout\n    box = hbox(stack, revealer)\n    set_child!(window, box)\n    present!(window)\nend\n\n@static if false\n\nmain() do app::Application\n\n    window = Window(app)\n    set_title!(window, \"Mousetrap.jl\")\n\n    # create render areas with different MSAA modes\n    left_area = RenderArea(ANTI_ALIASING_QUALITY_OFF)\n    right_area = RenderArea(ANTI_ALIASING_QUALITY_BEST)\n\n    # paned that will hold both areas\n    paned = Paned(ORIENTATION_HORIZONTAL)\n\n    # create singular shape, which will be shared between areas\n    shape = Rectangle(Vector2f(-0.5, 0.5), Vector2f(1, 1))\n    add_render_task!(left_area, RenderTask(shape))\n    add_render_task!(right_area, RenderTask(shape))\n\n    # rotate shape 1° per frame\n    set_tick_callback!(paned) do clock::FrameClock\n\n        # rotate shape \n        rotate!(shape, degrees(1), get_centroid(shape))\n\n        # force redraw for both areas\n        queue_render(left_area) \n        queue_render(right_area)\n\n        # continue callback indefinitely\n        return TICK_CALLBACK_RESULT_CONTINUE\n    end\n\n    # setup window layout for viewing\n    for area in [left_area, right_area]\n        set_size_request!(area, Vector2f(150, 150))\n    end\n\n    # caption labels\n    left_label = Label(\"<tt>OFF</tt>\")\n    right_label = Label(\"<tt>BEST</tt>\")\n\n    for label in [left_label, right_label]\n        set_margin!(label, 10)\n    end\n\n    # format paned\n    set_start_child_shrinkable!(paned, false)\n    set_end_child_shrinkable!(paned, false)\n    set_start_child!(paned, vbox(AspectFrame(1.0, left_area), left_label))\n    set_end_child!(paned, vbox(AspectFrame(1.0, right_area), right_label))\n\n    # present\n    set_child!(window, paned)\n    present!(window)\nend\n\nend # @static if\n\n    =#\n\nend"
  },
  {
    "path": "test/makie_test.jl",
    "content": "\"\"\"\nMinimum working example showing how to display a GLMakie plot using Mousetrap `GLArea`\n\"\"\"\nmodule MousetrapMakie\n\n    export GLMakieArea, create_glmakie_screen\n\n    using Mousetrap\n    using ModernGL, GLMakie, Colors, GeometryBasics, ShaderAbstractions\n    using GLMakie: empty_postprocessor, fxaa_postprocessor, OIT_postprocessor, to_screen_postprocessor\n    using GLMakie.GLAbstraction\n    using GLMakie.Makie\n\n    \"\"\"\n    ## GLMakieArea <: Widget\n    `GLArea` wrapper that automatically connects all necessary callbacks in order for it to be used as a GLMakie render target. \n\n    Use `create_glmakie_screen` to initialize a screen you can render to using Makie from this widget. Note that `create_glmakie_screen` needs to be \n    called **after** `GLMakieArea` has been realized, as only then will the internal OpenGL context be available. See the example below.\n\n    ## Constructors\n    `GLMakieArea()`\n\n    ## Signals\n    (no unique signals)\n\n    ## Fields\n    (no public fields)\n\n    ## Example\n    ```\n    using Mousetrap, MousetrapMakie\n    main() do app::Application\n        window = Window(app)\n        canvas = GLMakieArea()\n        set_size_request!(canvas, Vector2f(200, 200))\n        set_child!(window, canvas)\n    \n        # use optional ref to delay screen allocation after `realize`\n        screen = Ref{Union{Nothing, GLMakie.Screen{GLMakieArea}}}(nothing)\n        connect_signal_realize!(canvas) do self\n            screen[] = create_glmakie_screen(canvas)\n            display(screen[], scatter(1:4))\n            return nothing\n        end\n        present!(window)\n    end\n    ```\n    \"\"\"\n    mutable struct GLMakieArea <: Widget\n\n        glarea::GLArea              # wrapped native widget\n        framebuffer_id::Ref{Int}    # set by render callback, used in MousetrapMakie.create_glmakie_screen\n        framebuffer_size::Vector2i  # set by resize callback, used in GLMakie.framebuffer_size\n\n        function GLMakieArea()\n            glarea = GLArea()\n            set_auto_render!(glarea, false) # should `render` be emitted everytime the widget is drawn\n            connect_signal_render!(on_makie_area_render, glarea)\n            connect_signal_resize!(on_makie_area_resize, glarea)\n            return new(glarea, Ref{Int}(0), Vector2i(0, 0))\n        end\n    end\n    Mousetrap.get_top_level_widget(x::GLMakieArea) = x.glarea\n\n    # maps hash(GLMakieArea) to GLMakie.Screen\n    const screens = Dict{UInt64, GLMakie.Screen}()\n\n    # maps hash(GLMakieArea) to Scene, used in `on_makie_area_resize`\n    const scenes = Dict{UInt64, GLMakie.Scene}()\n\n    # render callback: if screen is open, render frame to `GLMakieArea`s OpenGL context\n    function on_makie_area_render(self, context)\n        key = Base.hash(self)\n        if haskey(screens, key)\n            screen = screens[key]\n            if !isopen(screen) return false end\n            screen.render_tick[] = nothing\n            glarea = screen.glscreen\n            glarea.framebuffer_id[] = glGetIntegerv(GL_FRAMEBUFFER_BINDING)\n            GLMakie.render_frame(screen) \n        end\n        return true\n    end\n\n    # resize callback: update framebuffer size, necessary for `GLMakie.framebuffer_size`\n    function on_makie_area_resize(self, w, h)\n        key = Base.hash(self)\n        if haskey(screens, key)\n            screen = screens[key]\n            glarea = screen.glscreen\n            glarea.framebuffer_size.x = w\n            glarea.framebuffer_size.y = h\n            queue_render(glarea.glarea)\n        end\n\n        if haskey(scenes, key)\n            scene = scenes[key]\n            scene.events.window_area[] = Recti(0, 0, glarea.framebuffer_size.x, glarea.framebuffer_size.y)\n            scene.events.window_dpi[] = Mousetrap.calculate_monitor_dpi(glarea)\n        end\n        return nothing\n    end\n\n    # resolution of `GLMakieArea` OpenGL framebuffer\n    GLMakie.framebuffer_size(self::GLMakieArea) = (self.framebuffer_size.x, self.framebuffer_size.y)\n\n    # forward retina scale factor from GTK4 back-end\n    GLMakie.retina_scaling_factor(w::GLMakieArea) = Mousetrap.get_scale_factor(w)\n\n    # resolution of `GLMakieArea` widget itself`\n    function GLMakie.window_size(w::GLMakieArea)\n        size = get_natural_size(w)\n        size.x = size.x * GLMakie.retina_scaling_factor(w)\n        size.y = size.y * GLMakie.retina_scaling_factor(w)\n        return (size.x, size.y)\n    end\n\n    # calculate screen size and dpi\n    function Makie.window_area(scene::Scene, screen::GLMakie.Screen{GLMakieArea})\n        glarea = screen.glscreen\n        scenes[hash(glarea)] = scene\n    end\n\n    # resize request by makie will be ignored\n    function GLMakie.resize_native!(native::GLMakieArea, resolution...)\n        # noop\n    end\n\n    # bind `GLMakieArea` OpenGL context\n    ShaderAbstractions.native_switch_context!(a::GLMakieArea) = make_current(a.glarea)\n\n    # check if `GLMakieArea` OpenGL context is still valid, it is while `GLMakieArea` widget stays realized\n    ShaderAbstractions.native_context_alive(x::GLMakieArea) = get_is_realized(x)\n\n    # destruction callback ignored, lifetime is managed by mousetrap instead\n    function GLMakie.destroy!(w::GLMakieArea)\n        # noop\n    end\n\n    # check if canvas is still realized\n    GLMakie.was_destroyed(window::GLMakieArea) = !get_is_realized(window)\n\n    # check if canvas should signal it is open\n    Base.isopen(w::GLMakieArea) = !GLMakie.was_destroyed(w)\n\n    # react to makie screen visibility request\n    GLMakie.set_screen_visibility!(screen::GLMakieArea, bool) = bool ? show(screen.glarea) : hide!(screen.glarea)\n\n    # apply glmakie config\n    function GLMakie.apply_config!(screen::GLMakie.Screen{GLMakieArea}, config::GLMakie.ScreenConfig; start_renderloop=true) \n        @warn \"In MousetrapMakie: GLMakie.apply_config!: This feature is not yet implemented, ignoring config\"\n        # cf https://github.com/JuliaGtk/Gtk4Makie.jl/blob/main/src/screen.jl#L111\n        return screen\n    end\n\n    # screenshot framebuffer\n    function Makie.colorbuffer(screen::GLMakie.Screen{GLMakieArea}, format::Makie.ImageStorageFormat = Makie.JuliaNative)\n        @warn \"In MousetrapMakie: GLMakie.colorbuffer: This feature is not yet implemented, returning framecache\"\n        # cf https://github.com/JuliaGtk/Gtk4Makie.jl/blob/main/src/screen.jl#L147\n        return screen.framecache\n    end\n\n    # ignore makie event model, use the mousetrap event controllers instead\n    Makie.window_open(::Scene, ::GLMakieArea) = nothing\n    Makie.disconnect!(::GLMakieArea, f) = nothing\n    GLMakie.pollevents(::GLMakie.Screen{GLMakieArea}) = nothing\n    Makie.mouse_buttons(::Scene, ::GLMakieArea) = nothing\n    Makie.keyboard_buttons(::Scene, ::GLMakieArea) = nothing\n    Makie.dropped_files(::Scene, ::GLMakieArea) = nothing\n    Makie.unicode_input(::Scene, ::GLMakieArea) = nothing\n    Makie.mouse_position(::Scene, ::GLMakie.Screen{GLMakieArea}) = nothing\n    Makie.scroll(::Scene, ::GLMakieArea) = nothing\n    Makie.hasfocus(::Scene, ::GLMakieArea) = nothing\n    Makie.entered_window(::Scene, ::GLMakieArea) = nothing\n\n    \"\"\"\n    ```\n    create_gl_makie_screen(::GLMakieArea; screen_config...) -> GLMakie.Screen{GLMakieArea}\n    ```\n    For a `GLMakieArea`, create a `GLMakie.Screen` that can be used to display makie graphics\n    \"\"\"\n    function create_glmakie_screen(area::GLMakieArea; screen_config...)\n\n        if !get_is_realized(area) \n            log_critical(\"MousetrapMakie\", \"In MousetrapMakie.create_glmakie_screen: GLMakieArea is not yet realized, it's internal OpenGL context cannot yet be accessed\")\n        end\n\n        config = Makie.merge_screen_config(GLMakie.ScreenConfig, screen_config)\n\n        set_is_visible!(area, config.visible)\n        set_expand!(area, true)\n\n        # quote from https://github.com/JuliaGtk/Gtk4Makie.jl/blob/main/src/screen.jl#L342\n        shader_cache = GLAbstraction.ShaderCache(area)\n        ShaderAbstractions.switch_context!(area)\n        fb = GLMakie.GLFramebuffer((1, 1)) # resized on GLMakieArea realization later\n\n        postprocessors = [\n            config.ssao ? ssao_postprocessor(fb, shader_cache) : empty_postprocessor(),\n            OIT_postprocessor(fb, shader_cache),\n            config.fxaa ? fxaa_postprocessor(fb, shader_cache) : empty_postprocessor(),\n            to_screen_postprocessor(fb, shader_cache, area.framebuffer_id)\n        ]\n\n        screen = GLMakie.Screen(\n            area, shader_cache, fb,\n            config, false,\n            nothing,\n            Dict{WeakRef, GLMakie.ScreenID}(),\n            GLMakie.ScreenArea[],\n            Tuple{GLMakie.ZIndex, GLMakie.ScreenID, GLMakie.RenderObject}[],\n            postprocessors,\n            Dict{UInt64, GLMakie.RenderObject}(),\n            Dict{UInt32, Makie.AbstractPlot}(),\n            false,\n        )\n        # end quote\n\n        hash = Base.hash(area.glarea)\n        screens[hash] = screen\n        \n        set_tick_callback!(area.glarea) do clock::FrameClock\n            if GLMakie.requires_update(screen)\n                queue_render(area.glarea)\n            end\n\n            if GLMakie.was_destroyed(area)\n                return TICK_CALLBACK_RESULT_DISCONTINUE\n            else\n                return TICK_CALLBACK_RESULT_CONTINUE\n            end\n        end\n        return screen\n    end\nend\n\n# test\nusing Mousetrap, .MousetrapMakie, GLMakie\nmain() do app::Application\n    window = Window(app)\n    set_title!(window, \"Mousetrap x Makie\")\n    canvas = GLMakieArea()\n    set_size_request!(canvas, Vector2f(200, 200))\n    set_child!(window, canvas)\n\n    # use optional ref to delay screen allocation after `realize`\n    screen = Ref{Union{Nothing, GLMakie.Screen{GLMakieArea}}}(nothing)\n    connect_signal_realize!(canvas) do self\n        screen[] = create_glmakie_screen(canvas)\n        display(screen[], scatter(rand(123)))\n        return nothing\n    end\n    present!(window)\nend"
  },
  {
    "path": "test/runtests.jl",
    "content": "#\n# Author: C. Cords (mail@clemens-cords.com)\n# https://github.com/clemapfel/mousetrap.jl\n#\n# Copyright © 2023, Licensed under lGPL3-0\n#\n\nusing Test\nusing Mousetrap\n\n### GLOBALS\n\napp_id = \"mousetrap.runtests.jl\"\napp = Ref{Union{Application, Nothing}}(nothing)\nwindow = Ref{Union{Window, Nothing}}(nothing)\nicon = Ref{Union{Icon, Nothing}}(nothing)\n\n### MAIN\n\nContainer = Stack\nfunction add_page!(container::Container, title::String, x::Widget)\n    add_child!(container, title, x)\nend\n\n### \n\nfunction test_action(::Container)\n    @testset \"Action\" begin\n        action_id = \"test.action\"\n        action = Action(action_id, Main.app[])\n        Base.show(devnull, action)\n\n        triggered = Ref{Bool}(false)\n        function on_activate(::Action, triggered::Ref{Bool}) \n            triggered[] = true\n            return nothing\n        end\n\n        set_function!(on_activate, action, triggered)\n        connect_signal_activated!(on_activate, action, triggered) \n\n        activate!(action)\n        @test triggered[] == true\n        @test get_id(action) == action_id\n        @test get_enabled(action) == true\n        set_enabled!(action, false)\n        @test get_enabled(action) == false\n\n        add_shortcut!(action, \"a\")\n        add_shortcut!(action, \"b\")\n\n        shortcuts = get_shortcuts(action)\n        @test \"a\" in shortcuts\n        @test \"b\" in shortcuts\n\n        clear_shortcuts!(action)\n        @test isempty(get_shortcuts(action))\n    end\nend\n\n### ANIMATION\n\nfunction test_animation(widget::Container)\n    @testset \"Animation\" begin\n        animation = Animation(widget, seconds(1))\n        Base.show(devnull, animation)\n\n        @test get_state(animation) == ANIMATION_STATE_IDLE\n        \n        @test get_duration(animation) == seconds(1)\n        set_duration!(animation, seconds(2))\n        @test get_duration(animation) = seconds(2)\n\n        @test get_value(animation) == 0\n        @test get_lower!(animation) == 0\n        @test get_upper!(animation) == 1\n\n        set_lower!(animation, -1)\n        set_upper!(animation, 2)\n\n        @test get_lower!(animation) == -1\n        @test get_upper!(animation) == 2\n\n        @test get_repeat_count(animation) == 1\n        set_repeat_count!(animation, 0)\n        @test get_repeat_count(animation) == 0\n\n        @test get_is_reversed(animation) == false\n        set_is_reversed!(animation, true)\n        @test get_is_reversed(animation) == true\n\n        set_timing_function!(animation, ANIMATION_TIMING_FUNCTION_ELASTIC_SIGMOID)\n        @test get_timing_function(animation) == ANIMATION_TIMING_FUNCTION_ELASTIC_SIGMOID\n\n        on_tick!(animation) do self::Animation, value::AbstractFloat end\n        on_done!(animation) do self::Animation end\n\n        play!(animation)\n        pause!(animation)\n        reset!(animation)\n    end\nend\n\n### ADJUSTMENT\n\nfunction test_adjustment(::Container)\n    @testset \"Adjustment\" begin\n        adjustment = Adjustment(0, -1, 2, 0.05)\n        Base.show(devnull, adjustment)\n\n        properties_changed_called = Ref{Bool}(false)\n        connect_signal_properties_changed!(adjustment, properties_changed_called) do ::Adjustment, properties_changed_called\n            properties_changed_called[] = true\n            return nothing\n        end\n\n        value_changed_called = Ref{Bool}(false)\n        connect_signal_value_changed!(adjustment, value_changed_called) do ::Adjustment, value_changed_called\n            value_changed_called[] = true\n            return nothing\n        end\n\n        @test get_lower(adjustment) == -1.0f0\n        set_lower!(adjustment, 0)\n        @test get_lower(adjustment) == 0.0f0\n\n        @test get_upper(adjustment) == 2.0f0\n        set_upper!(adjustment, 1)\n        @test get_upper(adjustment) == 1.0f0\n\n        @test get_step_increment(adjustment) == 0.05f0\n        set_step_increment!(adjustment, 0.01)\n        @test get_step_increment(adjustment) == 0.01f0\n        \n        @test get_value(adjustment) == 0.0f0\n        set_value!(adjustment, 0.5)\n        @test get_value(adjustment) == 0.5f0\n\n        @test properties_changed_called[] == true\n        @test value_changed_called[] == true\n    end\nend\n\nfunction test_application(::Container)\n    @testset \"Application\" begin\n        app = Main.app[]\n        Base.show(devnull, app)\n        @test get_id(app) == Main.app_id\n\n        action_id = \"application.test_action\"\n        action = Action(action_id, app) do ::Action end\n\n        add_action!(app, action)\n        @test has_action(app, action_id) == true\n        @test get_id(get_action(app, action_id)) == action_id\n        remove_action!(app, action_id)\n        @test has_action(app, action_id) == false\n        \n        @test get_is_holding(app) == false\n        hold!(app)\n        @test get_is_holding(app) == true\n        release!(app)\n        @test get_is_holding(app) == false\n\n        @test get_is_marked_as_busy(app) == false\n        mark_as_busy!(app)\n        @test get_is_marked_as_busy(app) == true\n        unmark_as_busy!(app)\n        @test get_is_marked_as_busy(app) == false\n\n        @test get_current_theme(app) isa Theme\n        set_current_theme!(app, THEME_DEFAULT_LIGHT)\n        @test get_current_theme(app) == THEME_DEFAULT_LIGHT\n    end\nend\n\nfunction test_alert_dialog(::Container)\n    @testset \"AlertDialog\" begin\n        message = \"message\"\n        detailed_message = \"detailed message\"\n        alert_dialog = AlertDialog(message, detailed_message)\n        Base.show(devnull, alert_dialog)\n        \n        button_label = \"Label\"\n        id = add_button!(alert_dialog, button_label)\n        @test id == 1\n        @test get_button_label(alert_dialog, 1) == button_label\n        new_label = \"new_label\"\n        set_button_label!(alert_dialog, 1, new_label)\n        @test get_button_label(alert_dialog, 1) == new_label\n\n        set_extra_widget!(alert_dialog, Separator())\n        remove_extra_widget!(alert_dialog)\n\n        @test get_n_buttons(alert_dialog) == 1\n\n        @test get_message(alert_dialog) == message\n        @test get_detailed_description(alert_dialog) == detailed_message\n\n        @test get_is_modal(alert_dialog) == true\n        set_is_modal!(alert_dialog, false)\n        @test get_is_modal(alert_dialog) == false\n\n        on_selection!(alert_dialog) do self::AlertDialog, id::Integer\n        end\n\n        present!(alert_dialog)\n        close!(alert_dialog)\n    end\nend\n\nfunction test_angle(::Container)\n    @testset \"Angle\" begin\n        angle = degrees(90)\n        Base.show(devnull, angle)\n        @test isapprox(as_degrees(radians(as_radians(degrees(90)))), 90.0)\n    end\nend\n\nfunction test_aspect_frame(::Container)\n    @testset \"AspectFrame\" begin\n\n        aspect_frame = AspectFrame(1.0)\n        Base.show(devnull, aspect_frame)\n        @test Mousetrap.is_native_widget(aspect_frame)\n\n        @test get_child_x_alignment(aspect_frame) == 0.5\n        @test get_child_y_alignment(aspect_frame) == 0.5\n\n        set_child_x_alignment!(aspect_frame, 1.0)\n        set_child_y_alignment!(aspect_frame, 1.0)\n\n        @test get_child_x_alignment(aspect_frame) == 1.0\n        @test get_child_y_alignment(aspect_frame) == 1.0\n\n        @test get_ratio(aspect_frame) == 1.0\n        set_ratio!(aspect_frame, 1.5)\n        @test get_ratio(aspect_frame) == 1.5\n    end\nend\n\nfunction test_button(::Container)\n    @testset \"Button\" begin\n            \n        button = Button()\n        Base.show(devnull, button)\n        @test Mousetrap.is_native_widget(button)\n        \n        set_child!(button, Label(\"Button\"))\n        \n        @test get_has_frame(button)\n        set_has_frame!(button, false)\n        @test !get_has_frame(button)\n\n        @test get_is_circular(button) == false\n        set_is_circular!(button, true)\n        @test get_is_circular(button) == true\n\n        clicked_called = Ref{Bool}(false)\n        connect_signal_clicked!(button) do ::Button\n            clicked_called[] = true\n            return nothing\n        end\n\n        activate!(button)\n        emit_signal_clicked(button)\n        @test clicked_called[] == true\n    end\nend\n\nfunction test_box(::Container)\n    @testset \"Box\" begin\n        box = Box(ORIENTATION_HORIZONTAL)\n        Base.show(devnull, box)\n        @test Mousetrap.is_native_widget(box)\n\n        @test get_homogeneous(box) == false\n        set_homogeneous!(box, true)\n        @test get_homogeneous(box) == true\n\n        @test get_orientation(box) == ORIENTATION_HORIZONTAL\n        set_orientation!(box, ORIENTATION_VERTICAL)\n        @test get_orientation(box) == ORIENTATION_VERTICAL\n\n        start = Separator()\n        push_front!(box, start)\n        push_back!(box, Separator())\n        insert_after!(box, Separator(), start)\n        remove!(box, start)\n\n        @test get_n_items(box) == 2\n\n        @test get_spacing(box) == 0\n        set_spacing!(box, 10)\n        @test get_spacing(box) == 10\n    end\nend\n\nfunction test_transform_bin(::Container)\n    @testset \"TransformBin\" begin\n        bin = TransformBin()\n        @test Mouestrap.is_native_widget(bin)\n        Base.show(devnull, bin)\n\n        set_child!(bin, Separator())\n\n        rotate!(bin, degrees(10))\n        translate!(bin, Vector2f(10, 10))\n        scale!(bin, 1.1, 0.9)\n        skew!(bin, 1.1, 0.9)\n        \n        remove_child!(bin)\n    end\nend\n\nfunction test_center_box(::Container)\n    @testset \"CenterBox\" begin\n        center_box = CenterBox(ORIENTATION_HORIZONTAL)\n        Base.show(devnull, center_box)\n        @test Mousetrap.is_native_widget(center_box)\n\n        @test get_orientation(center_box) == ORIENTATION_HORIZONTAL\n        set_orientation!(center_box, ORIENTATION_VERTICAL)\n        @test get_orientation(center_box) == ORIENTATION_VERTICAL\n\n        set_start_child!(center_box, Separator())\n        remove_start_child!(center_box)\n\n        set_center_child!(center_box, Separator())\n        remove_center_child!(center_box)\n\n        set_end_child!(center_box, Separator())\n        remove_end_child!(center_box)\n    end\nend\n\nfunction test_check_button(::Container)\n    @testset \"CheckButton\" begin\n        check_button = CheckButton()\n        Base.show(devnull, check_button)\n        @test Mousetrap.is_native_widget(check_button)\n\n        toggled_called = Ref{Bool}(false)\n        connect_signal_toggled!(check_button, toggled_called) do _, toggled_called\n            toggled_called[] = true\n            return nothing\n        end\n\n        set_is_active!(check_button, true)\n        @test get_is_active(check_button) == true\n\n        set_state!(check_button, CHECK_BUTTON_STATE_INACTIVE)\n        @test get_state(check_button) == CHECK_BUTTON_STATE_INACTIVE\n        @test get_is_active(check_button) == false\n\n        set_state!(check_button, CHECK_BUTTON_STATE_ACTIVE)\n        @test get_state(check_button) == CHECK_BUTTON_STATE_ACTIVE\n        @test get_is_active(check_button) == true\n\n        set_state!(check_button, CHECK_BUTTON_STATE_INCONSISTENT)\n        @test get_state(check_button) == CHECK_BUTTON_STATE_INCONSISTENT\n        @test get_is_active(check_button) == false\n\n        set_child!(check_button, Separator())\n        remove_child!(check_button)\n    end\nend\n\nfunction test_event_controller(this::Container)\n\n    area = this\n\n    function test_single_click_gesture(controller)\n        \n    end\n\n    function test_event_controller(controller)\n        @test get_propagation_phase(controller) != PROPAGATION_PHASE_NONE\n        set_propagation_phase!(controller, PROPAGATION_PHASE_NONE)\n        @test get_propagation_phase(controller) == PROPAGATION_PHASE_NONE\n\n        if controller isa SingleClickGesture\n            @test get_current_button(controller) isa ButtonID\n            set_only_listens_to_button!(controller, BUTTON_ID_NONE)\n            @test get_only_listens_to_button(controller) == BUTTON_ID_NONE\n\n            @test get_touch_only(controller) == false\n            set_touch_only!(controller, true)\n            @test get_touch_only(controller) == true\n        end\n    end\n\n    let controller = ClickEventController()\n        Base.show(devnull, controller)\n        @testset \"ClickEventController\" begin\n            \n            test_event_controller(controller)\n\n            connect_signal_click_pressed!(controller) do self::ClickEventController, n_presses::Integer, x::AbstractFloat, y::AbstractFloat\n            end\n            @test get_signal_click_pressed_blocked(controller) == false\n\n            connect_signal_click_released!(controller) do self::ClickEventController, n_presses::Integer, x::AbstractFloat, y::AbstractFloat\n            end\n            @test get_signal_click_released_blocked(controller) == false\n\n            connect_signal_click_stopped!(controller) do self::ClickEventController\n            end\n            @test get_signal_click_stopped_blocked(controller) == false\n        end\n        add_controller!(area, controller)\n    end\n\n    let controller = DragEventController()\n        Base.show(devnull, controller)\n        @testset \"DragEventController\" begin\n\n            test_event_controller(controller)\n\n            connect_signal_drag_begin!(controller) do self::DragEventController, start_x::AbstractFloat, start_y::AbstractFloat\n            end\n            @test get_signal_drag_begin_blocked(controller) == false\n\n            connect_signal_drag!(controller) do self::DragEventController, x_offset::AbstractFloat, y_offset::AbstractFloat\n            end\n            @test get_signal_drag_blocked(controller) == false\n\n            connect_signal_drag_end!(controller) do self::DragEventController, x_offset::AbstractFloat, y_offset::AbstractFloat\n            end\n            @test get_signal_drag_end_blocked(controller) == false\n        end \n        add_controller!(area, controller)\n    end\n\n    let controller = FocusEventController() \n        Base.show(devnull, controller)\n        @testset \"FocusEventController\" begin\n\n            test_event_controller(controller)\n\n            connect_signal_focus_gained!(controller) do self::FocusEventController\n            end\n            @test get_signal_focus_gained_blocked(controller) == false\n\n            connect_signal_focus_lost!(controller) do self::FocusEventController\n            end\n            @test get_signal_focus_lost_blocked(controller) == false\n        end\n        add_controller!(area, controller)\n    end\n\n    let controller = KeyEventController()\n        Base.show(devnull, controller)\n        @testset \"KeyEventController\" begin\n\n            test_event_controller(controller)\n\n            connect_signal_key_pressed!(controller) do self::KeyEventController, key::KeyCode, modifier::ModifierState\n            end\n            @test get_signal_key_pressed_blocked(controller) == false\n\n            connect_signal_key_released!(controller) do self::KeyEventController, key::KeyCode, modifier::ModifierState\n            end\n            @test get_signal_key_released_blocked(controller) == false\n\n            connect_signal_modifiers_changed!(controller) do self::KeyEventController, modifier::ModifierState\n            end\n            @test get_signal_modifiers_changed_blocked(controller) == false\n        end\n        add_controller!(area, controller)\n    end\n\n    let controller = LongPressEventController()\n        Base.show(devnull, controller)\n        @testset \"LongPressEventController\" begin\n\n            test_event_controller(controller)\n\n            connect_signal_pressed!(controller) do self::LongPressEventController, x::AbstractFloat, y::AbstractFloat\n            end\n            @test get_signal_pressed_blocked(controller) == false\n\n            connect_signal_press_cancelled!(controller) do self::LongPressEventController\n            end\n            @test get_signal_press_cancelled_blocked(controller) == false\n        end\n        add_controller!(area, controller)\n    end\n\n    let controller = MotionEventController()\n        Base.show(devnull, controller)\n        @testset \"MotionEventController\" begin\n\n            test_event_controller(controller)\n\n            connect_signal_motion!(controller) do self::MotionEventController, x::AbstractFloat, y::AbstractFloat\n            end\n            @test get_signal_motion_blocked(controller) == false\n\n            connect_signal_motion_enter!(controller) do self::MotionEventController, x::AbstractFloat, y::AbstractFloat\n            end\n            @test get_signal_motion_enter_blocked(controller) == false\n\n            connect_signal_motion_leave!(controller) do self::MotionEventController\n            end\n            @test get_signal_motion_leave_blocked(controller) == false\n        end\n        add_controller!(area, controller)\n    end\n\n    let controller = PanEventController(ORIENTATION_HORIZONTAL)\n        Base.show(devnull, controller)\n        @testset \"PanEventController\" begin\n            \n            test_event_controller(controller)\n\n            connect_signal_pan!(controller) do self::PanEventController, direction::PanDirection, offset::AbstractFloat\n            end\n            @test get_signal_pan_blocked(controller) == false\n\n            @test get_orientation(controller) == ORIENTATION_HORIZONTAL\n            set_orientation!(controller, ORIENTATION_VERTICAL)\n            @test get_orientation(controller) == ORIENTATION_VERTICAL\n        end\n        add_controller!(area, controller)\n    end\n\n    let controller = PinchZoomEventController()\n        Base.show(devnull, controller)\n        @testset \"PinchZoomController\" begin\n            \n            test_event_controller(controller)\n\n            connect_signal_scale_changed!(controller) do self::PinchZoomEventController, scale::AbstractFloat\n            end\n            @test get_signal_scale_changed_blocked(controller) == false                \n        end\n        add_controller!(area, controller)\n    end\n\n    let controller = RotateEventController()\n        Base.show(devnull, controller)\n        @testset \"RotateEventController\" begin\n            \n            test_event_controller(controller)\n\n            connect_signal_rotation_changed!(controller) do self::RotateEventController, angle_absolute::AbstractFloat, angle_delta::AbstractFloat\n            end\n            @test get_signal_rotation_changed_blocked(controller) == false\n        end\n        add_controller!(area, controller)\n\n    end\n\n    let controller = ScrollEventController(false)\n        Base.show(devnull, controller)\n        @testset \"ScrollEventController\" begin\n\n            test_event_controller(controller)\n            \n            @test get_kinetic_scrolling_enabled(controller) == false\n            set_kinetic_scrolling_enabled!(controller, true)\n            @test get_kinetic_scrolling_enabled(controller) == true\n\n            connect_signal_scroll_begin!(controller) do self::ScrollEventController\n            end\n            @test get_signal_scroll_begin_blocked(controller) == false\n\n            connect_signal_scroll!(controller) do self::ScrollEventController, x_delta::AbstractFloat, y_delta::AbstractFloat\n            end\n            @test get_signal_scroll_blocked(controller) == false\n\n            connect_signal_scroll_end!(controller) do self::ScrollEventController\n            end\n            @test get_signal_scroll_end_blocked(controller) == false\n\n            connect_signal_kinetic_scroll_decelerate!(controller) do self::ScrollEventController, x_velocity::AbstractFloat, y_velocity::AbstractFloat\n            end\n            @test get_signal_kinetic_scroll_decelerate_blocked(controller) == false\n        end\n        add_controller!(area, controller)\n    end\n\n    let controller = ShortcutEventController()\n        Base.show(devnull, controller)\n        @testset \"ShortcutEventController\" begin\n            \n            test_event_controller(controller)\n\n            @test get_scope(controller) == SHORTCUT_SCOPE_LOCAL\n            set_scope!(controller, SHORTCUT_SCOPE_GLOBAL)\n            @test get_scope(controller) == SHORTCUT_SCOPE_GLOBAL\n\n            action = Action(\"test.action\", Main.app[]) do self end\n            add_action!(controller, action)\n            remove_action!(controller, action)\n        end\n        add_controller!(area, controller)\n    end\n\n    let controller = StylusEventController()\n        Base.show(devnull, controller)\n        @testset \"StylusEventController\" begin\n            \n            test_event_controller(controller)\n\n            connect_signal_stylus_up!(controller) do self::StylusEventController, x::AbstractFloat, y::AbstractFloat\n            end\n            @test get_signal_stylus_up_blocked(controller) == false\n\n            connect_signal_stylus_down!(controller) do self::StylusEventController, x::AbstractFloat, y::AbstractFloat\n            end\n            @test get_signal_stylus_down_blocked(controller) == false\n\n            connect_signal_proximity!(controller) do self::StylusEventController, x::AbstractFloat, y::AbstractFloat\n            end\n            @test get_signal_proximity_blocked(controller) == false\n\n            connect_signal_motion!(controller) do self::StylusEventController, x::AbstractFloat, y::AbstractFloat\n            end\n            @test get_signal_motion_blocked(controller) == false\n\n            @test get_hardware_id(controller) isa Csize_t\n            @test get_tool_type(controller) isa ToolType\n            @test has_axis(controller, DEVICE_AXIS_Y) isa Bool \n        end\n        add_controller!(area, controller)\n    end\n\n    let controller = SwipeEventController()\n        Base.show(devnull, controller)\n        @testset \"SwipeEventController\" begin\n            \n            test_event_controller(controller)\n            \n            connect_signal_swipe!(controller) do self::SwipeEventController, x_velocity::AbstractFloat, y_velocity::AbstractFloat\n            end\n            @test get_signal_swipe_blocked(controller) == false\n        end\n        add_controller!(area, controller)\n    end\nend\n\nfunction test_clipboard(::Container)\n    @testset \"Clipboard\" begin\n        clipboard = get_clipboard(Main.window[])\n        Base.show(devnull, clipboard)\n\n        set_string!(clipboard, \"test\")\n        @test contains_string(clipboard) == true\n        @test get_is_local(clipboard) == true\n\n        get_string(clipboard) do self::Clipboard, value::String\n            @test true\n            return nothing\n        end\n\n        set_file!(clipboard, FileDescriptor(\".\"))\n        @test contains_file(clipboard) == true\n        @test get_is_local(clipboard) == true\n\n        get_string(clipboard) do self::Clipboard, value::String\n            @test true\n            return nothing\n        end\n        \n        set_image!(clipboard, Image(1, 1, RGBA(1, 0, 0, 1)))\n        @test contains_image(clipboard) == true\n        @test get_is_local(clipboard) == true\n\n        get_image(clipboard) do self::Clipboard, value::Image\n            @test true\n            return nothing\n        end\n    end\nend\n\nfunction test_clamp_frame(::Container)\n    @testset \"ClampFrame\" begin\n        frame = ClampFrame(150)\n        Base.show(devnull, frame)\n\n        set_child!(frame, Separator())\n        remove_child!(frame)\n\n        @test get_orientation(frame) == ORIENTATION_HORIZONTAL\n        set_orientation!(frame, ORIENTATION_VERTICAL)\n        @test get_orientation(frame) == ORIENTATION_VERTICAL\n\n        @test get_maximum_size(frame) == 150\n        set_maximum_size!(frame, 50)\n        @test get_maximum_size(frame) == 50\n    end\nend\n\nfunction test_time(::Container)\n    @testset \"Time\" begin\n        value = 1234\n        @test as_minutes(minutes(value)) == value\n        @test as_seconds(seconds(value)) == value\n        @test as_milliseconds(milliseconds(value)) == value\n        @test as_microseconds(microseconds(value)) == value\n        @test as_nanoseconds(nanoseconds(value)) == value\n\n        Base.show(devnull, milliseconds(1234))\n    end\n\n    @testset \"Clock\" begin\n        clock = Clock()\n        sleep(0.1)\n        @test as_seconds(elapsed(clock)) > 0.0\n        @test as_seconds(restart!(clock)) > 0.0\n\n        Base.show(devnull, clock)\n    end\nend\n\nfunction test_color_chooser(::Container)\n    @testset \"ColorChooser\" begin\n        color_chooser = ColorChooser()\n        Base.show(devnull, color_chooser)\n\n        on_accept!(color_chooser) do self::ColorChooser, color::RGBA\n        end\n\n        on_cancel!(color_chooser) do self::ColorChooser\n        end\n\n        @test get_color(color_chooser) isa RGBA\n        @test get_is_modal(color_chooser) == true\n        set_is_modal!(color_chooser, false)\n        @test get_is_modal(color_chooser) == false\n\n        @test get_title(color_chooser) == \"\"\n        set_title!(color_chooser, \"TEST\")\n        @test get_title(color_chooser) == \"TEST\"\n    end\nend\n\nfunction test_column_view(::Container)\n    @testset \"ColumnViewTest\" begin\n        column_view = ColumnView()\n        Base.show(devnull, column_view)\n        @test Mousetrap.is_native_widget(column_view)\n\n        push_back_column!(column_view, \"column 01\")\n        push_front_column!(column_view, \"column 03\")\n\n        column_name = \"column 02\"\n        column = insert_column_at!(column_view, 1, column_name)\n\n        push_back_row!(column_view, Label(\"\"), Label(\"\"), Label(\"\"))\n        push_front_row!(column_view, Label(\"\"), Label(\"\"), Label(\"\"))\n        insert_row_at!(column_view, 2, Label(\"\"), Label(\"\"), Label(\"\"))\n        @test get_title(column) == column_name\n\n        new_title = \"new title\"\n        set_title!(column, new_title)\n        @test get_title(column) == new_title\n\n        @test get_fixed_width(column) isa Float32\n        set_fixed_width!(column, 100)\n        @test get_fixed_width(column) == 100\n\n        model = MenuModel()\n        set_header_menu!(column, model)\n\n        @test get_is_visible(column) == true\n        set_is_visible!(column, false)\n        @test get_is_visible(column) == false\n\n        set_is_resizable!(column, true)\n        @test get_is_resizable(column) == true\n\n        @test has_column_with_title(column_view, new_title) == true\n        other_column = get_column_with_title(column_view, new_title)\n        @test get_title(other_column) == new_title\n\n        remove_column!(column_view, get_column_at(column_view, 1))\n    end\nend\n\nfunction test_drop_down(::Container)\n    @testset \"DropDown\" begin\n        drop_down = DropDown()\n        Base.show(devnull, drop_down)\n        @test Mousetrap.is_native_widget(drop_down)\n\n        label = \"Label\";\n        id_02 = push_back!(drop_down, label, label) do self::DropDown\n        end\n\n        @test get_item_at(drop_down, 1) == id_02\n\n        id_01 = push_front!(drop_down, label, label) do self::DropDown\n        end\n\n        id_03 = insert_at!(drop_down, 1, label) do self::DropDown\n        end\n\n        remove!(drop_down, id_03)\n\n        set_selected!(drop_down, id_01)\n        @test get_selected(drop_down) == id_01\n\n        @test get_always_show_arrow(drop_down) == true\n        set_always_show_arrow!(drop_down, false)\n        @test get_always_show_arrow(drop_down) == false\n    end\nend\n\nfunction test_entry(::Container)\n    @testset \"Entry\" begin\n        entry = Entry()\n        Base.show(devnull, entry)\n        @test Mousetrap.is_native_widget(entry)\n\n        activate_called = Ref{Bool}(false)\n        connect_signal_activate!(entry, activate_called) do entry::Entry, activate_called\n            activate_called[] = true\n            return nothing\n        end\n\n        text_changed_called = Ref{Bool}(false)\n        connect_signal_text_changed!(entry, text_changed_called) do entry::Entry, text_changed_called\n            text_changed_called[] = true\n            return nothing\n        end\n\n        @test get_has_frame(entry) == true\n        set_has_frame!(entry, false)\n        @test get_has_frame(entry) == false\n\n        @test get_max_width_chars(entry) == 0\n        set_max_width_chars!(entry, 64)\n        @test get_max_width_chars(entry) == 64\n\n        @test get_text(entry) == \"\"\n        set_text!(entry, \"text\")\n        @test get_text(entry) == \"text\"\n        \n        @test get_text_visible(entry) == true\n        set_text_visible!(entry, false)\n        @test get_text_visible(entry) == false\n\n        set_primary_icon!(entry, Main.icon[])\n        set_secondary_icon!(entry, Main.icon[])\n        remove_primary_icon!(entry)\n        remove_secondary_icon!(entry)\n\n        # TODO: activate! does not cause `activate` signal to be emitted, see gtk_widget_set_activate_signal\n        emit_signal_activate(entry)\n\n        @test activate_called[] == true\n        @test text_changed_called[] == true\n    end\nend\n\nfunction test_expander(::Container)\n    @testset \"Expander\" begin\n        expander = Expander()\n        Base.show(devnull, expander)\n        @test Mousetrap.is_native_widget(expander)\n\n        activate_called = Ref{Bool}(false)\n        connect_signal_activate!(expander, activate_called) do self::Expander, activate_called\n            activate_called[] = true\n            return nothing\n        end\n\n        activate!(expander)\n        @test activate_called[] == true\n\n        set_child!(expander, Separator())\n        set_label_widget!(expander, Separator())\n\n        set_is_expanded!(expander, true)\n        @test get_is_expanded(expander) == true\n\n        remove_child!(expander)\n        remove_label_widget!(expander)\n    end\nend\n\nfunction test_file_chooser(::Container)\n\n    filter = FileFilter(\"test\")\n    @testset \"FileFilter\" begin\n        Base.show(devnull, filter)\n        add_allow_all_supported_image_formats!(filter)\n        add_allowed_mime_type!(filter, \"text/plain\")\n        add_allowed_pattern!(filter, \"*.jl\")\n        add_allowed_suffix!(filter, \"jl\")\n\n        @test get_name(filter) == \"test\"\n    end\n\n    @testset \"FileChooser\" begin\n        for action in instances(FileChooserAction)\n            file_chooser = FileChooser(action)\n            Base.show(devnull, file_chooser)\n\n            add_filter!(file_chooser, filter)\n            set_initial_filter!(file_chooser, filter)\n            set_initial_file!(file_chooser, FileDescriptor(\".\"))\n            set_initial_folder!(file_chooser, FileDescriptor(\".\"))\n            set_initial_name!(file_chooser, \"name\");\n\n            set_accept_label!(file_chooser, \"accept\")\n            @test get_accept_label(file_chooser) == \"accept\"\n\n            @test get_is_modal(file_chooser) == true\n            set_is_modal!(file_chooser, false)\n            @test get_is_modal(file_chooser) == false\n\n            @test get_title(file_chooser) == \"\"\n            set_title!(file_chooser, \"TEST\")\n            @test get_title(file_chooser) == \"TEST\"\n\n            on_accept!(file_chooser) do x::FileChooser, files::Vector{FileDescriptor}\n            end\n\n            on_cancel!(file_chooser) do x::FileChooser\n            end\n\n            #present!(file_chooser)\n            cancel!(file_chooser)\n        end\n    end\nend\n\nfunction test_file_descriptor(::Container)\n    @testset \"FileDescriptor\" begin\n           \n        name = tempname()\n        path = name * \".txt\"\n        file = open(path, \"w+\")\n        write(file, \"test\\n\")\n        close(file)\n\n        descriptor = FileDescriptor(\".\")\n        Base.show(devnull, descriptor)\n\n        create_from_path!(descriptor, path)\n        @test exists(descriptor)\n\n        @test get_name(descriptor)[end-3:end] == \".txt\"\n        @test get_path(descriptor) == path\n        @test get_uri(descriptor) isa String\n\n        @test get_path_relative_to(descriptor, descriptor) == \"\"\n        @test exists(get_parent(descriptor))\n        \n        @test is_file(descriptor) == true\n        @test is_folder(descriptor) == false\n        @test is_symlink(descriptor) == false\n\n        # todo read_symlink\n\n        @test is_executable(descriptor) == false\n        @test get_content_type(descriptor) isa String # type is OS-specific\n        @test query_info(descriptor, \"standard::name\") == get_name(descriptor)\n\n        monitor = create_monitor(descriptor)\n        on_file_changed!(monitor) do ::FileMonitor, ::FileMonitorEvent, ::FileDescriptor, ::FileDescriptor\n        end\n        cancel!(monitor)\n        @test is_cancelled(monitor)\n\n        gtk_file = FileDescriptor(tempname() * \".txt\")\n        create_file_at!(gtk_file)\n        delete_at!(gtk_file)\n        create_directory_at!(gtk_file)\n        #move_to_trash!(gtk_file)\n        # silenced because /tmp files can't be moved to the trash\n\n        close(file)\n    end\nend\n\nfunction test_fixed(::Container)\n    @testset \"Fixed\" begin\n        fixed = Fixed()\n        Base.show(devnull, fixed)\n        @test Mousetrap.is_native_widget(fixed)\n\n        child = Label(\"(32, 32)\")\n\n        add_child!(fixed, child, Vector2f(32, 32))\n        set_child_position!(fixed, child, Vector2f(64, 64))\n       \n        remove_child!(fixed, child)\n    end\nend\n\nfunction test_flow_box(::Container)\n    @testset \"FlowBox\" begin\n        box = FlowBox(ORIENTATION_HORIZONTAL)\n        Base.show(devnull, box)\n        @test Mousetrap.is_native_widget(box)\n\n        @test get_homogeneous(box) == false\n        set_homogeneous!(box, true)\n        @test get_homogeneous(box) == true\n\n        @test get_orientation(box) == ORIENTATION_HORIZONTAL\n        set_orientation!(box, ORIENTATION_VERTICAL)\n        @test get_orientation(box) == ORIENTATION_VERTICAL\n\n        start = Separator()\n        push_front!(box, start)\n        push_back!(box, Separator())\n        insert_at!(box, 1, Separator())\n        remove!(box, start)\n\n        @test get_n_items(box) == 2\n\n        @test get_row_spacing(box) == 0\n        set_row_spacing!(box, 10)\n        @test get_row_spacing(box) == 10\n\n        @test get_column_spacing(box) == 0\n        set_column_spacing!(box, 10)\n        @test get_column_spacing(box) == 10\n    end\nend\n\nfunction test_frame(::Container)\n    @testset \"Frame\" begin\n\n        frame = Frame()\n        Base.show(devnull, frame)\n        @test Mousetrap.is_native_widget(frame)\n\n        set_child!(frame, Separator())\n        @test get_label_x_alignment(frame) == 0.0\n        set_label_x_alignment!(frame, 0.5)\n        @test get_label_x_alignment(frame) == 0.5\n\n        set_child!(frame, Separator())\n        set_label_widget!(frame, Label())\n        remove_child!(frame)\n        remove_label_widget!(frame)\n    end\nend\n\nfunction test_gl_transform(::Container)\n    @testset \"GLTransform\" begin\n\n        if !Mousetrap.MOUSETRAP_ENABLE_OPENGL_COMPONENT\n            return \n        end    \n\n        transform = GLTransform()\n        Base.show(devnull, transform)\n\n        rotate!(transform, degrees(90))\n        scale!(transform, 1.5, 1.5)\n        translate!(transform, Vector2f(0.5, 0.5))\n\n        new_transform = combine_with(transform, GLTransform())\n        for x in 1:3\n            for y in 1:3\n                @test transform[x, y] == new_transform[x, y]\n            end\n        end\n\n        reset!(new_transform)\n    end\nend\n\nfunction test_gl_area(::Container)\n    @testset \"GLArea\" begin\n        area = GLArea()\n        Base.show(devnull, area)\n        connect_signal_render!(area) do self::GLArea, context::Ptr{Cvoid} \n            return true\n        end\n        connect_signal_resize!(area) do self::GLArea, w, h end\n\n        #make_current(area) # would print gtk warning because area is not yet realized\n        queue_render(area)\n        @test get_auto_render(area) isa Bool\n        set_auto_render!(area, false)\n        @test get_auto_render(area) == false\n    end\nend\n\nfunction test_grid(::Container)\n    @testset \"Grid\" begin\n        grid = Grid()\n        Base.show(devnull, grid)\n        @test Mousetrap.is_native_widget(grid)\n\n        @test get_column_spacing(grid) == 0.0\n        set_column_spacing!(grid, 2.0)\n        @test get_column_spacing(grid) == 2.0\n\n        @test get_row_spacing(grid) == 0.0\n        set_row_spacing!(grid, 2.0)\n        @test get_row_spacing(grid) == 2.0\n\n        @test get_orientation(grid) == ORIENTATION_HORIZONTAL\n        set_orientation!(grid, ORIENTATION_VERTICAL)\n        @test get_orientation(grid) == ORIENTATION_VERTICAL\n\n        @test get_rows_homogeneous(grid) == false\n        set_rows_homogeneous!(grid, true)\n        @test get_rows_homogeneous(grid) == true\n\n        @test get_columns_homogeneous(grid) == false\n        set_columns_homogeneous!(grid, true)\n        @test get_columns_homogeneous(grid) == true\n\n        widget_01 = Separator()\n        widget_02 = Separator()\n\n        insert_at!(grid, widget_01, 1, 2, 3, 4)\n        insert_next_to!(grid, widget_02, widget_01, RELATIVE_POSITION_RIGHT_OF, 3, 4)\n\n        @test get_position(grid, widget_01) == Vector2i(1, 2)\n        @test get_size(grid, widget_01) == Vector2i(3, 4)\n        @test get_position(grid, widget_02) == Vector2i(4, 2)\n        @test get_size(grid, widget_02) == Vector2i(3, 4)\n\n        insert_row_at!(grid, 1)\n        insert_column_at!(grid, 1)\n        remove_row_at!(grid, 1)\n        remove_column_at!(grid, 1)\n    end\nend\n\n### GRID_VIEW\n\nfunction test_grid_view(::Container)\n    @testset \"GridView\" begin\n        grid_view = GridView(ORIENTATION_HORIZONTAL,SELECTION_MODE_MULTIPLE)\n        Base.show(devnull, grid_view)\n        @test Mousetrap.is_native_widget(grid_view)\n\n        @test get_orientation(grid_view) == ORIENTATION_HORIZONTAL\n        set_orientation!(grid_view, ORIENTATION_VERTICAL)\n        @test get_orientation(grid_view) == ORIENTATION_VERTICAL\n\n        @test get_max_n_columns(grid_view) > 0\n        set_max_n_columns!(grid_view, 3)\n        @test get_max_n_columns(grid_view) == 3\n        \n        @test get_min_n_columns(grid_view) > 0\n        set_min_n_columns!(grid_view, 3)\n        @test get_min_n_columns(grid_view) == 3\n\n        set_enable_rubberband_selection!(grid_view, true)\n        @test get_enable_rubberband_selection(grid_view) == true\n\n        @test get_single_click_activate(grid_view) == false\n        set_single_click_activate!(grid_view, true)\n        @test get_single_click_activate(grid_view) == true\n\n        push_front!(grid_view, Separator())\n        push_back!(grid_view, Separator())\n\n        child = Separator()\n        insert_at!(grid_view, 1, child)\n        @test find(grid_view, child) == 1 \n        \n        remove!(grid_view, 1)\n        @test get_n_items(grid_view) == 2\n        @test get_selection_model(grid_view) isa SelectionModel\n        \n        activate_item_called = Ref{Bool}(false)\n        connect_signal_activate_item!(grid_view, activate_item_called) do self::GridView, index, activate_item_called\n            activate_item_called[] = true\n            return nothing\n        end\n        \n        #@test activate_called[] == true\n    end\nend\n\n### COLORS\n\nfunction test_colors(::Container)\n    @testset \"Colors\" begin\n        color_rgba = RGBA(1, 0, 1, 1)\n        color_hsva = HSVA(1, 0, 1, 1)\n\n        Base.show(devnull, color_rgba)\n        Base.show(devnull, color_hsva)\n        \n        @test color_rgba == hsva_to_rgba(rgba_to_hsva(color_rgba))\n        @test color_hsva == rgba_to_hsva(hsva_to_rgba(color_hsva))\n    end\nend\n\n### HEADER_BAR\n\nfunction test_header_bar(::Container)\n    @testset \"HeaderBar\" begin\n\n        layout = \"close:minimize,maximize\"\n        header_bar = HeaderBar(layout)\n        @test Mousetrap.is_native_widget(header_bar)\n\n        Base.show(devnull, header_bar)\n\n        @test get_layout(header_bar) == layout\n        set_layout!(header_bar, \"\")\n        @test get_layout(header_bar) == \"\"\n        \n        @test get_show_title_buttons(header_bar) == true\n        set_show_title_buttons!(header_bar, false)\n        @test get_show_title_buttons(header_bar) == false\n\n        widget = Separator()\n        push_front!(header_bar, widget)\n        push_back!(header_bar, Separator())\n        remove!(header_bar, widget)\n\n        set_title_widget!(header_bar, Label(\"title\"))\n        remove_title_widget!(header_bar)\n    end\nend\n\nfunction test_icon(::Container)\n\n    theme = IconTheme(Main.window[])\n    names = get_icon_names(theme)\n        # names will contain linux default theme, but is empty on windows\n\n    @testset \"Icon\" begin\n        if !isempty(names)\n            icon_name = names[1]\n            icon = Icon(theme, icon_name, 64)\n            @test get_size(icon).x == 64 && get_size(icon).y == 64\n        else\n            icon = Icon()\n        end\n\n        Base.show(devnull, icon)\n    end \n\n    @testset \"IconTheme\" begin\n\n        Base.show(devnull, theme)\n    \n        add_resource_path!(theme, \".\")\n        set_resource_path!(theme, \".\")\n        \n        # TODO: validate resource path\n    end\nend\n\n### IMAGE\n\nfunction test_image(::Container)\n    @testset \"Image\" begin\n        image = Image(1, 1, RGBA(1, 0, 1, 1))\n        Base.show(devnull, image)\n\n        @test get_size(image) == Vector2i(1, 1)\n        @test get_n_pixels(image) == 1\n\n        @test get_pixel(image, 1, 1) == RGBA(1, 0, 1, 1)\n        set_pixel!(image, 1, 1, RGBA(0, 0, 1, 1))\n        @test get_pixel(image, 1, 1) == RGBA(0, 0, 1, 1)\n\n        flipped = as_flipped(image, true, true)\n        @test get_size(flipped) == Vector2i(1, 1)\n        @test get_size(as_scaled(image, 2, 2, INTERPOLATION_TYPE_HYPERBOLIC)) == Vector2i(2, 2)\n        @test get_size(as_cropped(image, 0, 0, 2, 2)) == Vector2i(2, 2)\n\n        save_to_file(image, tempname() * \".png\")\n    end\nend\n\n### IMAGE_DISPLAY\n\nfunction test_image_display(::Container)\n    @testset \"ImageDisplay\" begin\n        image_display = ImageDisplay()\n        Base.show(devnull, image_display)\n        @test Mousetrap.is_native_widget(image_display)\n\n        image = Image(1, 1, RGBA(1, 0, 1, 1))\n        create_from_image!(image_display, image)\n        @test get_size(image_display) == Vector2i(1, 1)\n        clear!(image_display)\n\n        create_from_icon!(image_display, Main.icon[])\n        @test get_size(image_display) == get_size(Main.icon[])\n        clear!(image_display)\n        @test get_size(image_display) == Vector2i(0, 0)\n    end\nend\n\n### KEY_FILE\n\nfunction test_key_file(::Container)\n    @testset \"KeyFile\" begin\n        file = KeyFile()\n        Base.show(devnull, file)\n\n        group_name = \"group_01\"\n        bool_key = \"bool\"\n        float_key = \"float\"\n        integer_key = \"integer\"\n        string_key = \"string\"\n\n        bool_list_key = \"bool_list\"\n        float_list_key = \"float_list\"\n        integer_list_key = \"integer_list\"\n        color_key = \"rgba\"\n        string_list_key = \"string_list\"\n\n        group_comment = \" group comment\"\n        key_comment = \" key comment\"\n\n        create_from_string!(file, \"\"\"\n        #$group_comment\n        [$group_name]\n        #$key_comment\n        $bool_key = true\n        $float_key = 1234.0\n        $integer_key = 1234\n        $string_key = abcd\n\n        $bool_list_key = true;false\n        $float_list_key = 1234.0;5678.0\n        $integer_list_key = 1234;5678\n        $string_list_key = abcd;efgh\n        $color_key = 1.0;0.0;1.0;1.0\n        \"\"\")\n\n        @test get_groups(file) == [group_name]\n        @test has_group(file, group_name) == true\n        @test isempty(get_keys(file, group_name)) == false\n        @test has_key(file, group_name, color_key) == true\n\n        @test get_comment_above(file, group_name) == group_comment\n        @test get_comment_above(file, group_name, bool_key) == key_comment\n\n        @test get_value(file, group_name, bool_key, Bool) == true\n        @test get_value(file, group_name, float_key, Float32) == 1234.0\n        @test get_value(file, group_name, integer_key, Int64) == 1234\n        @test get_value(file, group_name, string_key, String) == \"abcd\"\n        @test get_value(file, group_name, color_key, RGBA) == RGBA(1, 0, 1, 1)\n        @test get_value(file, group_name, color_key, HSVA) == HSVA(1, 0, 1, 1)\n        @test get_value(file, group_name, bool_list_key, Vector{Bool}) == [true, false]\n        @test get_value(file, group_name, float_list_key, Vector{Float32}) == Float32[1234.0, 5678.0]\n        @test get_value(file, group_name, integer_list_key, Vector{Int64}) == Int64[1234, 5678]\n        @test get_value(file, group_name, string_list_key, Vector{String}) == [\"abcd\", \"efgh\"]\n\n        set_value!(file, group_name, bool_key, false)\n        @test get_value(file, group_name, bool_key, Bool) == false\n\n        set_value!(file, group_name, float_key, Float32(999))\n        @test get_value(file, group_name, float_key, Float32) == 999\n\n        set_value!(file, group_name, integer_key, Int64(999))\n        @test get_value(file, group_name, integer_key, Int64) == 999\n\n        set_value!(file, group_name, string_key, String(\"none\"))\n        @test get_value(file, group_name, string_key, String) == \"none\"\n\n        set_value!(file, group_name, color_key, RGBA(0, 0, 0, 1))\n        @test get_value(file, group_name, color_key, RGBA) == RGBA(0, 0, 0, 1)\n\n        set_value!(file, group_name, bool_list_key, [false, true])\n        @test get_value(file, group_name, bool_list_key, Vector{Bool}) == [false, true]\n\n        set_value!(file, group_name, float_list_key, [Float32(1), Float32(2)])\n        @test get_value(file, group_name, float_list_key, Vector{Float32}) == Float32[1.0, 2.0]\n\n        set_value!(file, group_name, integer_list_key, [Int64(1), Int64(2)])\n        @test get_value(file, group_name, integer_list_key, Vector{Int64}) == Int64[1, 2]\n\n        set_value!(file, group_name, string_list_key, [\"none\", \"nothing\"])\n        @test get_value(file, group_name, string_list_key, Vector{String}) == [\"none\", \"nothing\"]\n    end\nend\n\n### LABEL\n\nfunction test_label(::Container)\n    @testset \"Label\" begin\n        label_string = \"label\"\n        label = Label(label_string)\n        Base.show(devnull, label)\n        @test Mousetrap.is_native_widget(label)\n\n        @test get_ellipsize_mode(label) == ELLIPSIZE_MODE_NONE\n        set_ellipsize_mode!(label, ELLIPSIZE_MODE_MIDDLE)\n        @test get_ellipsize_mode(label) == ELLIPSIZE_MODE_MIDDLE\n\n        @test get_justify_mode(label) == JUSTIFY_MODE_LEFT\n        set_justify_mode!(label, JUSTIFY_MODE_CENTER)\n        @test get_justify_mode(label) == JUSTIFY_MODE_CENTER\n\n        @test get_is_selectable(label) == false\n        set_is_selectable!(label, true)\n        @test get_is_selectable(label) == true\n\n        @test get_max_width_chars(label) == -1\n        set_max_width_chars!(label, 1234)\n        @test get_max_width_chars(label) == 1234\n\n        @test get_text(label) == label_string\n        set_text!(label, \"new\")\n        @test get_text(label) == \"new\"\n\n        @test get_use_markup(label) == true\n        set_use_markup!(label, false)\n        @test get_use_markup(label) == false\n\n        @test get_wrap_mode(label) == LABEL_WRAP_MODE_NONE\n        set_wrap_mode!(label, LABEL_WRAP_MODE_WORD_OR_CHAR)\n        @test get_wrap_mode(label) == LABEL_WRAP_MODE_WORD_OR_CHAR\n\n        @test get_x_alignment(label) == 0.5\n        set_x_alignment!(label, 0.0)\n        @test get_x_alignment(label) == 0.0\n\n        @test get_y_alignment(label) == 0.5\n        set_y_alignment!(label, 0.0)\n        @test get_y_alignment(label) == 0.0\n    end\nend\n\n### LEVEL_BAR\n\nfunction test_level_bar(::Container)\n    @testset \"LevelBar\" begin    \n        level_bar = LevelBar(0, 1)\n        Base.show(devnull, level_bar)\n        @test Mousetrap.is_native_widget(level_bar)\n\n        @test get_max_value(level_bar) == 1\n        set_max_value!(level_bar, 2)\n        @test get_max_value(level_bar) == 2\n\n        @test get_min_value(level_bar) == 0\n        set_min_value!(level_bar, 1)\n        @test get_min_value(level_bar) == 1\n\n        @test get_mode(level_bar) == LEVEL_BAR_MODE_CONTINUOUS\n        set_mode!(level_bar, LEVEL_BAR_MODE_DISCRETE)\n        @test get_mode(level_bar) == LEVEL_BAR_MODE_DISCRETE\n\n        @test get_orientation(level_bar) == ORIENTATION_HORIZONTAL\n        set_orientation!(level_bar, ORIENTATION_VERTICAL)\n        @test get_orientation(level_bar) == ORIENTATION_VERTICAL\n        \n        set_value!(level_bar, 1)\n        @test get_value(level_bar) == 1\n\n        marker_label = \"marker\"\n        add_marker!(level_bar, marker_label, 1)\n        remove_marker!(level_bar, marker_label)\n    end\nend\n\n### LIST_VIEW\n\nfunction test_list_view(::Container)\n    @testset \"ListView\" begin\n        list_view = ListView(ORIENTATION_HORIZONTAL, SELECTION_MODE_MULTIPLE)\n        Base.show(devnull, list_view)\n        @test Mousetrap.is_native_widget(list_view)\n\n        @test get_orientation(list_view) == ORIENTATION_HORIZONTAL\n        set_orientation!(list_view, ORIENTATION_VERTICAL)\n        @test get_orientation(list_view) == ORIENTATION_VERTICAL\n\n        set_enable_rubberband_selection!(list_view, true)\n        @test get_enable_rubberband_selection(list_view) == true\n\n        @test get_single_click_activate(list_view) == false\n        set_single_click_activate!(list_view, true)\n        @test get_single_click_activate(list_view) == true\n\n        @test get_show_separators(list_view) == false\n        set_show_separators!(list_view, true)\n        @test get_show_separators(list_view) == true\n\n        push_front!(list_view, Separator())\n        push_back!(list_view, Separator())\n\n        child = Separator()\n        it = insert_at!(list_view, 1, child)\n        @test find(list_view, child) == 1\n\n        push_back!(list_view, Separator(), it)\n        set_widget_at!(list_view, 1, Separator(), it)\n\n        @test get_n_items(list_view) == 3\n        remove!(list_view, 1)\n        @test get_n_items(list_view) == 2\n\n        @test get_selection_model(list_view) isa SelectionModel\n\n        connect_signal_activate_item!(list_view) do self::ListView, index::Integer\n            @test true\n            return nothing\n        end\n    end\nend\n\n### LOG\n\nfunction test_log(::Container)\n    @testset \"Log\" begin\n    \n        name = tempname()\n        @test set_log_file!(name) == true\n\n        set_surpress_debug!(Mousetrap.MOUSETRAP_DOMAIN, false)\n        set_surpress_info!(Mousetrap.MOUSETRAP_DOMAIN, false)\n\n        message = \"LOG TEST\"\n        log_info(Mousetrap.MOUSETRAP_DOMAIN, message)\n        log_debug(Mousetrap.MOUSETRAP_DOMAIN, message)\n        log_warning(Mousetrap.MOUSETRAP_DOMAIN, message)\n        log_critical(Mousetrap.MOUSETRAP_DOMAIN, message)\n        # log_fatal(Mousetrap.MOUSETRAP_DOMAIN, message) # this would quit runtime \n\n        file = open(name)\n        lines = readlines(file)\n        @test isempty(lines) == false\n        close(file)\n\n        @test get_surpress_debug(Mousetrap.MOUSETRAP_DOMAIN) == false\n        set_surpress_debug!(Mousetrap.MOUSETRAP_DOMAIN, true)\n        @test get_surpress_debug(Mousetrap.MOUSETRAP_DOMAIN) == true\n        \n        @test get_surpress_info(Mousetrap.MOUSETRAP_DOMAIN) == false\n        set_surpress_info!(Mousetrap.MOUSETRAP_DOMAIN, true)\n        @test get_surpress_info(Mousetrap.MOUSETRAP_DOMAIN) == true\n    end\nend\n\n### MENU_MODEL\n\nfunction test_menus(::Container)\n   \n    icon = Main.icon[]\n    action = Action(\"test.action\", Main.app[]) do self::Action\n    end\n\n    root = MenuModel()\n    submenu = MenuModel()\n    section = MenuModel()\n\n    items_changed_called = Ref{Bool}(false)\n    connect_signal_items_changed!(root, items_changed_called) do self::MenuModel, position::Integer, n_removed::Integer, n_added::Integer, items_changed_called\n        items_changed_called[] = true\n        return nothing\n    end\n\n    add_action!(submenu, \"Action\", action)\n    add_icon!(submenu, icon, action)\n    add_widget!(submenu, Separator())\n\n    add_action!(section, \"Section\", action)\n    add_section!(submenu, \"section\", section)\n    add_submenu!(root, \"Submenu\", submenu)\n\n    @testset \"MenuModel\" begin\n        Base.show(devnull, root)\n        @test items_changed_called[] == true\n    end\n   \n    @testset \"MenuBar\" begin\n        bar = MenuBar(root)\n        @test Mousetrap.is_native_widget(bar)\n        Base.show(devnull, bar)\n    end\n\n    @testset \"PopoverMenu\" begin\n        popover = PopoverMenu(root)\n        @test Mousetrap.is_native_widget(popover)\n        Base.show(devnull, popover)\n    end\nend\n\n### NOTE_BOOK\n\nfunction test_notebook(::Container)\n\n    @testset \"Notebook\" begin\n        notebook = Notebook()\n\n        Base.show(devnull, notebook)\n        @test Mousetrap.is_native_widget(notebook)\n\n        page_added_called = Ref{Bool}(false)\n        connect_signal_page_added!(notebook, page_added_called) do self::Notebook, page_index::Integer, page_added_called\n            page_added_called[] = true\n            return nothing\n        end\n\n        page_removed_called = Ref{Bool}(false)\n        connect_signal_page_removed!(notebook, page_removed_called) do self::Notebook, page_index::Integer, page_removed_called\n            page_removed_called[] = true\n            return nothing\n        end\n\n        page_reordered_called = Ref{Bool}(false)\n        connect_signal_page_reordered!(notebook, page_reordered_called) do self::Notebook, page_index::Integer, page_reordered_called\n            page_reordered_called[] = true\n            return nothing\n        end\n\n        page_selection_changed_called = Ref{Bool}(false)\n        connect_signal_page_selection_changed!(notebook, page_selection_changed_called) do self::Notebook, page_index::Integer, page_selection_changed_called\n            page_selection_changed_called[] = true\n            return nothing\n        end\n\n        push_front!(notebook, Label(\"01\"), Label(\"01\"))\n        push_back!(notebook, Label(\"02\"), Label(\"02\"))\n        insert_at!(notebook, 1, Label(\"03\"), Label(\"03\"))\n        move_page_to!(notebook, 1, 2)\n\n\n        @test get_current_page(notebook) == 1\n\n        #=\n        next_page!(notebook)\n        @test get_current_page(notebook) == 2\n\n        previous_page!(notebook)\n        @test get_current_page(notebook) == 1\n        =#\n\n        # these tests pass, but they trigger \"gtk_root_get_focus: assertion 'GTK_IS_ROOT (self)' failed\" because notebook is not yet realized\n\n        @test get_n_pages(notebook) == 3\n        remove!(notebook, 1)\n        @test get_n_pages(notebook) == 2\n\n        @test get_is_scrollable(notebook) == false\n        set_is_scrollable!(notebook, true)\n        @test get_is_scrollable(notebook) == true\n        \n        @test get_has_border(notebook) == true\n        set_has_border!(notebook, false)\n        @test get_has_border(notebook) == false\n\n        @test get_tabs_visible(notebook) == true\n        set_tabs_visible!(notebook, false)\n        @test get_tabs_visible(notebook) == false\n\n        @test get_quick_change_menu_enabled(notebook) == false\n        set_quick_change_menu_enabled!(notebook, true)\n        @test get_quick_change_menu_enabled(notebook) == true\n\n        @test get_tab_position(notebook) == RELATIVE_POSITION_ABOVE\n        set_tab_position!(notebook, RELATIVE_POSITION_BELOW)\n        @test get_tab_position(notebook) == RELATIVE_POSITION_BELOW\n\n        @test get_tabs_reorderable(notebook) == false\n        set_tabs_reorderable!(notebook, true)\n        @test get_tabs_reorderable(notebook) == true\n\n        @test page_added_called[]\n        @test page_removed_called[]\n        @test page_reordered_called[]\n        @test page_selection_changed_called[]\n    end\nend\n\n### OVERLAY\n\nfunction test_overlay(::Container)\n    @testset \"Overlay\" begin\n        overlay = Overlay()\n        Base.show(devnull, overlay)\n        @test Mousetrap.is_native_widget(overlay)\n\n        overlay_child = Separator()\n\n        set_child!(overlay, Separator())\n        add_overlay!(overlay, overlay_child)\n\n        remove_child!(overlay)\n        remove_overlay!(overlay, overlay_child)\n\n        @test overlay isa Widget\n    end\nend\n\n### PANED\n\nfunction test_paned(::Container)\n    @testset \"Paned\" begin\n        paned = Paned(ORIENTATION_HORIZONTAL)\n        Base.show(devnull, paned)\n        @test Mousetrap.is_native_widget(paned)\n\n        set_start_child!(paned, Separator())\n        set_end_child!(paned, Separator())\n\n        @test get_start_child_resizable(paned) == true\n        set_start_child_resizable!(paned, false)\n        @test get_start_child_resizable(paned) == false\n\n        @test get_start_child_shrinkable(paned) == true\n        set_start_child_shrinkable!(paned, false)\n        @test get_start_child_shrinkable(paned) == false\n\n        @test get_end_child_resizable(paned) == true\n        set_end_child_resizable!(paned, false)\n        @test get_end_child_resizable(paned) == false\n\n        @test get_end_child_shrinkable(paned) == true\n        set_end_child_shrinkable!(paned, false)\n        @test get_end_child_shrinkable(paned) == false\n\n        @test get_has_wide_handle(paned) == true\n        set_has_wide_handle!(paned, false)\n        @test get_has_wide_handle(paned) == false\n\n        @test get_orientation(paned) == ORIENTATION_HORIZONTAL\n        set_orientation!(paned, ORIENTATION_VERTICAL)\n        @test get_orientation(paned) == ORIENTATION_VERTICAL\n\n        set_position!(paned, 32)\n        @test get_position(paned) == 32\n\n        remove_start_child!(paned)\n        remove_end_child!(paned)\n    end\nend\n\n### POPOVER \n\nfunction test_popover(container::Container)\n\n    popover = Popover()\n\n    @testset \"Popover\" begin\n        Base.show(devnull, popover)\n        @test Mousetrap.is_native_widget(popover)\n\n        set_child!(popover, Separator())\n        id = add_child!(container, popover, \"Popover\")\n\n        @test get_has_base_arrow(popover) == true\n        set_has_base_arrow!(popover, false)\n        @test get_has_base_arrow(popover) == false\n\n        @test get_autohide(popover) == true\n        set_autohide!(popover, false)\n        @test get_autohide(popover) == false\n\n        set_relative_position!(popover, RELATIVE_POSITION_BELOW)\n        @test get_relative_position(popover) == RELATIVE_POSITION_BELOW\n\n        connect_signal_closed!(popover) do self::Popover\n            return nothing\n        end\n\n        connect_signal_realize!(popover) do self::Popover   \n            popup!(popover)\n            popdown!(popover)\n        end\n\n        remove_child!(container, id)\n    end\n\n    @testset \"PopoverButton\" begin\n        popover_button = PopoverButton(popover)\n\n        Base.show(devnull, popover_button)\n        @test Mousetrap.is_native_widget(popover_button)\n\n        @test get_always_show_arrow(popover_button) == true\n        set_always_show_arrow!(popover_button, false)\n        @test get_always_show_arrow(popover_button) == false\n\n        @test get_has_frame(popover_button) == true\n        set_has_frame!(popover_button, false)\n        @test get_has_frame(popover_button) == false\n\n        @test get_is_circular(popover_button) == false\n        set_is_circular!(popover_button, true)\n        @test get_is_circular(popover_button) == true\n\n        set_relative_position!(popover_button, RELATIVE_POSITION_BELOW)\n        @test get_relative_position(popover_button) == RELATIVE_POSITION_BELOW\n\n        set_child!(popover_button, Separator())\n        #remove_child!(popover_button)\n        # cf. https://gitlab.gnome.org/GNOME/gtk/-/issues/5969\n\n        set_popover!(popover_button, Popover())\n        remove_popover!(popover_button)\n\n        set_popover_menu!(popover_button, PopoverMenu(MenuModel()))\n        remove_popover!(popover_button)\n\n        activate_called = Ref{Bool}(false)\n        connect_signal_activate!(popover_button, activate_called) do self::PopoverButton, activate_called\n            activate_called[] = true\n            return nothing\n        end\n        activate!(popover_button)\n        @test activate_called[] == true\n    end\nend\n\n### POPUP_MESSAGE\n\nfunction test_popup_message(::Container)\n\n    overlay = PopupMessageOverlay()\n    Base.show(devnull, overlay)\n    set_child!(overlay, Separator())\n\n    message = PopupMessage(\"title\", \"button\")\n\n    @test get_title(message) == \"title\"\n    set_title!(message, \"not title\")\n    @test get_title(message) == \"not title\"\n\n    @test get_button_label(message) == \"button\"\n    set_button_label!(message, \"not button\")\n    @test get_button_label(message) == \"not button\"\n\n    id = \"test_popup_message.action\"\n    action = Action(id, Main.app[]) do self end\n    set_button_action!(message, action)\n    @test get_button_action_id(message) == id\n\n    @test get_is_high_priority(message) == false\n    set_is_high_priority!(message, true)\n    @test get_is_high_priority(message) == true\n\n    set_timeout!(message, seconds(1))\n    @test get_timeout(message) == seconds(1)\n\n    connect_signal_dismissed!(message) do self::PopupMessage end\n    connect_signal_button_clicked!(message) do self::PopupMessage end\n    \n    show_message!(overlay, message)\n    remove_child!(overlay)\nend\n\n### PROGRESS_BAR\n\nfunction test_progress_bar(::Container)\n    @testset \"ProgressBar\" begin\n        progress_bar = ProgressBar()\n        Base.show(devnull, progress_bar)\n        @test Mousetrap.is_native_widget(progress_bar)\n\n        @test get_fraction(progress_bar) == 0.0\n        set_fraction!(progress_bar, 0.5)\n        @test get_fraction(progress_bar) == 0.5\n\n        @test get_orientation(progress_bar) == ORIENTATION_HORIZONTAL\n        set_orientation!(progress_bar, ORIENTATION_VERTICAL)\n        @test get_orientation(progress_bar) == ORIENTATION_VERTICAL\n\n        @test get_is_inverted(progress_bar) == false\n        set_is_inverted!(progress_bar, true)\n        @test get_is_inverted(progress_bar) == true\n\n        @test get_show_text(progress_bar) == false\n        set_show_text!(progress_bar, true)\n        set_text!(progress_bar, \"text\")\n        @test get_show_text(progress_bar) == true\n        @test get_text(progress_bar) == \"text\"\n\n        pulse(progress_bar)\n    end\nend\n\n### REVEALER\n\nfunction test_revealer(::Container)\n    @testset \"Revealer\" begin\n        revealer = Revealer()\n        Base.show(devnull, revealer)\n        @test Mousetrap.is_native_widget(revealer)\n\n        revealed_called = Ref{Bool}(false)\n        connect_signal_revealed!(revealer, revealed_called) do self::Revealer, revealed_called\n            revealed_called[] = true\n            return nothing\n        end\n\n        set_child!(revealer, Separator())\n        set_is_revealed!(revealer, false)\n        @test get_is_revealed(revealer) == false\n        set_is_revealed!(revealer, true)\n        @test get_is_revealed(revealer) == true\n    \n        set_transition_duration!(revealer, seconds(1))\n        @test get_transition_duration(revealer) == seconds(1)\n\n        set_transition_type!(revealer, REVEALER_TRANSITION_TYPE_SLIDE_DOWN)\n        @test get_transition_type(revealer) == REVEALER_TRANSITION_TYPE_SLIDE_DOWN\n\n        @test revealed_called[] == true\n    end\nend\n\n### ACTION BAR\n\nfunction test_action_bar(::Container)\n    @testset \"ActionBar\" begin\n        bar = ActionBar()\n        Base.show(devnull, bar)\n        @test Mousetrap.is_native_widget(bar)\n\n        widget = Label(\"\")\n        push_front!(bar, widget)\n        push_back!(bar, Label(\"\"))\n        remove!(bar, widget)\n\n        set_center_child!(bar, Label(\"\"))\n        remove_center_child!(bar)\n\n        @test get_is_revealed(bar) == true\n        set_is_revealed!(bar, false)\n        @test get_is_revealed(bar) == false\n    end\nend\n\n### SCALE\n\nfunction test_scale(::Container)\n    @testset \"Scale\" begin\n        scale = Scale(0, 1, 0.01)\n        Base.show(devnull, scale)\n        @test Mousetrap.is_native_widget(scale)\n\n        value_changed_called = Ref{Bool}(false)\n        connect_signal_value_changed!(scale, value_changed_called) do self::Scale, value_changed_called\n            value_changed_called[] = true\n            return nothing\n        end\n\n        @test get_value(scale) == 0.5\n        set_value!(scale, 0.6)\n        @test get_value(scale) == 0.6f0\n\n        @test get_lower(scale) == 0.0\n        set_lower!(scale, 1.0)\n        @test get_lower(scale) == 1.0\n\n        @test get_upper(scale) == 1.0\n        set_upper!(scale, 2.0)\n        @test get_upper(scale) == 2.0\n\n        @test get_step_increment(scale) == 0.01f0\n        set_step_increment!(scale, 0.5)\n        @test get_step_increment(scale) == 0.5\n\n        @test get_has_origin(scale) == true\n        set_has_origin!(scale, false)\n        @test get_has_origin(scale) == false\n\n        @test get_orientation(scale) == ORIENTATION_HORIZONTAL\n        set_orientation!(scale, ORIENTATION_VERTICAL)\n        @test get_orientation(scale) == ORIENTATION_VERTICAL\n\n        @test get_should_draw_value(scale) == false\n        set_should_draw_value!(scale, true)\n        @test get_should_draw_value(scale) == true\n\n        @test get_adjustment(scale) isa Adjustment\n        add_mark!(scale, 1.5, RELATIVE_POSITION_RIGHT_OF, \"label\")\n        clear_marks!(scale)\n\n        @test value_changed_called[] == true\n    end\nend\n\n### SCROLLBAR\n\nfunction test_scrollbar(::Container)\n    @testset \"Scrollbar\" begin\n        scrollbar = Scrollbar(ORIENTATION_HORIZONTAL, Adjustment(0, 0, 1, 0.01))\n        Base.show(devnull, scrollbar)\n        @test Mousetrap.is_native_widget(scrollbar)\n        \n        @test get_value(get_adjustment(scrollbar)) == 0.0\n        \n        @test get_orientation(scrollbar) == ORIENTATION_HORIZONTAL\n        set_orientation!(scrollbar, ORIENTATION_VERTICAL)\n        @test get_orientation(scrollbar) == ORIENTATION_VERTICAL \n    end\nend\n\n### SELECTION_MODEL\n\nfunction test_selection_model(::Container)\n    @testset \"SelectionModel\" begin\n        list_view = ListView(ORIENTATION_HORIZONTAL, SELECTION_MODE_MULTIPLE)\n        push_back!(list_view, Separator())\n        push_back!(list_view, Separator())\n        push_back!(list_view, Separator())\n\n        selection_model = get_selection_model(list_view)\n        Base.show(devnull, selection_model)\n        @test Mousetrap.is_native_widget(list_view)\n\n        @test get_n_items(selection_model) == 3\n        select!(selection_model, 1)\n        select_all!(selection_model)\n        @test length(get_selection(selection_model)) == 3\n        unselect_all!(selection_model)\n        @test length(get_selection(selection_model)) == 0\n    end\nend\n\n### SEPARATOR \n\nfunction test_separator(::Container)\n    @testset \"Separator\" begin\n        separator = Separator(ORIENTATION_HORIZONTAL)\n        Base.show(devnull, separator)\n        @test Mousetrap.is_native_widget(separator)\n\n        @test get_orientation(separator) == ORIENTATION_HORIZONTAL\n        set_orientation!(separator, ORIENTATION_VERTICAL)\n        @test get_orientation(separator) == ORIENTATION_VERTICAL\n    end\nend\n\n### SPIN_BUTTON\n\nfunction test_spin_button(::Container)\n    @testset \"SpinButton\" begin\n\n        scale = SpinButton(0, 1, 0.01)\n        Base.show(devnull, scale)\n        @test Mousetrap.is_native_widget(scale)\n\n        value_changed_called = Ref{Bool}(false)\n        connect_signal_value_changed!(scale, value_changed_called) do self::SpinButton, value_changed_called\n            value_changed_called[] = true\n            return nothing\n        end\n\n        set_value!(scale, 0.5)\n        @test get_value(scale) == 0.5\n\n        @test get_lower(scale) == 0.0\n        set_lower!(scale, 1.0)\n        @test get_lower(scale) == 1.0\n\n        @test get_upper(scale) == 1.0\n        set_upper!(scale, 2.0)\n        @test get_upper(scale) == 2.0\n\n        @test get_step_increment(scale) == 0.01f0\n        set_step_increment!(scale, 0.5)\n        @test get_step_increment(scale) == 0.5f0\n\n        set_acceleration_rate!(scale, 2)\n        @test get_acceleration_rate(scale) == 2\n\n        @test get_allow_only_numeric(scale) == true\n        set_allow_only_numeric!(scale, false)\n        @test get_allow_only_numeric(scale) == false\n        \n        @test get_n_digits(scale) > 0\n        set_n_digits!(scale, 10)\n        @test get_n_digits(scale) == 10\n\n        @test get_should_wrap(scale) == false\n        set_should_wrap!(scale, true)\n        @test get_should_wrap(scale) == true\n\n        @test get_should_snap_to_ticks(scale) == false\n        set_should_snap_to_ticks!(scale, true)\n        @test get_should_snap_to_ticks(scale) == true\n\n        set_text_to_value_function!(scale) do self::SpinButton, text::String\n            value::Float32 = 0\n            return value\n        end\n\n        set_value_to_text_function!(scale) do self::SpinButton, value::AbstractFloat\n            result = \"\"\n            return result\n        end\n\n        @test value_changed_called[] == true\n    end\nend\n\n### SPINNER\n\nfunction test_spinner(::Container)\n    @testset \"Spinner\" begin\n        spinner = Spinner()\n        Base.show(devnull, spinner)\n        @test Mousetrap.is_native_widget(spinner)\n\n        @test get_is_spinning(spinner) == true\n        set_is_spinning!(spinner, false)\n        @test get_is_spinning(spinner) == false\n        stop!(spinner)\n        @test get_is_spinning(spinner) == false\n        start!(spinner)\n        @test get_is_spinning(spinner) == true\n    end\nend\n\n### STACK\n\nfunction test_stack(::Container)\n    @testset \"Stack\" begin\n        stack = Stack()\n        Base.show(devnull, stack)\n        @test Mousetrap.is_native_widget(stack)\n\n        id_01 = add_child!(stack, Separator(), \"01\")\n        id_02 = add_child!(stack, Separator(), \"02\")\n\n        @test get_child_at(stack, 1) == id_01\n\n        @test get_is_horizontally_homogeneous(stack) == true\n        set_is_horizontally_homogeneous!(stack, false)\n        @test get_is_horizontally_homogeneous(stack) == false\n\n        @test get_is_vertically_homogeneous(stack) == true\n        set_is_vertically_homogeneous!(stack, false)\n        @test get_is_vertically_homogeneous(stack) == false\n\n        @test get_should_interpolate_size(stack) == false\n        set_should_interpolate_size!(stack, true)\n        @test get_should_interpolate_size(stack) == true\n\n        set_transition_type!(stack, STACK_TRANSITION_TYPE_OVER_UP)\n        @test get_transition_type(stack) == STACK_TRANSITION_TYPE_OVER_UP\n\n        set_transition_duration!(stack, seconds(1))\n        @test get_transition_duration(stack) == seconds(1)\n\n        @test get_selection_model(stack) isa SelectionModel\n        set_visible_child!(stack, id_02)\n\n        sidebar = StackSidebar(stack)\n        @test sidebar isa Widget\n\n        switcher = StackSwitcher(stack)\n        @test switcher isa Widget\n    end\nend\n\n### SWITCH\n\nfunction test_switch(::Container)\n    @testset \"Switch\" begin\n        switch = Switch()\n        Base.show(devnull, switch)\n        @test Mousetrap.is_native_widget(switch)\n\n        switched_called = Ref{Bool}(false)\n        connect_signal_switched!(switch, switched_called) do self::Switch, switched_called\n            switched_called[] = true\n            set_is_active!(self, true) # `activate!` toggles switch only once it is realized, so we do it manually to avoid having to wait for the window to render\n            return nothing\n        end\n\n        set_is_active!(switch, false)\n        @test get_is_active(switch) == false\n        set_is_active!(switch, true)\n        @test get_is_active(switch) == true\n        @test switched_called[] == true\n    end\nend\n\n### TEXT_VIEW\n\nfunction test_text_view(::Container)\n    @testset \"TextView\" begin\n        text_view = TextView()\n        Base.show(devnull, text_view)\n        @test Mousetrap.is_native_widget(text_view)\n\n        text_changed_called = Ref{Bool}(false)\n        connect_signal_text_changed!(text_view, text_changed_called) do self::TextView, text_changed_called\n            text_changed_called[] = true\n            return nothing\n        end\n\n        @test get_bottom_margin(text_view) == 0\n        set_bottom_margin!(text_view, 10)\n        @test get_bottom_margin(text_view) == 10\n\n        @test get_left_margin(text_view) == 0\n        set_left_margin!(text_view, 10)\n        @test get_left_margin(text_view) == 10\n\n        @test get_right_margin(text_view) == 0\n        set_right_margin!(text_view, 10)\n        @test get_right_margin(text_view) == 10\n\n        @test get_top_margin(text_view) == 0\n        set_top_margin!(text_view, 10)\n        @test get_top_margin(text_view) == 10\n\n        @test get_editable(text_view) == true\n        set_editable!(text_view, false)\n        @test get_editable(text_view) == false\n\n        set_was_modified!(text_view, false)\n        @test get_was_modified(text_view) == false\n        set_text!(text_view, \"modified\")\n        @test get_was_modified(text_view) == true\n\n        undo!(text_view)\n        redo!(text_view)\n\n        @test text_changed_called[] == true\n    end\nend\n\n### TOGGLE_BUTTON\n\nfunction test_toggle_button(::Container)\n    @testset \"ToggleButton\" begin\n        \n        button = ToggleButton()\n        Base.show(devnull, button)\n        @test Mousetrap.is_native_widget(button)\n\n        toggled_called = Ref{Bool}(false)\n        connect_signal_toggled!(button, toggled_called) do self::ToggleButton, toggled_called\n            toggled_called[] = true\n            return nothing\n        end\n\n        clicked_called = Ref{Bool}(false)\n        connect_signal_clicked!(button, clicked_called) do self::ToggleButton, clicked_called\n            clicked_called[] = true\n            return nothing\n        end\n\n        set_is_active!(button, true)\n        @test get_is_active(button) == true\n\n        set_child!(button, Separator())\n        set_icon!(button, Main.icon[])\n        remove_child!(button)\n\n        @test get_is_circular(button) == false\n        set_is_circular!(button, true)\n        @test get_is_circular(button) == true\n    end\nend\n\nfunction test_typed_function(::Container)\n    @testset \"TypedFunction\" begin\n        yes_f(x::Int64) ::Nothing = return nothing\n        no_f1(x::Int32) ::Nothing = return nothing\n        no_f2(x::Int64) ::Int64 = return 1234\n\n        Test.@test TypedFunction(yes_f, Nothing, (Int64,)) isa TypedFunction\n        Test.@test_throws AssertionError TypedFunction(no_f1, Nothing, (Int64,))\n        Test.@test_throws AssertionError TypedFunction(no_f2, Nothing, (Int64,)) isa TypedFunction\n            # if return_t is nothing, the result is ignored, regardless of the results type\n\n        Base.show(devnull, TypedFunction(() -> nothing, Nothing, ()))\n    end\nend\n\nfunction test_viewport(::Container)\n    @testset \"Viewport\" begin\n        viewport = Viewport()\n        Base.show(devnull, viewport)\n        @test Mousetrap.is_native_widget(viewport)\n\n        connect_signal_scroll_child!(viewport) do self::Viewport, scroll_type::ScrollType, is_horizontal::Bool\n        end\n\n        @test get_has_frame(viewport) == false\n        set_has_frame!(viewport, true)\n        @test get_has_frame(viewport) == true\n\n        @test get_horizontal_adjustment(viewport) isa Adjustment\n        @test get_vertical_adjustment(viewport) isa Adjustment\n\n        @test get_kinetic_scrolling_enabled(viewport) == true\n        set_kinetic_scrolling_enabled!(viewport, false)\n        @test get_kinetic_scrolling_enabled(viewport) == false\n\n        @test get_propagate_natural_width(viewport) == false\n        set_propagate_natural_width!(viewport, true)\n        @test get_propagate_natural_width(viewport) == true\n\n        @test get_propagate_natural_height(viewport) == false\n        set_propagate_natural_height!(viewport, true)\n        @test get_propagate_natural_height(viewport) == true\n\n        @test get_vertical_scrollbar_policy(viewport) == SCROLLBAR_VISIBILITY_POLICY_AUTOMATIC\n        set_vertical_scrollbar_policy!(viewport, SCROLLBAR_VISIBILITY_POLICY_NEVER)\n        @test get_vertical_scrollbar_policy(viewport) == SCROLLBAR_VISIBILITY_POLICY_NEVER\n\n        @test get_horizontal_scrollbar_policy(viewport) == SCROLLBAR_VISIBILITY_POLICY_AUTOMATIC\n        set_horizontal_scrollbar_policy!(viewport, SCROLLBAR_VISIBILITY_POLICY_NEVER)\n        @test get_horizontal_scrollbar_policy(viewport) == SCROLLBAR_VISIBILITY_POLICY_NEVER\n\n        set_scrollbar_placement!(viewport, CORNER_PLACEMENT_TOP_LEFT)\n        @test get_scrollbar_placement(viewport) == CORNER_PLACEMENT_TOP_LEFT\n    end\nend\n\nfunction test_window(::Container)\n    @testset \"Window\" begin\n        \n        window = Window(Main.app[])\n        other_window = Window(Main.app[])\n\n        Base.show(devnull, window)\n        @test Mousetrap.is_native_widget(window)\n\n        close_request_called = Ref{Bool}(false)\n        connect_signal_close_request!(window, close_request_called) do self::Window, close_request_called\n            close_request_called[] = true\n            return WINDOW_CLOSE_REQUEST_RESULT_ALLOW_CLOSE\n        end\n\n        activate_default_widget_called = Ref{Bool}(false)\n        connect_signal_activate_default_widget!(window, activate_default_widget_called) do self::Window, activate_default_widget_called\n            activate_default_widget_called[] = true\n            return nothing\n        end\n\n        activate_focused_widget_called = Ref{Bool}(false)\n        connect_signal_activate_focused_widget!(window, activate_focused_widget_called) do self::Window, activate_focused_widget_called\n            activate_focused_widget_called[] = true\n            return nothing\n        end\n\n        @test get_destroy_with_parent(window) == false\n        set_destroy_with_parent!(window, true)\n        @test get_destroy_with_parent(window) == true\n        \n        @test get_focus_visible(window) == true\n        set_focus_visible!(window, false)\n        @test get_focus_visible(window) == false\n\n        @test get_has_close_button(window) == true\n        set_has_close_button!(window, false)\n        @test get_has_close_button(window) == false\n\n        @test get_is_decorated(window) == true\n        set_is_decorated!(window, false)\n        @test get_is_decorated(window) == false\n\n        @test get_is_modal(window) == false\n        set_is_modal!(window, true)\n        @test get_is_modal(window) == true\n\n        set_title!(window, \"test\")\n        @test get_title(window) == \"test\"\n\n        button = Entry()\n        set_child!(window, button)\n        set_default_widget!(window, button)\n        activate!(button)\n\n        set_transient_for!(other_window, window)\n\n        #@test activate_default_widget_called[] == true\n        #@test activate_focused_widget_called[] == true\n\n        @test get_header_bar(window) isa HeaderBar\n\n        @test get_hide_on_close(window) == false\n        set_hide_on_close!(window, true)\n        @test get_hide_on_close(window) == true\n\n        @test get_is_closed(window) == true\n        present!(window)\n        @test get_is_closed(window) == false\n        set_minimized!(window, true)\n        set_maximized!(window, true)\n\n        close!(other_window)\n        close!(window)\n        @test get_is_closed(window) == true\n        destroy!(window)\n        destroy!(other_window)\n    end\nend\n\n### WIDGET\n\nfunction test_widget(widget::Container)\n\n    @testset \"Widget\" begin\n\n        for s in [:realize, :unrealize, :destroy, :hide, :show, :map, :unmap]\n            signal_called = Ref{Bool}(false)\n            connect_signal_f = Symbol(\"connect_signal_$(s)!\")\n            eval(:(\n                $connect_signal_f($widget, $signal_called) do self::Container, signal_called\n                    signal_called[] = true\n                    @test signal_called[]\n                    return nothing\n                end\n            ))\n        end\n\n        set_size_request!(widget, Vector2f(50, 100))\n        @test get_size_request(widget) == Vector2f(50, 100)\n\n        set_margin_top!(widget, 1)\n        @test get_margin_top(widget) == 1\n        \n        set_margin_bottom!(widget, 2)\n        @test get_margin_bottom(widget) == 2\n\n        set_margin_start!(widget, 3)\n        @test get_margin_start(widget) == 3\n\n        set_margin_end!(widget, 4)\n        @test get_margin_end(widget) == 4\n\n        set_margin_horizontal!(widget, 5)\n        @test get_margin_start(widget) == 5\n        @test get_margin_end(widget) == 5\n\n        set_margin_vertical!(widget, 6)\n        @test get_margin_top(widget) == 6\n        @test get_margin_bottom(widget) == 6\n\n        set_margin!(widget, 7)\n        @test get_margin_top(widget) == 7\n        @test get_margin_bottom(widget) == 7\n        @test get_margin_start(widget) == 7\n        @test get_margin_end(widget) == 7\n\n        set_expand_horizontally!(widget, true)\n        @test get_expand_horizontally(widget) == true\n\n        set_expand_vertically!(widget, true)\n        @test get_expand_horizontally(widget) == true\n\n        set_expand!(widget, false)\n        @test get_expand_horizontally(widget) == false\n        @test get_expand_horizontally(widget) == false \n\n        set_horizontal_alignment!(widget, ALIGNMENT_START)\n        @test get_horizontal_alignment(widget) == ALIGNMENT_START\n\n        set_vertical_alignment!(widget, ALIGNMENT_START)\n        @test get_vertical_alignment(widget) == ALIGNMENT_START\n\n        set_alignment!(widget, ALIGNMENT_END)\n        @test get_vertical_alignment(widget) == ALIGNMENT_END\n        @test get_horizontal_alignment(widget) == ALIGNMENT_END\n\n        @test isapprox(get_opacity(widget), 1.0)\n        set_opacity!(widget, 0.6)\n        @test isapprox(get_opacity(widget), 0.6)\n        set_opacity!(widget, 1.0)\n\n        @test calculate_monitor_dpi(widget) > 0\n        @test get_scale_factor(widget) > 0\n\n        set_is_visible!(widget, false)\n        @test get_is_visible(widget) == false\n        set_is_visible!(widget, true)\n\n        set_tooltip_text!(widget, \"test\")\n        set_tooltip_widget!(widget, Label(\"test\"))\n        remove_tooltip_widget!(widget)\n\n        set_cursor!(widget, CURSOR_TYPE_NONE)\n        set_cursor_from_image!(widget, Image(1, 1, RGBA(1, 0, 0, 1)))\n\n        hide!(widget)\n        show!(widget)\n\n        set_is_focusable!(widget, true)\n        @test get_is_focusable(widget) == true\n        set_is_focusable!(widget, false)\n        @test get_is_focusable(widget) == false\n\n        set_focus_on_click!(widget, true)\n        @test get_focus_on_click(widget) == true\n\n        grab_focus!(widget)\n        @test get_has_focus(widget) isa Bool\n\n        set_can_respond_to_input!(widget, false)\n        @test get_can_respond_to_input(widget) == false\n        set_can_respond_to_input!(widget, true)\n        @test get_can_respond_to_input(widget) == true\n\n        @test get_is_realized(widget) == true\n\n        # sizes are only available after first show\n\n        minimum_size = get_minimum_size(widget)\n        @test minimum_size.x >= 0 && minimum_size.y >= 0\n\n        natural_size = get_natural_size(widget)\n        @test natural_size.x >= 0 && natural_size.y >= 0\n\n        position = get_position(widget)\n        @test position.x >= 0 && position.y >= 0\n\n        allocated_size = get_allocated_size(widget)\n        @test allocated_size.x >= 0 && allocated_size.y >= 0\n\n        set_hide_on_overflow!(widget, true)\n        @test get_hide_on_overflow(widget) == true\n        set_hide_on_overflow!(widget, false)\n\n        Mousetrap.add_css_class!(widget, \"test\")\n        @test !isempty(Mousetrap.get_css_classes(widget))\n        Mousetrap.remove_css_class!(widget, \"test\")\n        @test isempty(Mousetrap.get_css_classes(widget))\n\n        tick_callback_called = Ref{Bool}(false)\n        set_tick_callback!(widget, tick_callback_called) do clock::FrameClock, tick_callback_called\n            tick_callback_called[] = true\n\n            @testset \"FrameClock\" begin\n                Base.show(devnull, clock)\n                \n                paint_called = Ref{Bool}(false)\n                connect_signal_paint!(clock, paint_called) do self::FrameClock, paint_called\n                    paint_called[] = true\n                    @test get_target_frame_duration(clock) isa Time\n                    @test get_time_since_last_frame(clock) isa Time\n                    return nothing\n                end\n        \n                update_called = Ref{Bool}(false)\n                connect_signal_update!(clock, update_called) do self::FrameClock, update_called\n                    update_called[] = true\n                    @test get_target_frame_duration(clock) isa Time\n                    @test get_time_since_last_frame(clock) isa Time\n                    return nothing\n                end\n        \n                @test clock isa FrameClock\n            end\n            return TICK_CALLBACK_RESULT_DISCONTINUE\n        end\n        # @test tick_callback_called[] == true  # only called after first frame is done, which never happens in tests\n    end\nend\n\n### RENDER_AREA\n\nfunction test_render_area(::Container)\n\n    if !Mousetrap.MOUSETRAP_ENABLE_OPENGL_COMPONENT || Mousetrap.detail.is_opengl_disabled()\n        return\n    end\n    \n    render_area = RenderArea()\n\n    @testset \"RenderArea\" begin\n        #make_current(render_area) # would print soft warning because render area is not yet realized\n        queue_render(render_area)\n        @test render_area isa RenderArea\n    end\n\n    for pair in [\n        \"Point\" => Point(Vector2f(0, 0)),\n        \"Points\" => Points([Vector2f(0.5, 0.5), Vector2f(0, 0,)]),\n        \"Triangle\" => Triangle(Vector2f(-0.5, 0.5), Vector2f(0.5, 0.5), Vector2f(0.5, -0.5)),\n        \"Rectangle\" => Rectangle(Vector2f(-0.5, -0.5), Vector2f(1, 1)),\n        \"Circle\" => Circle(Vector2f(0, 0), 1, 16),\n        \"Ellipse\" => Ellipse(Vector2f(0, 0), 0.5, 1, 16),\n        \"Line\" => Line(Vector2f(-0.5, 0.5), Vector2f(0.5, 0.5)),\n        \"LineStrip\" => LineStrip([Vector2f(-0.5, 0.5), Vector2f(0.5, 0.5), Vector2f(0.5, -0.5)]),\n        \"Polygon\" => Polygon([Vector2f(-0.5, 0.5), Vector2f(0.5, 0.5), Vector2f(0.5, -0.5)]),\n        \"RectangularFrame\" => RectangularFrame(Vector2f(-0.5, 0.5), Vector2f(1, 1), 0.1, 0.1),\n        \"CircularRing\" => CircularRing(Vector2f(0.0, 0.0), 1.0, 0.1, 8),\n        \"EllipticalRing\" => EllipticalRing(Vector2f(0.0, 0.0), 1.0, 0.1, 0.2, 0.1, 8),\n        \"Wireframe\" => Wireframe([Vector2f(-0.5, 0.5), Vector2f(0.5, 0.5), Vector2f(0.5, -0.5)])\n    ]\n        @testset \"$(pair[1])\" begin\n        \n            shape = pair[2]\n            add_render_task!(render_area, RenderTask(shape))\n            add_render_task!(render_area, RenderTask(Outline(shape)))\n\n            for i in 1:get_n_vertices(shape)\n                set_vertex_color!(shape, i, RGBA(1, 0, 1, 1))\n                @test get_vertex_color(shape, i) == RGBA(1, 0, 1, 1)\n                coordinate = get_vertex_texture_coordinate(shape, i) \n                @test coordinate.x >= 0 && coordinate.x <= 1\n                @test coordinate.y >= 0 && coordinate.y <= 1\n                \n                set_vertex_position!(shape, i, Vector3f(0, 0, 0))\n                @test get_vertex_position(shape, i) == Vector3f(0, 0, 0)\n\n                set_vertex_texture_coordinate!(shape, i, Vector2f(-1, -1))\n                @test get_vertex_texture_coordinate(shape, i) == Vector2f(-1, -1)\n\n                Base.show(devnull, shape)\n            end\n\n            @test get_is_visible(shape) == true\n            set_is_visible!(shape, false)\n            @test get_is_visible(shape) == false\n            \n            bounding_box = get_bounding_box(shape)\n            @test bounding_box.top_left == get_top_left(shape)\n            @test bounding_box.size == get_size(shape)\n\n            set_top_left!(shape, Vector2f(1, 2))\n            @test get_top_left(shape) == Vector2f(1, 2)\n\n            set_centroid!(shape, Vector2f(1, 2))\n            centroid = get_centroid(shape)\n            @test isapprox(centroid.x, 1) && isapprox(centroid.y, 2)\n\n            rotate!(shape, degrees(180), get_centroid(shape))\n\n            new_centroid = get_centroid(shape)\n            @test isapprox(centroid.x, new_centroid.x) && isapprox(centroid.y, new_centroid.y)\n\n            set_color!(shape, RGBA(0, 1, 1, 1))\n            @test get_vertex_color(shape, 1) == RGBA(0, 1, 1, 1)\n        end\n    end\n\n    @testset \"Shader\" begin\n        \n        vertex_shader_source = \"\"\"\n        #version 330\n\n        layout (location = 0) in vec3 _vertex_position_in;\n        layout (location = 1) in vec4 _vertex_color_in;\n        layout (location = 2) in vec2 _vertex_texture_coordinates_in;\n\n        uniform mat4 _transform;\n\n        out vec4 _vertex_color;\n        out vec2 _texture_coordinates;\n        out vec3 _vertex_position;\n\n        void main()\n        {\n            gl_Position = _transform * vec4(_vertex_position_in, 1.0);\n            _vertex_color = _vertex_color_in;\n            _vertex_position = _vertex_position_in;\n            _texture_coordinates = _vertex_texture_coordinates_in;\n        }\n        \"\"\"\n\n        fragment_shader_source = \"\"\"\n        #version 130\n\n        in vec4 _vertex_color;\n        in vec2 _texture_coordinates;\n        in vec3 _vertex_position;\n\n        out vec4 _fragment_color;\n\n        uniform int _texture_set;\n        uniform sampler2D _texture;\n\n        uniform float _float;\n        uniform int _int;\n        uniform uint _uint;\n        uniform vec2 _vec2;\n        uniform vec3 _vec3;\n        uniform vec4 _vec4;\n        uniform mat4 _mat4;\n\n        void main()\n        {\n            // prevent optimizing uniform away\n            float value = _float + _int + _uint + _vec2.x + _vec3.x + _vec4.x + _mat4[0][0];\n            _fragment_color = vec4(vec3(value), 1);\n        }\n        \"\"\"\n\n        shader = Shader()\n        @test create_from_string!(shader, SHADER_TYPE_FRAGMENT, fragment_shader_source)\n        @test create_from_string!(shader, SHADER_TYPE_VERTEX, vertex_shader_source)\n\n        @test get_program_id(shader) != 0\n        @test get_fragment_shader_id(shader) != 0\n        @test get_vertex_shader_id(shader) != 0\n\n        for name in [\"_float\", \"_int\", \"_uint\", \"_vec2\", \"_vec3\", \"_vec4\", \"_mat4\"]\n            @test get_uniform_location(shader, name) >= 0\n        end\n\n        set_uniform_float!(shader, \"_float\", Cfloat(1234.0))\n        set_uniform_int!(shader, \"_int\", Cint(1234))\n        set_uniform_uint!(shader, \"_uint\", Cuint(1234))\n        set_uniform_vec2!(shader, \"_vec2\", Vector2f(1, 2))\n        set_uniform_vec3!(shader, \"_vec3\", Vector3f(1, 2, 3))\n        set_uniform_vec4!(shader, \"_vec4\", Vector4f(1, 2, 3, 4))\n        set_uniform_transform!(shader, \"_mat4\", GLTransform())\n\n        Base.show(devnull, shader)\n    end\n\n    @testset \"RenderTask\" begin\n        shape = Rectangle(Vector2f(-0.5, 0.5), Vector2f(1, 1))\n        shader = Shader()\n        blend_mode = BLEND_MODE_NONE\n        transform = GLTransform()\n\n        task = RenderTask(shape; shader = shader, transform = transform, blend_mode = blend_mode)\n\n        set_uniform_float!(task, \"_float\", Cfloat(1234.0))\n        @test get_uniform_float(task, \"_float\") == 1234.0\n\n        set_uniform_int!(task, \"_int\", Cint(1234))\n        @test get_uniform_int(task, \"_int\") == 1234\n\n        set_uniform_uint!(task, \"_uint\", UInt32(1234))\n        @test get_uniform_uint(task, \"_uint\") == UInt(1234)\n\n        set_uniform_vec2!(task, \"_vec2\", Vector2f(1, 2))\n        @test get_uniform_vec2(task, \"_vec2\") == Vector2f(1, 2)\n\n        set_uniform_vec3!(task, \"_vec3\", Vector3f(1, 2, 3))\n        @test get_uniform_vec3(task, \"_vec3\") == Vector3f(1, 2, 3)\n\n        set_uniform_vec4!(task, \"_vec4\", Vector4f(1, 2, 3, 4))\n        @test get_uniform_vec4(task, \"_vec4\") == Vector4f(1, 2, 3, 4)\n\n        set_uniform_rgba!(task, \"_rgba\", RGBA(1, 0, 1, 1))\n        @test get_uniform_rgba(task, \"_rgba\") == RGBA(1, 0, 1, 1)\n\n        set_uniform_hsva!(task, \"_hsva\", HSVA(0.5, 0, 1, 1))\n        @test get_uniform_hsva(task, \"_hsva\") == HSVA(0.5, 0, 1, 1)\n\n        transform = GLTransform()\n        translate!(transform, Vector2f(0.5, 0.5))\n\n        set_uniform_transform!(task, \"_transform\", transform)\n        @test get_uniform_transform(task, \"_transform\") == transform\n\n        Base.show(devnull, task)\n    end\n\n    @testset \"TextureObject\" begin\n        image = Image(32, 32, RGBA(1, 0, 0, 1))\n        texture = Texture()\n        render_texture = RenderTexture()\n\n        for t in [texture, render_texture]\n            create!(t, 100, 100)\n            create_from_image!(t, image)\n\n            Mousetrap.download(texture) == image\n            Mousetrap.bind(t)\n            Mousetrap.unbind(t)\n\n            @test get_wrap_mode(t) == TEXTURE_WRAP_MODE_REPEAT\n            set_wrap_mode!(t, TEXTURE_WRAP_MODE_STRETCH)\n            @test get_wrap_mode(t) == TEXTURE_WRAP_MODE_STRETCH\n\n            @test get_scale_mode(t) == TEXTURE_SCALE_MODE_NEAREST\n            set_scale_mode!(t, TEXTURE_SCALE_MODE_LINEAR)\n            @test get_scale_mode(t) == TEXTURE_SCALE_MODE_LINEAR\n\n            @test get_size(t) == Vector2i(32, 32)\n            @test get_native_handle(t) != 0\n\n            Base.show(devnull, t)\n        end\n\n        bind_as_render_target(render_texture)\n        unbind_as_render_target(render_texture)\n    end\nend\n\n### MAIN\n\nmain(Main.app_id) do app::Application\n\n    #=\n    Main.app[] = app\n    Main.window[] = Window(app)\n    set_is_decorated!(window, false) # prevent user from closing the window during tests\n\n    theme = IconTheme(Main.window[])\n    Main.icon[] = Icon()\n\n    container = Container()\n    viewport = Viewport()\n    set_child!(viewport, container)\n    set_child!(window, viewport)\n\n    connect_signal_realize!(container, window) do container::Container, window\n        \n        temp = \"\"\"\n        test_action(container)\n        test_adjustment(container)\n        test_alert_dialog(container)\n        test_angle(container)\n        test_application(container)\n        test_aspect_frame(container)\n        test_box(container)\n        test_button(container)\n        test_center_box(container)\n        test_check_button(container)\n        test_clamp_frame(container)\n        test_clipboard(container)\n        test_color_chooser(container)\n        test_colors(container)\n        test_column_view(container)\n        test_drop_down(container)\n        test_entry(container)\n        test_event_controller(container)\n        test_expander(container)\n        test_file_chooser(container)\n        test_file_descriptor(container)\n        test_fixed(container)\n        test_frame(container)\n        test_flow_box(container)\n        test_gl_transform(container)\n        test_gl_area(container)\n        test_grid(container)\n        test_grid_view(container)\n        test_header_bar(container)\n        test_icon(container)\n        test_image(container)\n        test_image_display(container)\n        test_key_file(container)\n        test_label(container)\n        test_level_bar(container)\n        test_list_view(container)\n        test_log(container)\n        test_menus(container)\n        test_notebook(container)\n        test_overlay(container)\n        test_paned(container)\n        test_popup_message(container)\n        test_popover(container)\n        test_progress_bar(container)\n        test_revealer(container)\n        test_render_area(container)\n        test_scale(container)\n        test_scrollbar(container)\n        test_selection_model(container)\n        test_separator(container)\n        test_spin_button(container)\n        test_spinner(container)\n        test_stack(container)\n        test_switch(container)\n        test_text_view(container)\n        test_time(container)\n        test_toggle_button(container)\n        test_typed_function(container)\n        test_viewport(container)\n        test_widget(container)\n        test_window(container)\n        \"\"\"\n\n        return nothing\n    end\n\n    present!(window)\n    close!(window)\n    #quit!(app)\n    =#\n\n    window = Window(Main.app[])\n        Base.show(devnull, window)\n        @test Mousetrap.is_native_widget(window)\n\n        close_request_called = Ref{Bool}(false)\n        connect_signal_close_request!(window, close_request_called) do self::Window, close_request_called\n            close_request_called[] = true\n            return WINDOW_CLOSE_REQUEST_RESULT_ALLOW_CLOSE\n        end\n\n        activate_default_widget_called = Ref{Bool}(false)\n        connect_signal_activate_default_widget!(window, activate_default_widget_called) do self::Window, activate_default_widget_called\n            activate_default_widget_called[] = true\n            return nothing\n        end\n\n        activate_focused_widget_called = Ref{Bool}(false)\n        connect_signal_activate_focused_widget!(window, activate_focused_widget_called) do self::Window, activate_focused_widget_called\n            activate_focused_widget_called[] = true\n            return nothing\n        end\n\n        @test get_destroy_with_parent(window) == false\n        set_destroy_with_parent!(window, true)\n        @test get_destroy_with_parent(window) == true\n        \n        @test get_focus_visible(window) == true\n        set_focus_visible!(window, false)\n        @test get_focus_visible(window) == false\n\n        @test get_has_close_button(window) == true\n        set_has_close_button!(window, false)\n        @test get_has_close_button(window) == false\n\n        @test get_is_decorated(window) == true\n        set_is_decorated!(window, false)\n        @test get_is_decorated(window) == false\n\n        @test get_is_modal(window) == false\n        set_is_modal!(window, true)\n        @test get_is_modal(window) == true\n\n        set_title!(window, \"test\")\n        @test get_title(window) == \"test\"\n\n        button = Entry()\n        set_child!(window, button)\n        set_default_widget!(window, button)\n        activate!(button)\n\n        #@test activate_default_widget_called[] == true\n        #@test activate_focused_widget_called[] == true\n\n        @test get_header_bar(window) isa HeaderBar\n\n        @test get_hide_on_close(window) == false\n        set_hide_on_close!(window, true)\n        @test get_hide_on_close(window) == true\n\n        other_window = Window(app)\n        set_transient_for!(other_window, window)\n\n        @test get_is_closed(window) == true\n        present!(window)\n        @test get_is_closed(window) == false\n        set_minimized!(window, true)\n        set_maximized!(window, true)\n        close!(window)\n        @test get_is_closed(window) == true\n        destroy!(window)\n\n    println(\"TODO: done.\")\nend\n"
  }
]