Full Code of Clemapfel/mousetrap.jl for AI

main 3fe2e89d1361 cached
39 files
1.1 MB
305.2k tokens
1 requests
Download .txt
Showing preview only (1,181K chars total). Download the full file or copy to clipboard to get everything.
Repository: Clemapfel/mousetrap.jl
Branch: main
Commit: 3fe2e89d1361
Files: 39
Total size: 1.1 MB

Directory structure:
gitextract_bwbj8psc/

├── .github/
│   └── workflows/
│       └── CI.yml
├── .gitignore
├── Artifacts.toml
├── LICENSE
├── Manifest.toml
├── Project.toml
├── README.md
├── docs/
│   ├── make.jl
│   └── src/
│       ├── 01_manual/
│       │   ├── 01_installation.md
│       │   ├── 02_signals.md
│       │   ├── 03_actions.md
│       │   ├── 04_widgets.md
│       │   ├── 05_event_handling.md
│       │   ├── 06_image.md
│       │   ├── 07_os_interface.md
│       │   ├── 08_menus.md
│       │   ├── 09_native_rendering.md
│       │   ├── 10_theme_customization.md
│       │   ├── 11_app_distribution.md
│       │   └── 12_opengl_integration.md
│       ├── assets/
│       │   ├── animation_fade_out.webm
│       │   ├── animation_spin.webm
│       │   ├── css_style_animation_spin.webm
│       │   └── mousetrip.webm
│       └── index.md
├── jll/
│   ├── build_tarballs.jl
│   ├── build_tarballs.jl.in
│   └── deploy.jl
├── logo.xcf
├── src/
│   ├── Mousetrap.jl
│   ├── docgen/
│   │   ├── enums.jl
│   │   ├── functions.jl
│   │   ├── signals.jl
│   │   └── types.jl
│   ├── docs.jl
│   └── key_codes.jl
└── test/
    ├── example.jl
    ├── makie_test.jl
    └── runtests.jl

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/workflows/CI.yml
================================================
name: CI
on:
  push:
  pull_request:  
  
defaults:
  run:
    shell: bash
    
jobs:
    test:
      name: Julia ${{ matrix.julia-version }} ${{ matrix.os }}
      runs-on: ${{ matrix.os }}
      strategy:
        fail-fast: false
        matrix:
          julia-version:
            - "1.7"
            - "1.9"
          os:
            - ubuntu-latest
            - macos-latest
            - windows-latest
          julia-arch:
            - x64
          include:
            - os: ubuntu-latest
              prefix: xvfb-run
      steps: 
        - uses: actions/checkout@v4
        - uses: julia-actions/setup-julia@latest
          with:
            version: ${{ matrix.julia-version }}
            arch: ${{ matrix.julia-arch }}
            show-versioninfo: true
        - uses: julia-actions/julia-buildpkg@v1
        - uses: julia-actions/julia-runtest@v1
  
    docs:
      name: Documentation
      runs-on: windows-latest
      steps:
        - uses: actions/checkout@v4
        - uses: julia-actions/setup-julia@latest
          with:
            version: '1.7'
        - run: julia --project=docs -e '
            using Pkg;
            Pkg.develop(PackageSpec(; path=pwd()));
            Pkg.instantiate();
            Pkg.add(url=\"https://github.com/JuliaDocs/Documenter.jl\");'
        - run: julia --project=docs docs/make.jl
          env:
            GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
            DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }}


================================================
FILE: .gitignore
================================================

docs/build/
docs/mousetrap
docs/src/02_library/classes.md
docs/src/02_library/enums.md
docs/src/02_library/functions.md
jll/build
jll/products
.idea/


================================================
FILE: Artifacts.toml
================================================


================================================
FILE: LICENSE
================================================
                   GNU LESSER GENERAL PUBLIC LICENSE
                       Version 3, 29 June 2007

 Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.


  This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.

  0. Additional Definitions.

  As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.

  "The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.

  An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.

  A "Combined Work" is a work produced by combining or linking an
Application with the Library.  The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".

  The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.

  The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.

  1. Exception to Section 3 of the GNU GPL.

  You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.

  2. Conveying Modified Versions.

  If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:

   a) under this License, provided that you make a good faith effort to
   ensure that, in the event an Application does not supply the
   function or data, the facility still operates, and performs
   whatever part of its purpose remains meaningful, or

   b) under the GNU GPL, with none of the additional permissions of
   this License applicable to that copy.

  3. Object Code Incorporating Material from Library Header Files.

  The object code form of an Application may incorporate material from
a header file that is part of the Library.  You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:

   a) Give prominent notice with each copy of the object code that the
   Library is used in it and that the Library and its use are
   covered by this License.

   b) Accompany the object code with a copy of the GNU GPL and this license
   document.

  4. Combined Works.

  You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:

   a) Give prominent notice with each copy of the Combined Work that
   the Library is used in it and that the Library and its use are
   covered by this License.

   b) Accompany the Combined Work with a copy of the GNU GPL and this license
   document.

   c) For a Combined Work that displays copyright notices during
   execution, include the copyright notice for the Library among
   these notices, as well as a reference directing the user to the
   copies of the GNU GPL and this license document.

   d) Do one of the following:

       0) Convey the Minimal Corresponding Source under the terms of this
       License, and the Corresponding Application Code in a form
       suitable for, and under terms that permit, the user to
       recombine or relink the Application with a modified version of
       the Linked Version to produce a modified Combined Work, in the
       manner specified by section 6 of the GNU GPL for conveying
       Corresponding Source.

       1) Use a suitable shared library mechanism for linking with the
       Library.  A suitable mechanism is one that (a) uses at run time
       a copy of the Library already present on the user's computer
       system, and (b) will operate properly with a modified version
       of the Library that is interface-compatible with the Linked
       Version.

   e) Provide Installation Information, but only if you would otherwise
   be required to provide such information under section 6 of the
   GNU GPL, and only to the extent that such information is
   necessary to install and execute a modified version of the
   Combined Work produced by recombining or relinking the
   Application with a modified version of the Linked Version. (If
   you use option 4d0, the Installation Information must accompany
   the Minimal Corresponding Source and Corresponding Application
   Code. If you use option 4d1, you must provide the Installation
   Information in the manner specified by section 6 of the GNU GPL
   for conveying Corresponding Source.)

  5. Combined Libraries.

  You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:

   a) Accompany the combined library with a copy of the same work based
   on the Library, uncombined with any other library facilities,
   conveyed under the terms of this License.

   b) Give prominent notice with the combined library that part of it
   is a work based on the Library, and explaining where to find the
   accompanying uncombined form of the same work.

  6. Revised Versions of the GNU Lesser General Public License.

  The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.

  Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.

  If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.


================================================
FILE: Manifest.toml
================================================
# This file is machine-generated - editing it directly is not advised

julia_version = "1.9.3"
manifest_format = "2.0"
project_hash = "35df27af9c014d69e11708d80e596801030231d1"

[[deps.ANSIColoredPrinters]]
git-tree-sha1 = "574baf8110975760d391c710b6341da1afa48d8c"
uuid = "a4c015fc-c6ff-483c-b24f-f7ea428134e9"
version = "0.0.1"

[[deps.AbstractTrees]]
git-tree-sha1 = "faa260e4cb5aba097a73fab382dd4b5819d8ec8c"
uuid = "1520ce14-60c1-5f80-bbc7-55ef81b5835c"
version = "0.4.4"

[[deps.ArgParse]]
deps = ["Logging", "TextWrap"]
git-tree-sha1 = "3102bce13da501c9104df33549f511cd25264d7d"
uuid = "c7e460c6-2fb9-53a9-8c5b-16f535851c63"
version = "1.1.4"

[[deps.ArgTools]]
uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f"
version = "1.1.1"

[[deps.Artifacts]]
uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33"

[[deps.AssetRegistry]]
deps = ["Distributed", "JSON", "Pidfile", "SHA", "Test"]
git-tree-sha1 = "b25e88db7944f98789130d7b503276bc34bc098e"
uuid = "bf4720bc-e11a-5d0c-854e-bdca1663c893"
version = "0.1.0"

[[deps.Attr_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "b132f9aeb209b8790dcc286c857f300369219d8d"
uuid = "1fd713ca-387f-5abc-8002-d8b8b1623b73"
version = "2.5.1+0"

[[deps.AutoHashEquals]]
git-tree-sha1 = "45bb6705d93be619b81451bb2006b7ee5d4e4453"
uuid = "15f4f7f2-30c1-5605-9d31-71845cf9641f"
version = "0.2.0"

[[deps.Base64]]
uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"

[[deps.BinaryBuilder]]
deps = ["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"]
git-tree-sha1 = "d3e0f34be7db381be6a4b201f7d1b2f4212a4379"
uuid = "12aac903-9f7c-5d81-afc2-d9565ea332ae"
version = "0.5.6"

[[deps.BinaryBuilderBase]]
deps = ["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"]
git-tree-sha1 = "5d3967982f7f293703404d3adc3014bd54d2c580"
uuid = "7f725544-6523-48cd-82d1-3fa08ff4056e"
version = "1.25.0"

[[deps.BitFlags]]
git-tree-sha1 = "43b1a4a8f797c1cddadf60499a8a077d4af2cd2d"
uuid = "d1d4a3ce-64b1-5f1a-9ba4-7e7e69966f35"
version = "0.1.7"

[[deps.Bzip2_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "19a35467a82e236ff51bc17a3a44b69ef35185a2"
uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0"
version = "1.0.8+0"

[[deps.Cairo_jll]]
deps = ["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"]
git-tree-sha1 = "4b859a208b2397a7a623a03449e4636bdb17bcf2"
uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a"
version = "1.16.1+1"

[[deps.CodecZlib]]
deps = ["TranscodingStreams", "Zlib_jll"]
git-tree-sha1 = "02aa26a4cf76381be7f66e020a3eddeb27b0a092"
uuid = "944b1d66-785c-5afd-91f1-9de20f533193"
version = "0.7.2"

[[deps.Compat]]
deps = ["UUIDs"]
git-tree-sha1 = "8a62af3e248a8c4bad6b32cbbe663ae02275e32c"
uuid = "34da2185-b29b-5c13-b0c7-acf172513d20"
version = "4.10.0"
weakdeps = ["Dates", "LinearAlgebra"]

    [deps.Compat.extensions]
    CompatLinearAlgebraExt = "LinearAlgebra"

[[deps.CompilerSupportLibraries_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae"
version = "1.0.5+0"

[[deps.ConcurrentUtilities]]
deps = ["Serialization", "Sockets"]
git-tree-sha1 = "5372dbbf8f0bdb8c700db5367132925c0771ef7e"
uuid = "f0e56b4a-5159-44fe-b623-3e5288b988bb"
version = "2.2.1"

[[deps.CxxWrap]]
deps = ["Libdl", "MacroTools", "libcxxwrap_julia_jll"]
git-tree-sha1 = "67ebbc028b385658142d3c0644e07992616653d9"
uuid = "1f15a43c-97ca-5a2a-ae31-89f07a497df4"
version = "0.14.0"

[[deps.DataAPI]]
git-tree-sha1 = "8da84edb865b0b5b0100c0666a9bc9a0b71c553c"
uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a"
version = "1.15.0"

[[deps.DataValueInterfaces]]
git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6"
uuid = "e2d170a0-9d28-54be-80f0-106bbe20a464"
version = "1.0.0"

[[deps.Dates]]
deps = ["Printf"]
uuid = "ade2ca70-3891-5945-98fb-dc099432e06a"

[[deps.Distributed]]
deps = ["Random", "Serialization", "Sockets"]
uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b"

[[deps.DocStringExtensions]]
deps = ["LibGit2"]
git-tree-sha1 = "2fb1e02f2b635d0845df5d7c167fec4dd739b00d"
uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
version = "0.9.3"

[[deps.Documenter]]
deps = ["ANSIColoredPrinters", "AbstractTrees", "Base64", "Dates", "DocStringExtensions", "Downloads", "IOCapture", "InteractiveUtils", "JSON", "LibGit2", "Logging", "Markdown", "MarkdownAST", "Pkg", "PrecompileTools", "REPL", "RegistryInstances", "SHA", "Test", "Unicode"]
git-tree-sha1 = "f667b805e90d643aeb1ca70189827f991a7cc115"
uuid = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
version = "1.1.0"

[[deps.Downloads]]
deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"]
uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6"
version = "1.6.0"

[[deps.EpollShim_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl"]
git-tree-sha1 = "8e9441ee83492030ace98f9789a654a6d0b1f643"
uuid = "2702e6a9-849d-5ed8-8c21-79e8b8f9ee43"
version = "0.0.20230411+0"

[[deps.ExceptionUnwrapping]]
deps = ["Test"]
git-tree-sha1 = "e90caa41f5a86296e014e148ee061bd6c3edec96"
uuid = "460bff9d-24e4-43bc-9d9f-a8973cb893f4"
version = "0.1.9"

[[deps.Expat_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl"]
git-tree-sha1 = "4558ab818dcceaab612d1bb8c19cee87eda2b83c"
uuid = "2e619515-83b5-522b-bb60-26c02a35a201"
version = "2.5.0+0"

[[deps.ExprTools]]
git-tree-sha1 = "27415f162e6028e81c72b82ef756bf321213b6ec"
uuid = "e2ba6199-217a-4e67-a87a-7c52f15ade04"
version = "0.1.10"

[[deps.FileIO]]
deps = ["Pkg", "Requires", "UUIDs"]
git-tree-sha1 = "299dc33549f68299137e51e6d49a13b5b1da9673"
uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
version = "1.16.1"

[[deps.FileWatching]]
uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee"

[[deps.Fontconfig_jll]]
deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Pkg", "Zlib_jll"]
git-tree-sha1 = "21efd19106a55620a188615da6d3d06cd7f6ee03"
uuid = "a3f928ae-7b40-5064-980b-68af3947d34b"
version = "2.13.93+0"

[[deps.Formatting]]
deps = ["Printf"]
git-tree-sha1 = "8339d61043228fdd3eb658d86c926cb282ae72a8"
uuid = "59287772-0a20-5a39-b81b-1366585eb4c0"
version = "0.4.2"

[[deps.FreeType2_jll]]
deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Zlib_jll"]
git-tree-sha1 = "d8db6a5a2fe1381c1ea4ef2cab7c69c2de7f9ea0"
uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7"
version = "2.13.1+0"

[[deps.FriBidi_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "aa31987c2ba8704e23c6c8ba8a4f769d5d7e4f91"
uuid = "559328eb-81f9-559d-9380-de523a88c83c"
version = "1.0.10+0"

[[deps.GLEW_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Xorg_libXi_jll"]
git-tree-sha1 = "0333b4790daf4e20cdd61b79cae9b86e2b98f359"
uuid = "bde7f898-03f7-559e-8810-194d950ce600"
version = "2.2.0+0"

[[deps.GLU_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pkg"]
git-tree-sha1 = "65af046f4221e27fb79b28b6ca89dd1d12bc5ec7"
uuid = "bd17208b-e95e-5925-bf81-e2f59b3e5c61"
version = "9.0.1+0"

[[deps.GTK4_jll]]
deps = ["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"]
git-tree-sha1 = "e4f9ed37b104967acb37989984d34ad07f3a2d26"
uuid = "6ebb71f1-8434-552f-b6b1-dc18babcca63"
version = "4.10.5+0"

[[deps.Gettext_jll]]
deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "XML2_jll"]
git-tree-sha1 = "9b02998aba7bf074d14de89f9d37ca24a1a0b046"
uuid = "78b55507-aeef-58d4-861c-77aaff3498b1"
version = "0.21.0+0"

[[deps.GitForge]]
deps = ["Dates", "HTTP", "JSON3", "StructTypes", "TimeZones", "UUIDs"]
git-tree-sha1 = "79f1366c7130a92c3719b296f04e96fe90c26626"
uuid = "8f6bce27-0656-5410-875b-07a5572985df"
version = "0.4.2"

[[deps.GitHub]]
deps = ["Base64", "Dates", "HTTP", "JSON", "MbedTLS", "Sockets", "SodiumSeal", "URIs"]
git-tree-sha1 = "5688002de970b9eee14b7af7bbbd1fdac10c9bbe"
uuid = "bc5e4493-9b4d-5f90-b8aa-2b2bcaad7a26"
version = "5.8.2"

[[deps.Glib_jll]]
deps = ["Artifacts", "Gettext_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE2_jll", "Zlib_jll"]
git-tree-sha1 = "e94c92c7bf4819685eb80186d51c43e71d4afa17"
uuid = "7746bdde-850d-59dc-9ae8-88ece973131d"
version = "2.76.5+0"

[[deps.Graphene_jll]]
deps = ["Artifacts", "Glib_jll", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "61850a17f562453e3485a489c9c8cccb3abcab93"
uuid = "75302f13-0b7e-5bab-a6d1-23fa92e4c2ea"
version = "1.10.6+0"

[[deps.Graphite2_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "344bf40dcab1073aca04aa0df4fb092f920e4011"
uuid = "3b182d85-2403-5c21-9c21-1e1f0cc25472"
version = "1.3.14+0"

[[deps.Gzip_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "e7a48abe6d5ba74904df632160aa4486b0e80bf0"
uuid = "be1be57a-8558-53c3-a7e5-50095f79957e"
version = "1.12.0+0"

[[deps.HTTP]]
deps = ["Base64", "CodecZlib", "ConcurrentUtilities", "Dates", "ExceptionUnwrapping", "Logging", "LoggingExtras", "MbedTLS", "NetworkOptions", "OpenSSL", "Random", "SimpleBufferStream", "Sockets", "URIs", "UUIDs"]
git-tree-sha1 = "5eab648309e2e060198b45820af1a37182de3cce"
uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3"
version = "1.10.0"

[[deps.HarfBuzz_jll]]
deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "Graphite2_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg"]
git-tree-sha1 = "129acf094d168394e80ee1dc4bc06ec835e510a3"
uuid = "2e76f6c2-a576-52d4-95c1-20adfe4de566"
version = "2.8.1+1"

[[deps.Hiccup]]
deps = ["MacroTools", "Test"]
git-tree-sha1 = "6187bb2d5fcbb2007c39e7ac53308b0d371124bd"
uuid = "9fb69e20-1954-56bb-a84f-559cc56a8ff7"
version = "0.2.2"

[[deps.HistoricalStdlibVersions]]
git-tree-sha1 = "56ce882a06a846583e82c7c2c2d5194029eec232"
uuid = "6df8b67a-e8a0-4029-b4b7-ac196fe72102"
version = "1.2.1"

[[deps.IOCapture]]
deps = ["Logging", "Random"]
git-tree-sha1 = "d75853a0bdbfb1ac815478bacd89cd27b550ace6"
uuid = "b5f81e59-6552-4d32-b1f0-c071b021bf89"
version = "0.2.3"

[[deps.InlineStrings]]
deps = ["Parsers"]
git-tree-sha1 = "9cc2baf75c6d09f9da536ddf58eb2f29dedaf461"
uuid = "842dd82b-1e85-43dc-bf29-5d0ee9dffc48"
version = "1.4.0"

[[deps.InteractiveUtils]]
deps = ["Markdown"]
uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240"

[[deps.IteratorInterfaceExtensions]]
git-tree-sha1 = "a3f24677c21f5bbe9d2a714f95dcd58337fb2856"
uuid = "82899510-4779-5014-852e-03e436cf321d"
version = "1.0.0"

[[deps.JLD2]]
deps = ["FileIO", "MacroTools", "Mmap", "OrderedCollections", "Pkg", "Printf", "Reexport", "Requires", "TranscodingStreams", "UUIDs"]
git-tree-sha1 = "c11d691a0dc8e90acfa4740d293ade57f68bfdbb"
uuid = "033835bb-8acc-5ee8-8aae-3f567f8a3819"
version = "0.4.35"

[[deps.JLLWrappers]]
deps = ["Artifacts", "Preferences"]
git-tree-sha1 = "7e5d6779a1e09a36db2a7b6cff50942a0a7d0fca"
uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210"
version = "1.5.0"

[[deps.JSON]]
deps = ["Dates", "Mmap", "Parsers", "Unicode"]
git-tree-sha1 = "31e996f0a15c7b280ba9f76636b3ff9e2ae58c9a"
uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
version = "0.21.4"

[[deps.JSON3]]
deps = ["Dates", "Mmap", "Parsers", "PrecompileTools", "StructTypes", "UUIDs"]
git-tree-sha1 = "95220473901735a0f4df9d1ca5b171b568b2daa3"
uuid = "0f8b85d8-7281-11e9-16c2-39a750bddbf1"
version = "1.13.2"

[[deps.JpegTurbo_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl"]
git-tree-sha1 = "6f2675ef130a300a112286de91973805fcc5ffbc"
uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8"
version = "2.1.91+0"

[[deps.LERC_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "bf36f528eec6634efc60d7ec062008f171071434"
uuid = "88015f11-f218-50d7-93a8-a6af411a945d"
version = "3.0.0+1"

[[deps.LLVMOpenMP_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "f689897ccbe049adb19a065c495e75f372ecd42b"
uuid = "1d63c593-3942-5779-bab2-d838dc0a180e"
version = "15.0.4+0"

[[deps.LZO_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "e5b909bcf985c5e2605737d2ce278ed791b89be6"
uuid = "dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac"
version = "2.10.1+0"

[[deps.LaTeXStrings]]
git-tree-sha1 = "f2355693d6778a178ade15952b7ac47a4ff97996"
uuid = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f"
version = "1.3.0"

[[deps.Latexify]]
deps = ["Formatting", "InteractiveUtils", "LaTeXStrings", "MacroTools", "Markdown", "OrderedCollections", "Printf", "Requires"]
git-tree-sha1 = "f428ae552340899a935973270b8d98e5a31c49fe"
uuid = "23fbe1c1-3f47-55db-b15f-69d7ec21a316"
version = "0.16.1"

    [deps.Latexify.extensions]
    DataFramesExt = "DataFrames"
    SymEngineExt = "SymEngine"

    [deps.Latexify.weakdeps]
    DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
    SymEngine = "123dc426-2d89-5057-bbad-38513e3affd8"

[[deps.LazilyInitializedFields]]
git-tree-sha1 = "410fe4739a4b092f2ffe36fcb0dcc3ab12648ce1"
uuid = "0e77f7df-68c5-4e49-93ce-4cd80f5598bf"
version = "1.2.1"

[[deps.LazyArtifacts]]
deps = ["Artifacts", "Pkg"]
uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3"

[[deps.LibCURL]]
deps = ["LibCURL_jll", "MozillaCACerts_jll"]
uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21"
version = "0.6.3"

[[deps.LibCURL_jll]]
deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"]
uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0"
version = "7.84.0+0"

[[deps.LibGit2]]
deps = ["Base64", "NetworkOptions", "Printf", "SHA"]
uuid = "76f85450-5226-5b5a-8eaa-529ad045b433"

[[deps.LibGit2_jll]]
deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll"]
uuid = "e37daf67-58a4-590a-8e99-b0245dd2ffc5"
version = "1.5.0+1"

[[deps.LibSSH2_jll]]
deps = ["Artifacts", "Libdl", "MbedTLS_jll"]
uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8"
version = "1.10.2+0"

[[deps.Libdl]]
uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb"

[[deps.Libepoxy_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pkg", "Xorg_libX11_jll"]
git-tree-sha1 = "7a0158b71f8be5c771e7a273183b2d0ac35278c5"
uuid = "42c93a91-0102-5b3f-8f9d-e41de60ac950"
version = "1.5.10+0"

[[deps.Libffi_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "0b4a5d71f3e5200a7dff793393e09dfc2d874290"
uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490"
version = "3.2.2+1"

[[deps.Libgcrypt_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll", "Pkg"]
git-tree-sha1 = "64613c82a59c120435c067c2b809fc61cf5166ae"
uuid = "d4300ac3-e22c-5743-9152-c294e39db1e4"
version = "1.8.7+0"

[[deps.Libglvnd_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll"]
git-tree-sha1 = "6f73d1dd803986947b2c750138528a999a6c7733"
uuid = "7e76a0d4-f3c7-5321-8279-8d96eeed0f29"
version = "1.6.0+0"

[[deps.Libgpg_error_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "c333716e46366857753e273ce6a69ee0945a6db9"
uuid = "7add5ba3-2f88-524e-9cd5-f83b8a55f7b8"
version = "1.42.0+0"

[[deps.Libiconv_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl"]
git-tree-sha1 = "f9557a255370125b405568f9767d6d195822a175"
uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531"
version = "1.17.0+0"

[[deps.Libmount_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "9c30530bf0effd46e15e0fdcf2b8636e78cbbd73"
uuid = "4b2f31a3-9ecc-558c-b454-b3730dcb73e9"
version = "2.35.0+0"

[[deps.Libtiff_jll]]
deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "LERC_jll", "Libdl", "Pkg", "Zlib_jll", "Zstd_jll"]
git-tree-sha1 = "3eb79b0ca5764d4799c06699573fd8f533259713"
uuid = "89763e89-9b03-5906-acba-b20f662cd828"
version = "4.4.0+0"

[[deps.Libuuid_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "7f3efec06033682db852f8b3bc3c1d2b0a0ab066"
uuid = "38a345b3-de98-5d2b-a5d3-14cd9215e700"
version = "2.36.0+0"

[[deps.LinearAlgebra]]
deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"]
uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"

[[deps.Logging]]
uuid = "56ddb016-857b-54e1-b83d-db4d58db5568"

[[deps.LoggingExtras]]
deps = ["Dates", "Logging"]
git-tree-sha1 = "5d4d2d9904227b8bd66386c1138cf4d5ffa826bf"
uuid = "e6f89c97-d47a-5376-807f-9c37f3926c36"
version = "0.4.9"

[[deps.MacroTools]]
deps = ["Markdown", "Random"]
git-tree-sha1 = "9ee1618cbf5240e6d4e0371d6f24065083f60c48"
uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09"
version = "0.5.11"

[[deps.Markdown]]
deps = ["Base64"]
uuid = "d6f4376e-aef5-505a-96c1-9c027394607a"

[[deps.MarkdownAST]]
deps = ["AbstractTrees", "Markdown"]
git-tree-sha1 = "e8513266815200c0c8f522d6d44ffb5e9b366ae4"
uuid = "d0879d2d-cac2-40c8-9cee-1863dc0c7391"
version = "0.1.1"

[[deps.MbedTLS]]
deps = ["Dates", "MbedTLS_jll", "MozillaCACerts_jll", "Random", "Sockets"]
git-tree-sha1 = "03a9b9718f5682ecb107ac9f7308991db4ce395b"
uuid = "739be429-bea8-5141-9913-cc70e7f3736d"
version = "1.1.7"

[[deps.MbedTLS_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1"
version = "2.28.2+0"

[[deps.Mmap]]
uuid = "a63ad114-7e13-5084-954f-fe012c677804"

[[deps.Mocking]]
deps = ["Compat", "ExprTools"]
git-tree-sha1 = "4cc0c5a83933648b615c36c2b956d94fda70641e"
uuid = "78c3b35d-d492-501b-9361-3d52fe80e533"
version = "0.7.7"

[[deps.MozillaCACerts_jll]]
uuid = "14a3606d-f60d-562e-9121-12d972cd8159"
version = "2022.10.11"

[[deps.Mustache]]
deps = ["Printf", "Tables"]
git-tree-sha1 = "821e918c170ead5298ff84bffee41dd28929a681"
uuid = "ffc61752-8dc7-55ee-8c37-f3e9cdd09e70"
version = "1.0.17"

[[deps.Mux]]
deps = ["AssetRegistry", "Base64", "HTTP", "Hiccup", "MbedTLS", "Pkg", "Sockets"]
git-tree-sha1 = "0bdaa479939d2a1f85e2f93e38fbccfcb73175a5"
uuid = "a975b10e-0019-58db-a62f-e48ff68538c9"
version = "1.0.1"

[[deps.NetworkOptions]]
uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908"
version = "1.2.0"

[[deps.ObjectFile]]
deps = ["Reexport", "StructIO"]
git-tree-sha1 = "55ce61d43409b1fb0279d1781bf3b0f22c83ab3b"
uuid = "d8793406-e978-5875-9003-1fc021f44a92"
version = "0.3.7"

[[deps.OpenBLAS_jll]]
deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"]
uuid = "4536629a-c528-5b80-bd46-f80d51c5b363"
version = "0.3.21+4"

[[deps.OpenGLMathematics_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "909a33f40ec4bc2ecc9fbc9379c1314bd480a48f"
uuid = "cc7be9be-d298-5888-8f50-b85d5f9d6d73"
version = "0.9.9+0"

[[deps.OpenSSL]]
deps = ["BitFlags", "Dates", "MozillaCACerts_jll", "OpenSSL_jll", "Sockets"]
git-tree-sha1 = "51901a49222b09e3743c65b8847687ae5fc78eb2"
uuid = "4d8831e6-92b7-49fb-bdf8-b643e874388c"
version = "1.4.1"

[[deps.OpenSSL_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl"]
git-tree-sha1 = "ceeda72c9fd6bbebc4f4f598560789145a8b6c4c"
uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95"
version = "3.0.11+0"

[[deps.OrderedCollections]]
git-tree-sha1 = "2e73fe17cac3c62ad1aebe70d44c963c3cfdc3e3"
uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
version = "1.6.2"

[[deps.OutputCollectors]]
git-tree-sha1 = "5d3f2b3b2e2a9d7d6f1774c78e94530ac7f360cc"
uuid = "6c11c7d4-943b-4e2b-80de-f2cfc2930a8c"
version = "0.1.1"

[[deps.PCRE2_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "efcefdf7-47ab-520b-bdef-62a2eaa19f15"
version = "10.42.0+0"

[[deps.Pango_jll]]
deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "FriBidi_jll", "Glib_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl"]
git-tree-sha1 = "4745216e94f71cb768d58330b059c9b76f32cb66"
uuid = "36c8627f-9965-5494-a995-c6b170f724f3"
version = "1.50.14+0"

[[deps.Parsers]]
deps = ["Dates", "PrecompileTools", "UUIDs"]
git-tree-sha1 = "716e24b21538abc91f6205fd1d8363f39b442851"
uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0"
version = "2.7.2"

[[deps.Pidfile]]
deps = ["FileWatching", "Test"]
git-tree-sha1 = "2d8aaf8ee10df53d0dfb9b8ee44ae7c04ced2b03"
uuid = "fa939f87-e72e-5be4-a000-7fc836dbe307"
version = "1.3.0"

[[deps.Pixman_jll]]
deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LLVMOpenMP_jll", "Libdl"]
git-tree-sha1 = "64779bc4c9784fee475689a1752ef4d5747c5e87"
uuid = "30392449-352a-5448-841d-b1acce4e97dc"
version = "0.42.2+0"

[[deps.Pkg]]
deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"]
uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
version = "1.9.2"

[[deps.PkgLicenses]]
deps = ["Test"]
git-tree-sha1 = "0af826be249c6751a3e783c07b8cd3034f508943"
uuid = "fc669557-7ec9-5e45-bca9-462afbc28879"
version = "0.2.0"

[[deps.PrecompileTools]]
deps = ["Preferences"]
git-tree-sha1 = "03b4c25b43cb84cee5c90aa9b5ea0a78fd848d2f"
uuid = "aea7be01-6a6a-4083-8856-8a6e6704d82a"
version = "1.2.0"

[[deps.Preferences]]
deps = ["TOML"]
git-tree-sha1 = "00805cd429dcb4870060ff49ef443486c262e38e"
uuid = "21216c6a-2e73-6563-6e65-726566657250"
version = "1.4.1"

[[deps.Printf]]
deps = ["Unicode"]
uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7"

[[deps.ProgressMeter]]
deps = ["Distributed", "Printf"]
git-tree-sha1 = "00099623ffee15972c16111bcf84c58a0051257c"
uuid = "92933f4c-e287-5a05-a399-4b506db050ca"
version = "1.9.0"

[[deps.REPL]]
deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"]
uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"

[[deps.Random]]
deps = ["SHA", "Serialization"]
uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"

[[deps.Reexport]]
git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b"
uuid = "189a3867-3050-52da-a836-e630ba90ab69"
version = "1.2.2"

[[deps.Registrator]]
deps = ["AutoHashEquals", "Base64", "Dates", "Distributed", "FileWatching", "GitForge", "GitHub", "HTTP", "JSON", "LibGit2", "Logging", "MbedTLS", "Mocking", "Mustache", "Mux", "Pkg", "RegistryTools", "Serialization", "Sockets", "TimeToLive", "URIs", "UUIDs", "ZMQ"]
git-tree-sha1 = "64a7d49e56cf609973854cdd48eb265ac418f6ee"
uuid = "4418983a-e44d-11e8-3aec-9789530b3b3e"
version = "1.6.0"

[[deps.RegistryInstances]]
deps = ["LazilyInitializedFields", "Pkg", "TOML", "Tar"]
git-tree-sha1 = "ffd19052caf598b8653b99404058fce14828be51"
uuid = "2792f1a3-b283-48e8-9a74-f99dce5104f3"
version = "0.1.0"

[[deps.RegistryTools]]
deps = ["AutoHashEquals", "LibGit2", "Pkg", "SHA", "UUIDs"]
git-tree-sha1 = "47ab54eff26db6be2496e6300d959e16d8203723"
uuid = "d1eb7eb1-105f-429d-abf5-b0f65cb9e2c4"
version = "1.9.1"

[[deps.Requires]]
deps = ["UUIDs"]
git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7"
uuid = "ae029012-a4dd-5104-9daa-d747884805df"
version = "1.3.0"

[[deps.SHA]]
uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce"
version = "0.7.0"

[[deps.Scratch]]
deps = ["Dates"]
git-tree-sha1 = "30449ee12237627992a99d5e30ae63e4d78cd24a"
uuid = "6c6a2e73-6563-6170-7368-637461726353"
version = "1.2.0"

[[deps.Serialization]]
uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b"

[[deps.SimpleBufferStream]]
git-tree-sha1 = "874e8867b33a00e784c8a7e4b60afe9e037b74e1"
uuid = "777ac1f9-54b0-4bf8-805c-2214025038e7"
version = "1.1.0"

[[deps.Sockets]]
uuid = "6462fe0b-24de-5631-8697-dd941f90decc"

[[deps.SodiumSeal]]
deps = ["Base64", "Libdl", "libsodium_jll"]
git-tree-sha1 = "80cef67d2953e33935b41c6ab0a178b9987b1c99"
uuid = "2133526b-2bfb-4018-ac12-889fb3908a75"
version = "0.1.1"

[[deps.StructIO]]
deps = ["Test"]
git-tree-sha1 = "010dc73c7146869c042b49adcdb6bf528c12e859"
uuid = "53d494c1-5632-5724-8f4c-31dff12d585f"
version = "0.3.0"

[[deps.StructTypes]]
deps = ["Dates", "UUIDs"]
git-tree-sha1 = "ca4bccb03acf9faaf4137a9abc1881ed1841aa70"
uuid = "856f2bd8-1eba-4b0a-8007-ebc267875bd4"
version = "1.10.0"

[[deps.TOML]]
deps = ["Dates"]
uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76"
version = "1.0.3"

[[deps.TZJData]]
deps = ["Artifacts"]
git-tree-sha1 = "d39314cdbaf5b90a047db33858626f8d1cc973e1"
uuid = "dc5dba14-91b3-4cab-a142-028a31da12f7"
version = "1.0.0+2023c"

[[deps.TableTraits]]
deps = ["IteratorInterfaceExtensions"]
git-tree-sha1 = "c06b2f539df1c6efa794486abfb6ed2022561a39"
uuid = "3783bdb8-4a98-5b6b-af9a-565f29a5fe9c"
version = "1.0.1"

[[deps.Tables]]
deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "LinearAlgebra", "OrderedCollections", "TableTraits"]
git-tree-sha1 = "a1f34829d5ac0ef499f6d84428bd6b4c71f02ead"
uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"
version = "1.11.0"

[[deps.Tar]]
deps = ["ArgTools", "SHA"]
uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e"
version = "1.10.0"

[[deps.Tar_jll]]
deps = ["Artifacts", "Attr_jll", "JLLWrappers", "Libdl", "Libiconv_jll"]
git-tree-sha1 = "85e7d0ef5248971fbd824f29c52ab6168b895dfd"
uuid = "9b64493d-8859-5bf3-93d7-7c32dd38186f"
version = "1.35.0+0"

[[deps.Test]]
deps = ["InteractiveUtils", "Logging", "Random", "Serialization"]
uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[[deps.TextWrap]]
git-tree-sha1 = "9250ef9b01b66667380cf3275b3f7488d0e25faf"
uuid = "b718987f-49a8-5099-9789-dcd902bef87d"
version = "1.0.1"

[[deps.TimeToLive]]
deps = ["Dates"]
git-tree-sha1 = "1f1389007d16385ec02e497bef6c2caffba99b65"
uuid = "37f0c46e-897f-50ef-b453-b26c3eed3d6c"
version = "0.3.0"

[[deps.TimeZones]]
deps = ["Artifacts", "Dates", "Downloads", "InlineStrings", "LazyArtifacts", "Mocking", "Printf", "Scratch", "TZJData", "Unicode", "p7zip_jll"]
git-tree-sha1 = "89e64d61ef3cd9e80f7fc12b7d13db2d75a23c03"
uuid = "f269a46b-ccf7-5d73-abea-4c690281aa53"
version = "1.13.0"

    [deps.TimeZones.extensions]
    TimeZonesRecipesBaseExt = "RecipesBase"

    [deps.TimeZones.weakdeps]
    RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"

[[deps.TranscodingStreams]]
deps = ["Random", "Test"]
git-tree-sha1 = "9a6ae7ed916312b41236fcef7e0af564ef934769"
uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa"
version = "0.9.13"

[[deps.URIs]]
git-tree-sha1 = "b7a5e99f24892b6824a954199a45e9ffcc1c70f0"
uuid = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4"
version = "1.5.0"

[[deps.UUIDs]]
deps = ["Random", "SHA"]
uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"

[[deps.Unicode]]
uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5"

[[deps.Wayland_jll]]
deps = ["Artifacts", "EpollShim_jll", "Expat_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg", "XML2_jll"]
git-tree-sha1 = "7558e29847e99bc3f04d6569e82d0f5c54460703"
uuid = "a2964d1f-97da-50d4-b82a-358c7fce9d89"
version = "1.21.0+1"

[[deps.Wayland_protocols_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "4528479aa01ee1b3b4cd0e6faef0e04cf16466da"
uuid = "2381bf8a-dfd0-557d-9999-79630e7b1b91"
version = "1.25.0+0"

[[deps.XML2_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Zlib_jll"]
git-tree-sha1 = "24b81b59bd35b3c42ab84fa589086e19be919916"
uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a"
version = "2.11.5+0"

[[deps.XSLT_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Libiconv_jll", "Pkg", "XML2_jll", "Zlib_jll"]
git-tree-sha1 = "91844873c4085240b95e795f692c4cec4d805f8a"
uuid = "aed1982a-8fda-507f-9586-7b0439959a61"
version = "1.1.34+0"

[[deps.XZ_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl"]
git-tree-sha1 = "cf2c7de82431ca6f39250d2fc4aacd0daa1675c0"
uuid = "ffd25f8a-64ca-5728-b0f7-c24cf3aae800"
version = "5.4.4+0"

[[deps.Xorg_libX11_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxcb_jll", "Xorg_xtrans_jll"]
git-tree-sha1 = "afead5aba5aa507ad5a3bf01f58f82c8d1403495"
uuid = "4f6342f7-b3d2-589e-9d20-edeb45f2b2bc"
version = "1.8.6+0"

[[deps.Xorg_libXau_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl"]
git-tree-sha1 = "6035850dcc70518ca32f012e46015b9beeda49d8"
uuid = "0c0b7dd1-d40b-584c-a123-a41640f87eec"
version = "1.0.11+0"

[[deps.Xorg_libXcursor_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXfixes_jll", "Xorg_libXrender_jll"]
git-tree-sha1 = "12e0eb3bc634fa2080c1c37fccf56f7c22989afd"
uuid = "935fb764-8cf2-53bf-bb30-45bb1f8bf724"
version = "1.2.0+4"

[[deps.Xorg_libXdamage_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXfixes_jll"]
git-tree-sha1 = "fe4ffb2024ba3eddc862c6e1d70e2b070cd1c2bf"
uuid = "0aeada51-83db-5f97-b67e-184615cfc6f6"
version = "1.1.5+4"

[[deps.Xorg_libXdmcp_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl"]
git-tree-sha1 = "34d526d318358a859d7de23da945578e8e8727b7"
uuid = "a3789734-cfe1-5b06-b2d0-1dd0d9d62d05"
version = "1.1.4+0"

[[deps.Xorg_libXext_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"]
git-tree-sha1 = "b7c0aa8c376b31e4852b360222848637f481f8c3"
uuid = "1082639a-0dae-5f34-9b06-72781eeb8cb3"
version = "1.3.4+4"

[[deps.Xorg_libXfixes_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"]
git-tree-sha1 = "0e0dc7431e7a0587559f9294aeec269471c991a4"
uuid = "d091e8ba-531a-589c-9de9-94069b037ed8"
version = "5.0.3+4"

[[deps.Xorg_libXi_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll", "Xorg_libXfixes_jll"]
git-tree-sha1 = "89b52bc2160aadc84d707093930ef0bffa641246"
uuid = "a51aa0fd-4e3c-5386-b890-e753decda492"
version = "1.7.10+4"

[[deps.Xorg_libXinerama_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll"]
git-tree-sha1 = "26be8b1c342929259317d8b9f7b53bf2bb73b123"
uuid = "d1454406-59df-5ea1-beac-c340f2130bc3"
version = "1.1.4+4"

[[deps.Xorg_libXrandr_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll", "Xorg_libXrender_jll"]
git-tree-sha1 = "34cea83cb726fb58f325887bf0612c6b3fb17631"
uuid = "ec84b674-ba8e-5d96-8ba1-2a689ba10484"
version = "1.5.2+4"

[[deps.Xorg_libXrender_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"]
git-tree-sha1 = "19560f30fd49f4d4efbe7002a1037f8c43d43b96"
uuid = "ea2f1a96-1ddc-540d-b46f-429655e07cfa"
version = "0.9.10+4"

[[deps.Xorg_libpthread_stubs_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl"]
git-tree-sha1 = "8fdda4c692503d44d04a0603d9ac0982054635f9"
uuid = "14d82f49-176c-5ed1-bb49-ad3f5cbd8c74"
version = "0.1.1+0"

[[deps.Xorg_libxcb_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "XSLT_jll", "Xorg_libXau_jll", "Xorg_libXdmcp_jll", "Xorg_libpthread_stubs_jll"]
git-tree-sha1 = "b4bfde5d5b652e22b9c790ad00af08b6d042b97d"
uuid = "c7cfdc94-dc32-55de-ac96-5a1b8d977c5b"
version = "1.15.0+0"

[[deps.Xorg_libxkbfile_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"]
git-tree-sha1 = "730eeca102434283c50ccf7d1ecdadf521a765a4"
uuid = "cc61e674-0454-545c-8b26-ed2c68acab7a"
version = "1.1.2+0"

[[deps.Xorg_xkbcomp_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxkbfile_jll"]
git-tree-sha1 = "330f955bc41bb8f5270a369c473fc4a5a4e4d3cb"
uuid = "35661453-b289-5fab-8a00-3d9160c6a3a4"
version = "1.4.6+0"

[[deps.Xorg_xkeyboard_config_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xkbcomp_jll"]
git-tree-sha1 = "691634e5453ad362044e2ad653e79f3ee3bb98c3"
uuid = "33bec58e-1273-512f-9401-5d533626f822"
version = "2.39.0+0"

[[deps.Xorg_xtrans_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl"]
git-tree-sha1 = "e92a1a012a10506618f10b7047e478403a046c77"
uuid = "c5fb5394-a638-5e4d-96e5-b29de1b5cf10"
version = "1.5.0+0"

[[deps.ZMQ]]
deps = ["FileWatching", "Sockets", "ZeroMQ_jll"]
git-tree-sha1 = "356d2bdcc0bce90aabee1d1c0f6d6f301eda8f77"
uuid = "c2297ded-f4af-51ae-bb23-16f91089e4e1"
version = "1.2.2"

[[deps.ZeroMQ_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "libsodium_jll"]
git-tree-sha1 = "fe5c65a526f066fb3000da137d5785d9649a8a47"
uuid = "8f1865be-045e-5c20-9c9f-bfbfb0764568"
version = "4.3.4+0"

[[deps.Zlib_jll]]
deps = ["Libdl"]
uuid = "83775a58-1f1d-513f-b197-d71354ab007a"
version = "1.2.13+0"

[[deps.Zstd_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl"]
git-tree-sha1 = "49ce682769cd5de6c72dcf1b94ed7790cd08974c"
uuid = "3161d3a3-bdf6-5164-811a-617609db77b4"
version = "1.5.5+0"

[[deps.gdk_pixbuf_jll]]
deps = ["Artifacts", "Glib_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libtiff_jll", "Pkg", "Xorg_libX11_jll", "libpng_jll"]
git-tree-sha1 = "e9190f9fb03f9c3b15b9fb0c380b0d57a3c8ea39"
uuid = "da03df04-f53b-5353-a52f-6a8b0620ced0"
version = "2.42.8+0"

[[deps.ghr_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "a83b3feeda837dd3f3cad19076bda0f0a524d687"
uuid = "07c12ed4-43bc-5495-8a2a-d5838ef8d533"
version = "0.14.0+0"

[[deps.iso_codes_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "51559b9071db7e363047a34f658d495843ccd35c"
uuid = "bf975903-5238-5d20-8243-bc370bc1e7e5"
version = "4.11.0+0"

[[deps.libadwaita_jll]]
deps = ["Artifacts", "GTK4_jll", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "51352620ef59bc200289a398fbc65c587f0034d7"
uuid = "583852a3-1c13-5035-b52b-3b742a7b3316"
version = "1.2.0+0"

[[deps.libblastrampoline_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "8e850b90-86db-534c-a0d3-1478176c7d93"
version = "5.8.0+0"

[[deps.libcxxwrap_julia_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl"]
git-tree-sha1 = "d2058c04963d424ae69f95460fc34fdfce4dc0cf"
uuid = "3eaa8342-bff7-56a5-9981-c04077f7cee7"
version = "0.11.0+1"

[[deps.libpng_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"]
git-tree-sha1 = "94d180a6d2b5e55e447e2d27a29ed04fe79eb30c"
uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f"
version = "1.6.38+0"

[[deps.libsodium_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "848ab3d00fe39d6fbc2a8641048f8f272af1c51e"
uuid = "a9144af2-ca23-56d9-984f-0d03f7b5ccf8"
version = "1.0.20+0"

[[deps.mousetrap_jll]]
deps = ["Artifacts", "GLEW_jll", "GLU_jll", "GTK4_jll", "JLLWrappers", "Libdl", "OpenGLMathematics_jll", "libadwaita_jll", "libcxxwrap_julia_jll"]
git-tree-sha1 = "6e5d87111686530feb339af45210317f56c27a82"
uuid = "0e90efc8-2bbd-550f-bf3c-306a2edaaeef"
version = "0.3.0+0"

[[deps.nghttp2_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d"
version = "1.48.0+0"

[[deps.p7zip_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0"
version = "17.4.0+0"

[[deps.pigz_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"]
git-tree-sha1 = "3c0c0b0c133b6ab53e1af05dc526091ce8781f16"
uuid = "1bc43ea1-30af-5bc8-a9d4-c018457e6e3e"
version = "2.7.0+0"

[[deps.xkbcommon_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Wayland_jll", "Wayland_protocols_jll", "Xorg_libxcb_jll", "Xorg_xkeyboard_config_jll"]
git-tree-sha1 = "9c304562909ab2bab0262639bd4f444d7bc2be37"
uuid = "d8fb68d0-12a3-5cfd-a85a-d49703b185fd"
version = "1.4.1+1"


================================================
FILE: Project.toml
================================================
name = "Mousetrap"
uuid = "5deeb4b9-6e04-4da7-8b7f-c77fb1eae65e"
authors = ["C.Cords <mail@clemens-cords.com>"]
version = "0.3.1"

[deps]
BinaryBuilder = "12aac903-9f7c-5d81-afc2-d9565ea332ae"
CxxWrap = "1f15a43c-97ca-5a2a-ae31-89f07a497df4"
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
GTK4_jll = "6ebb71f1-8434-552f-b6b1-dc18babcca63"
Glib_jll = "7746bdde-850d-59dc-9ae8-88ece973131d"
InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
Latexify = "23fbe1c1-3f47-55db-b15f-69d7ec21a316"
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
libadwaita_jll = "583852a3-1c13-5035-b52b-3b742a7b3316"
libcxxwrap_julia_jll = "3eaa8342-bff7-56a5-9981-c04077f7cee7"
mousetrap_jll = "0e90efc8-2bbd-550f-bf3c-306a2edaaeef"

[compat]
CxxWrap = "0.14.0"
mousetrap_jll = "0.3.0"
GTK4_jll = "4.10"
Glib_jll = "2.76"

================================================
FILE: README.md
================================================
# Mousetrap

![](docs/src/assets/banner.png)

Mousetrap 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.

It 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.

> **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.

> **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.
---

## Table of Contents
0. [Introduction](https://github.com/Clemapfel/mousetrap.jl)<br>
1. [Features](#features)<br>
2. [Planned Features](#planned-features)<br>
3. [Showcase](#showcase)<br>
3.1 [Hello World](#hello-world)<br>
3.2 [Swapping between Light- and Dark Themes](#swapping-between-light--and-dark-themes)<br>
3.3 [Opening a File Explorer Dialog](#opening-a-file-explorer-dialog)<br>
3.4 [Rendering a Rectangle using OpenGL](#rendering-a-rectangle-with-opengl)<br>
3.5 [Displaying a GLMakie Plot in a Mousetrap Window](#displaying-a-glmakie-plot-in-a-mousetrap-window)<br>
4. [Supported Platforms](#supported-platforms)<br>
6. [Documentation](#documentation)<br>
5. [Installation](#installation)<br>
7. [Credits & Donations](#credits--donations)<br>
8. [License](#license)<br>

---

## Features
+ Create complex GUI application for Linux, Windows, and macOS
+ Choose from over 40 different kinds of pre-made widgets, or create your own
+ Supports mice, keyboards, touchscreens, touchpads, and stylus devices
+ Image processing facilities, well-suited for image manipulation programs
+ 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)
+ [Hand-written manual and extensive documentation](https://clemens-cords.com/mousetrap): every exported symbol is documented

---

## Planned Features

In order of priority, highest first:

+ Allow bundling of Mousetrap apps using [`PackageCompiler.jl`](https://github.com/JuliaLang/PackageCompiler.jl)
+ Implement installation of .desktop files on end-user computers
+ Implement drag-and-drop for files, images, and widgets
+ Allow filtering and searching of selectable widget containers such as `ListView` and `ColumnView`
+ Allow adding custom signals that use the GLib marshalling system
+ Make all functions that modify the global state thread-safe

---

## Showcase

### Hello World

```julia
using Mousetrap
main() do app::Application
    window = Window(app)
    set_child!(window, Label("Hello World!"))
    present!(window)
end
```
![](docs/src/assets/readme_hello_world.png)

---

### Swapping between Light- and Dark Themes

```julia
set_current_theme!(app, THEME_DEFAULT_LIGHT) 
```
![](docs/src/assets/light_dark_theme.png)

---

### Opening a File Explorer Dialog

```julia
file_chooser = FileChooser()
on_accept!(file_chooser) do self::FileChooser, files
    println("selected files: $files")
end
present!(file_chooser)
```
![](docs/src/assets/readme_file_chooser.png)

---

### Rendering a Rectangle with OpenGL

```julia
render_area = RenderArea()
rectangle = Rectangle(Vector2f(-0.5, 0.5), Vector2f(1, 1))
add_render_task!(render_area, RenderTask(rectangle))
```
![](docs/src/assets/readme_opengl_rectangle.png)

---

### Displaying a GLMakie Plot in a Mousetrap Window

```julia
using GLMakie, MousetrapMakie
canvas = GLMakieArea()
window = Mousetrap.Window()
set_child!(window, canvas) # can be used like any other widget

screen = create_glmakie_screen(canvas)
display(screen, scatter(rand(123)))
```
![](docs/src/assets/makie_scatter.png)

(**Note**: This feature is still experimental. See [here](https://github.com/Clemapfel/MousetrapMakie.jl) for more information)

---

## Supported Platforms

Since `v0.3.0`, Mousetrap is fully portable. All features are available for all 64-bit versions of Linux, FreeBSD, macOS, and Windows.

> **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.

> **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).

---

## Documentation

Documentation 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.

---

## Installation

In the Julia REPL, execute:

```julia
import Pkg;
begin
    Pkg.add(url="https://github.com/clemapfel/mousetrap.jl")
    Pkg.test("Mousetrap")
end
```

At the end, it should say `Mousetrap tests passed`.

> **Note**: On Windows, some `GLib` log messages regarding dbus connections may appear during testing. These do not indicate a problem.

> **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.

---

If 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:

```julia
import Pkg
begin
    try Pkg.rm("mousetrap") catch end
    try Pkg.rm("mousetrap_windows_jll") catch end
    try Pkg.rm("mousetrap_linux_jll") catch end
    try Pkg.rm("mousetrap_apple_jll") catch end
    try Pkg.rm("libmousetrap_jll") catch end
    Pkg.gc()
end
```

This will remove any trace of older versions that may cause conflicts.

--- 

## Credits & Donations

Mousetrap was designed and implemented by [C.Cords](https://clemens-cords.com).

It 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:
+ [GitHub Sponsors](https://github.com/sponsors/Clemapfel)
+ [PayPal](https://www.paypal.com/donate/?hosted_button_id=8KWF3JTDF8XL2)

The 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.

---

## License

The 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.



================================================
FILE: docs/make.jl
================================================
#
# Author: C. Cords (mail@clemens-cords.com)
# https://github.com/clemapfel/mousetrap.jl
#
# Copyright © 2023, Licensed under lGPL3-0
#

using Documenter, Pkg, InteractiveUtils
using Mousetrap

let file = open("docs/src/02_library/classes.md", "w+")
    @info "Mousetrap: Exporting Index..."
    write(file, "# Index: Classes\n")

    for name in sort(union(
        Mousetrap.types, 
        Mousetrap.signal_emitters, 
        Mousetrap.widgets, 
        Mousetrap.event_controllers, 
        Mousetrap.abstract_types,
    ))
        if name in [
            :Vector2f, :Vector3f, :Vector4f,
            :Vector2i, :Vector3i, :Vector4i,
            :Vector2ui, :Vector3ui, :Vector4ui
        ]
            continue
        end

        if occursin("STYLE_CLASS", string(name))
            continue
        end
        
        out = "## $name\n"
        out *= "```@docs\n"
        out *= "$name\n"
        out *= "```\n"
        
        binding = getproperty(Mousetrap, name)
        already_seen = Set{Symbol}()

        once = true

        signal_methods = []
        non_signal_methods = []

        for method in methodswith(binding, Mousetrap) 
            if isnothing(match(r".*_signal_.*", string(method.name)))
                # first or second argument is type, this is the equivalent of a member function in Julia
                try
                    if hasproperty(method.sig, :parameters) && (method.sig.parameters[2] == binding || method.sig.parameters[3] == binding)
                        if method.name in [:copy!, :flush, :bind, :download]
                            push!(non_signal_methods, Symbol("Mousetrap." * string(method.name)))
                        else
                            push!(non_signal_methods, method.name)
                        end
                    end
                catch e
                end
            else
                push!(signal_methods, method.name)
            end
        end

        # sort by signal, as opposed to alphabetically

        signals = Set{String}()
        for method in signal_methods
            m = match(r".*_signal_(.*)_", string(method))
            if !isnothing(m)
                push!(signals, m.captures[1])
            end
        end

        signal_methods_sorted = []
        for signal in sort([signals...])
            for method in signal_methods
                if occursin("_" * signal, string(method))
                    push!(signal_methods_sorted, method)
                end
            end
            push!(signal_methods_sorted, Symbol(""))
        end

        if length(non_signal_methods) + length(signal_methods) > 0
            for m in [non_signal_methods..., Symbol(""), signal_methods_sorted...]

                if once
                    out *= "#### Functions that operate on this type:\n"
                    once = false
                end

                as_string = string(m)
                if isempty(as_string)
                    out *= "\n\n"
                    continue
                end

                seen = m in already_seen
                push!(already_seen, m)

                try 
                if seen
                    continue
                elseif binding === String # skip ID typedefs
                    continue
                elseif getproperty(Mousetrap, m) isa Type # omit ctors
                    continue
                end
                catch end # to deal with copy!, flush, etc.

                out *= "+ [`$m`](@ref)\n"
            end
        end

        out *= "---\n"
        write(file, out)
    end

    close(file)

    file = open("docs/src/02_library/enums.md", "w+")
    write(file, "# Index: Enums\n")
    for enum_name in Mousetrap.enums
        write(file, "## $enum_name\n")
        write(file, "```@docs\n")
        write(file, "$enum_name\n")
        enum = getproperty(Mousetrap, enum_name)
        values = []
        for value_name in Mousetrap.enum_values
            if typeof(getproperty(Mousetrap, value_name)) <: enum
                write(file, "$value_name\n")
            end
        end

        write(file, "```\n")
        write(file, "---\n")
    end
    close(file)

    file = open("docs/src/02_library/functions.md", "w+")

    write(file, "# Index: Functions\n")
    
    for f in Mousetrap.functions
        write(file, "## `$f`\n")
        write(file, "```@docs\n")
        write(file, "Mousetrap.$f\n")
        write(file, "```\n")
    end
    close(file)
end 

makedocs(
    sitename="Mousetrap", 
    format = Documenter.HTML(
        size_threshold_warn = nothing,
        size_threshold = Integer(2e+6)
    )
)


================================================
FILE: docs/src/01_manual/01_installation.md
================================================
# Chapter 1: Installation & Workflow

In this chapter, we will learn:
+ How to install Mousetrap.jl
+ How to create our first Mousetrap application
+ Basic Julia skills that are needed to understand the rest of this manual

---

## Installation


To install Mousetrap, in the REPL, press the `]` key, then


```julia
import Pkg;
begin
    Pkg.add(url="https://github.com/clemapfel/mousetrap.jl")
    Pkg.test("Mousetrap")
end
```

Installation may take a long time. Once installation is succesfull, `Mousetrap tests passed` will be printed.

!!! compat "Removing older versions"
    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`:
    ```julia
    import Pkg
    begin
        try Pkg.rm("mousetrap") catch end
        try Pkg.rm("mousetrap_windows_jll") catch end
        try Pkg.rm("mousetrap_linux_jll") catch end
        try Pkg.rm("mousetrap_apple_jll") catch end
        try Pkg.rm("libmousetrap_jll") catch end
    end
    ```
  

## Hello World

To create our first Mousetrap app, we create a Julia file `main.jl`, with the following contents:

```julia
using Mousetrap
main() do app::Application
    window = Window(app)
    set_child!(window, Label("Hello World!"))
    present!(window)
end
```

To start our app, we navigate to the location of `main.jl` in our console, then execute:

```shell
# in same directory as `main.jl`
julia main.jl
```
![](../assets/readme_hello_world.png)

!!! compat "GIO Warnings on non-Linux"
    On Windows and macOS, running `main` may  produce a warning of the type:

    ```
    (julia:10512): GLib-GIO-WARNING **: 15:29:40.047: dbus binary failed to launch bus, maybe incompatible version
    ```

    This is due to a non-critical bug in one of Mousetraps dependencies, and does not indicate a problem.

!!! compat "Interactive Use"
    Interactive use inside the Julia REPL is only available for Mousetrap `v0.2.1` or newer.

---

## Julia Crash Course

The 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.

### Glossary

The following terms may be unfamiliar to some.

#### Invocation

To "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`*:

```julia
foo(x) = println(x) # definition
foo(1234) # invocation
```

#### Instantiation, Construction

In 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:

```julia
struct T 
  function T() # constructor
    return new() 
  end
end
```

We call an object returned by a constructor an **instance** of `T`. The act of creating an instance is called **instantiation** of `T`. 

In the above, `T()` (the constructor, which is a function), *instantiates* an object of type `T`, then returns that *instance*.

#### Scope

"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`

```julia
function f(x) # hard scope begin
    y = x + 1
    return y
end # hard scope end

println(y) # errors because y is not available, it was defined in hard scope
```

`begin`-`end` blocks are a "soft scope", meaning we can access definitions from within this soft scope from the outer scope:

```julia
begin # soft scope begin
    z = 1234
end # soft scope end

println(z) # works
```
```
1234
```

A "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:

```julia
a = 1234
module M
    b = "abcd"
end
```

This 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.

#### Front-End, Back-End, Engine

Regarding 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*. 

An *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.

#### Rendering, Frames

In 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*.

Each 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.

In 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. 
For 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.

This 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.

#### Native Rendering

Native 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.

---

## Object-Oriented Design

While 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**.

To 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.

If our object is of type `T`, an outer method will have the structure

```julia
function get_foo(instance::T) ::Foo
    # ...
end

function set_foo!(instance::T, new_value::Foo) ::Nothing
    # ...
end
```

Where `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.

Because 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.

Another 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:

```julia
using Mousetrap
methodswith(Window)
```

which will print a list of all functions that have at least one argument of type `Window`.

---

## C Enums   

Mousetraps 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`.

Each enum is a proper Mousetrap type, while each enum *value* is a numerical constant which is defined as being of that type. 

For example, the enum `Orientation`, which describes whether an object is vertically or horizontally oriented, is a type called `Mousetrap.Orientation`.

The **values** of `Orientation` are global constants:

+ `ORIENTATION_HORIZONTAL`
+ `ORIENTATION_VERTICAL`

In this example, `Orientation` is the enum, while `ORIENTATION_HORIZONTAL` and `ORIENTATION_VERTICAL` are the enums values.

Inspecting the values in the REPL, we see that they are actually just numbers:

```
julia> ORIENTATION_HORIZONTAL
Orientation(0)

julia> ORIENTATION_VERTICAL
Orientation(1)
```

But both are of type `Orientation`:

```julia
julia> ORIENTATION_HORIZONTAL isa Orientation && ORIENTATION_VERTICAL isa Orientation
true
```

All enum values are written in `SCREAMING_SNAKE_CASE`, while the enum types name uses `UpperCamelCase`. 

To check which enum has which values, we can again use the [Mousetrap documentation](../02_library/enums.md).

---

## Do Syntax

In Julia, any function whose **first argument is another function** can use **do-syntax**.

For example, the following function takes two arguments:

```julia
function example_f(f, arg)
    f(arg)
end
```
It applies its first argument, a function, to its second argument.

Invoking `example_f`, we could do:

```julia
to_invoke(x::Integer) = println(x)
example_f(to_invoke, 1234)
```
```
1234
```

Where `to_invoke` is used as the **first** argument. Because it is the first, we can also write the above using do-syntax:

```julia
example_f(1234) do x::Integer
    println(x)
end
```

Here, 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`.

## Anonymous Functions in Stacktraces

In the REPL, we can print any object's name to inspect it. Creating a new function, which prints its argument's name:

```julia
print_f_name(f) = println(f)
```

We see that if we invoke this function using regular function syntax, we get the following output:

```julia
function to_invoke()
    # do nothing
end

print_f_name(to_invoke)
```
```
to_invoke
```

If we instead call this function using do-syntax:

```julia
print_f_name() do 
    # do nothing
end
```
```
#9
```

We 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:

```julia
main() do app::Application
    throw(ErrorException("error"))
end
```
```
[ERROR] In Mousetrap.main: error
Stacktrace:
 [1] (::var"#11#12")(app::Application)
   @ Main ./REPL[15]:2
 [2] (::TypedFunction)(args::Application)
   @ Mousetrap ~/Workspace/Mousetrap.jl/src/Mousetrap.jl:74
 [3] (::Mousetrap.var"#15#17"{TypedFunction})(app::Application)
   @ Mousetrap ~/Workspace/Mousetrap.jl/src/Mousetrap.jl:1571
 [4] (::TypedFunction)(args::Application)
   @ Mousetrap ~/Workspace/Mousetrap.jl/src/Mousetrap.jl:74
 [5] (::Mousetrap.var"#6#8"{TypedFunction})(x::Tuple{CxxWrap.CxxWrapCore.CxxRef{Mousetrap.detail._Application}})
   @ Mousetrap ~/Workspace/Mousetrap.jl/src/Mousetrap.jl:620
 [6] safe_call(scope::String, f::Function, args::Tuple{CxxWrap.CxxWrapCore.CxxRef{Mousetrap.detail._Application}})
   @ Mousetrap ~/Workspace/Mousetrap.jl/src/Mousetrap.jl:144
 [7] run!(arg1::Mousetrap.detail._ApplicationAllocated)
   @ Mousetrap.detail ~/.julia/packages/CxxWrap/aXNBY/src/CxxWrap.jl:624
 [8] run!(app::Application)
   @ Mousetrap ~/Workspace/Mousetrap.jl/src/Mousetrap.jl:1538
 [9] (::Mousetrap.var"#14#16"{var"#11#12", String})()
   @ Mousetrap ~/Workspace/Mousetrap.jl/src/Mousetrap.jl:1581
```

We see that the anonymous function was allocated as `var"#11#12"`. This refers to the function defined using the do-block after `main()`.

Mousetrap stacktraces can get quite long, so it's best to parse them by reading the original message at the top first:

```
[ERROR] In Mousetrap.main: error
```
We 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`.

Knowledge about anonymous functions and how to read stacktraces will greatly aid us in debugging Mousetrap applications while learning.



================================================
FILE: docs/src/01_manual/02_signals.md
================================================
# Chapter 2: Signals

In this chapter, we will learn:
+ What signals and signal handlers are
+ How to connect to a signal
+ How to check which signature a signal expects
+ How and why to block signals

---

## Signal Architecture

Central 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.

A **signal**, in this context, has three components:
+ an **ID**, which uniquely identifies it. IDs may not be shared between signals
+ an **emitter**, which is a non-signal object
+ a **callback** or **signal handler**, which is a function called when an emitter emits a signal

It may be easiest to consider an example:

One 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.

In 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`. 

If 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:

```julia
# create `Button` instance
button = Button()

# create a signal handler
on_clicked(self::Button) = println("clicked")

# connect signal handler to the signal
connect_signal_clicked!(on_clicked, button)
```

Which can also be written more succinctly using [do-syntax](https://docs.julialang.org/en/v1/manual/functions/#Do-Block-Syntax-for-Function-Arguments):

```julia
button = Button()
connect_signal_clicked!(button) do self::Button
    println("clicked")
end
```

!!! details "Running Code Snippets"

    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:

    ```julia
    using Mousetrap
    main() do app::Application
        window = Window(app)

        # code snippet goes here

        set_child!(window, widget) # add whatever widget the code snippet is about here
        present!(window)
    end
    ```

    For example, to execute the example snippet above, we would create the following `main.jl` file:

    ```julia
    using Mousetrap
    main() do app::Application
        window = Window(app)

        # snippet start
        button = Button()
        connect_signal_clicked!(button) do self::Button
            println("clicked")
        end
        # snippet end

        set_child!(window, button) # add the button to the window
        present!(window)
    end
    ```

    Then execute it from the console by calling `julia main.jl`

When we execute this code, we see that a small window opens that contains our button. By clicking it, we get:

```
clicked
```

Only 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:

```julia
callback_01() = # ...
callback_02() = # ...

# call both functions from the signal handler
connect_signal_clicked!(button) do self::Button
    callback_01()
    callback_02()
end
```

---

## SignalEmitters

`Button`, like most classes in Mousetrap, is a subtype of an abstract type called [`SignalEmitter`](@ref). 

Subtyping `SignalEmitter` is equivalent to saying "This object can emit signals". Not all objects in Mousetrap are signal emitters, but most are. 

When we say "an object can emit signal `<id>`", what that means is that the following functions are defined for that object:

+ `connect_signal_<id>!`
+ `disconnect_signal_<id>!`
+ `emit_signal_<id>`
+ `set_signal_<id>_blocked!`
+ `get_signal_<id>_blocked`

For example, `Button` supports the signal with ID `clicked`, so the following functions are defined for it:

+ `connect_signal_clicked!`
+ `disconnect_signal_clicked!`
+ `emit_signal_clicked`
+ `set_signal_clicked_blocked!`
+ `get_signal_clicked_blocked`


We'll now go through what each of these functions does and how to use them.

---

## Connecting Signal Handlers

Above, we've already seen an example of how to connect a signal handler to a signal using `connect_signal_clicked!`. 

What 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**.

!!! note "Function Signature Syntax" 
    
    A function's **signature** describes a function's return- and argument types. For example, the function

    ```julia
    function foo(i::Int32, s::String) ::String
        return string(i) * s
    end
    ```
    
    Has the signature `(::Int32, ::String) -> String`.  It takes a 32-bit integer and a string, and it returns a string.

    The anonymous function from this do-block:

    ```julia
    connect_signal_clicked!(button) do self::Button
        println("clicked")
    end
    ```

    has the signature `(::Button) -> Nothing`. It takes an instance of type `Button` and returns `nothing`.

    For a function with an optional argument like this:

    ```julia
    function foo_optional(i::Int32, string::String, optional::Bool = true) ::String
        return string(i) * string * string(optional)
    end
    ```

    We convey that the last argument is optional by enclosing it in `[]`: `(::Int32, ::String, [::Bool]) -> String`

    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 
    ```
    (Arg1_t, Arg2_t, ..., [Optional1_t, Optional2_t, ...]) -> Return_t`.
    ```

    If and only if the `Return_t` of a function is `Nothing`, we can omit the return types along with the trailing `->`.


Each 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. 

## Checking Signal Signature

Working with our example, signal `clicked` of class `Button`, let's say we do not know what function signature this signal expects.
To 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:

```
help?> Mousetrap.Button
```
```  
  Button <: Widget
  ≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡

  Button with a label. Connect to signal clicked or specify an action via set_action! in order to
  react to the user clicking the button.

  Constructors
  ==============

  Button()
  Button(label::Widget)
  Button(::Icon)

  Signals
  =========

  │  clicked
  │  ---------
  │
  │  │  (::Button, [::Data_t]) -> Nothing
  │
  │  Emitted when the user clicks a widget using a mouse or touchscreen.

  Fields
  ========

  (no public fields)

  Example
  =========

  button = Button()
  set_child!(button, Label("Click Me"))
  connect_signal_clicked!(button) do x::Button
      println("clicked!")
  end
  set_child!(window, button)
```

We 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.

## Handing Data to Signal Handlers

While 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).

Instead, 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`.

Expanding 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:

```julia
button = Button()

# new signal handler that takes two arguments
on_clicked(self::Button, data) = println(data)  

# connect the signal handler, providing a third argument as `data`
connect_signal_clicked!(on_clicked, button, "custom message")
```

Or, using do-syntax:

```julia
button = Button()
connect_signal_clicked!(button, "custom message") do self::Button, data
    println(data)
end
```

By clicking the button, we now get:

```
custom message
```

Any and all objects can be provided as `data`, but they have to be packaged as exactly **one** argument.

### Grouping Data Arguments

Because 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, 
because we can use a simple trick to group any number of objects into a single argument.

Let'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:

```julia
button = Button()

function on_clicked(self::Button, data)
    println(data.string_value)
    println(data.integer_value)
    println(data.vector_value)
end

# create a named tuple that groups the arguments
named_tuple = (
    string_value = "abc",
    integer_value = 999,
    vector_value = [1.0, 2.0, 3.0]
)

# provide the named tuple as
connect_signal_clicked!(on_clicked, button, named_tuple)
```

Here, 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
easy-to-read name.

Again, we can write the above more succinctly using do-syntax:

```julia
button = Button()
connect_signal_clicked!(button, (
    string_value = "abc", integer_value = 999, vector_value = [1.0, 2.0, 3.0]
)) do self::Button, data
    println(data.string_value)
    println(data.integer_value)
    println(data.vector_value)
end
```

Using this technique, we can forward any objects to the signal handler via the optional `[::Data_t]` argument. This technique is available for all signals.

## Implicit Return Types

Julia 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:

```julia
button = Button()

to_append = []
function on_clicked(self::Button)
    push!(to_append, 1234)
end

connect_signal_clicked!(on_clicked, button)
```

Here 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:

```julia
(::Button, [::Data_t]) -> Nothing
```

`on_clicked`, in this example, does not explicitly return a value, yet running the above code we get:

```
[ERROR] In Mousetrap.main: AssertionError: Object `on_clicked` is not invokable as function with signature `(Button) -> Nothing`, because its return type is not `Nothing`
```

This is because `Base.push!` actually **does** return a value, the vector it is operating on:

```julia
to_append = []
out = push!(to_append, 1234)
out == to_append # true
```

Because `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.

In 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. 

To fix the above error, we should return `nothing` manually:

```julia
to_append = []
function on_clicked(self::Button)
    push!(to_append, 1234)
    return nothing
end
connect_signal_clicked!(on_clicked, button) # works
```

---

## Blocking Signal Emission

If we want an object to *not* call the signal handler on signal emission, we have two options:

Using `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. 

A much more performant and convenient method to **temporarily** prevent signal emission is  **blocking** the signal.

Blocking a signal will prevent the invocation of the signal handler. This means, for our `Button` example, once we call:

```julia
set_signal_clicked_blocked(button, true)
```

The user can still click the button, but the connected handler is not called.

To 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`.

### Signal Blocking: An Example

When is blocking necessary? Consider the following use-case:

```julia
# declare two buttons
button_01 = Button()
button_02 = Button()

# when button 01 is clicked, 02 is triggered programmatically
connect_signal_clicked!(button_01, button_02) do button_01::Button, button_02::Button
    # button_01 is self, button_02 is data
    println("01 clicked")
    emit_signal_clicked(button_02)
end

# when button 02 is clicked, 01 is triggered programmatically
connect_signal_clicked!(button_02, button_01) do button_02::Button, button_01::Button
    # button_02 is self, button_01 is data
    println("02 clicked")
    emit_signal_clicked(button_01)
end

# add both buttons to the window
set_child!(window, hbox(button_01, button_02))
```

In which we use [`emit_signal_clicked`](@ref), which manually triggers emission of signal `clicked`.  

[`hbox`](@ref) in the last line makes it so that we can display both buttons in the window.

The 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.

Running the above code as-is and clicking `button_01`, we get the following output:

```
01 clicked
02 clicked
01 clicked
02 clicked
01 clicked
02 clicked
01 clicked
02 clicked
...
```

And our application deadlocks. This is, of course, extremely undesirable, so let's talk through why this happens.

When `button_01` is clicked, it emits signal `clicked`, which invokes the connected signal handler. Going line-by-line through the handler :
+ `button_01`s handler prints `"01 clicked"`
+ `button_01`s handler activates `button_02`, triggering emission of signal `clicked` on `button_02`
+ `button_02`s handler prints `"02 clicked"`
+ `button_02`s handler activates `button_01`, triggering emission of signal `clicked` on `button_01`
+ `button_01`'s handler prints `"01 clicked"`
+ etc.

We created an infinite loop.

We can avoid this behavior by **blocking signals** at strategic times:

```julia
button_01 = Button()
button_02 = Button()

connect_signal_clicked!(button_01, button_02) do button_01::Button, button_02::Button
    println("01 clicked")
    
    # block self (01)
    set_signal_clicked_blocked!(button_01, true)

    # activate other (02)
    emit_signal_clicked(button_02)

    # unblock self (01)
    set_signal_clicked_blocked!(button_01, false)
end

connect_signal_clicked!(button_02, button_01) do button_02::Button, button_01::Button
    println("02 clicked")

    # block self (02)
    set_signal_clicked_blocked!(button_02, true)

    # activate other (01)
    emit_signal_clicked(button_01)

    # unblock self (02)
    set_signal_clicked_blocked!(button_02, false)
end

set_child!(window, hbox(button_01, button_02))
```

Let'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:

+ `button_01` invokes its signal handler
+ `button_01`s signal handler prints `01 clicked`
+ `button_01` blocks invocation of its signal handler
+ `button_01` activates `button_02`, triggering emission of signal `clicked`
+ `button_02`s signal handler prints `02 clicked`
+ `button_02` blocks invocation of its signal handler
+ `button_02` attempts to activate `button_01`, **but that buttons signal is blocked, so nothing happens**
+ `button_02` unblocks itself
+ `button_01` unblocks itself
+ both signal handlers return normally 

```
01 clicked
02 clicked
```

By 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.

To verify this is indeed the resulting behavior, we can use the following `main.jl`:

```julia
using Mousetrap
main() do app::Application
    window = Window(app)
    
    button_01 = Button()
    button_02 = Button()

    connect_signal_clicked!(button_01, button_02) do button_01::Button, button_02::Button
        println("01 clicked")
        
        set_signal_clicked_blocked!(button_01, true)
        emit_signal_clicked(button_02)
        set_signal_clicked_blocked!(button_01, false)
    end

    connect_signal_clicked!(button_02, button_01) do button_02::Button, button_01::Button
        println("02 clicked")

        set_signal_clicked_blocked!(button_02, true)
        emit_signal_clicked(button_01)
        set_signal_clicked_blocked!(button_02, false)
    end

    set_child!(window, hbox(button_01, button_02))
    present!(window)
end
```

![](../assets/double_button_signal_blocking.png)

---


================================================
FILE: docs/src/01_manual/03_actions.md
================================================
# Chapter 3: Actions

In this chapter, we will learn:
+ How and why to use the command pattern to encapsulate application functionality
+ How to create and use `Action`
+ How to trigger actions using `Button`, or by pressing a keyboard shortcut

---

## Introduction: The Command Pattern

As 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. 

Things 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).

A **command**, henceforth called **action**, is an object that has the following components:
+ A **function**, which is the action's behavior
+ An **ID** that uniquely identifies the action
+ An optional **shortcut trigger**, also often called a keybinding

In Mousetrap, a command is represented by the type [`Action`](@ref).

## Action

As 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.

For example, in the previous chapter, we declared a [`Button`](@ref) with the following behavior:

```julia
button = Button()
connect_signal_clicked!(button) do self::Button
    println("clicked")
end
```

In 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`.

### Action IDs

When 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. 

For 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.

An appropriate ID for our button behavior would therefore be `example.print_clicked`. 

### Action Function

Armed with this ID, we can create an action:

```julia
action = Action("example.print_clicked", app)
```
Where `app` is the application instance from our `main`.

The second part of an action is its function, also called its callback. We assign an actions function using [`set_function!`](@ref):

```julia
function on_example_print_clicked(x::Action) ::Nothing
    println("clicked")
end

action = Action("example.print_clicked", app)
set_function!(on_example_print_clicked, action)
```

or, using do-syntax:

```julia
action = Action("example.print_clicked", app)
set_function!(action) do x::Action
    println("clicked")
end
```

The function registered using `set_function!` is required to have the following signature:

```julia
(::Action, [::Data_t]) -> Nothing
```

We see that, much like with signal handlers, the callback is provided the `Action` instance, along with an optional `data` argument.

`Action` also provides a constructor that directly takes the function as its first argument. Using this, we can write the above even more succinctly:

```julia
action = Action("example.print_clicked", app) do x::Action
    println("clicked")
end
```

### Triggering Actions

At any point, we can call [`activate!`](@ref) to trigger the actions' callback. This is not the only way to trigger an action, however. 

`Button` provides [`set_action!`](@ref), which makes it such that when the button is clicked, the action is triggered:

```julia
action = Action("example.print_clicked", app)
set_function!(action) do x::Action
    println("clicked")
end

button = Button()
set_action!(button, action)
```
```
clicked
```

So far, this doesn't seem to have any upsides over just connecting to signal `clicked`. This is about to change.

## Disabling Actions

Similarly 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. 

## Action Maps

We 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.

Once `set_function!` was called, we can, at any point, retrieve the action from the application using `get_action!`:

```julia
let action = Action("example.print_clicked", app)
    set_function!(action) do x::Action
        println("clicked")
    end
end

# `action` is no longer defined here because of `let`

activate!(get_action(app, "example.print_clicked"))

# but we can retrieve it anyway
```
```
clicked
```

Where 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.

This 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.

---

## Shortcuts

An action can have any number of optional **shortcut triggers**, which are also called **keybindings**. 

A 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. 

Most of the time, we will have to implement behavior like this and associate a shortcut with it manually, using actions.

### Shortcut Trigger Syntax

Before we can learn about keybindings, we need to talk about keys. In Mousetrap, keyboard keys
are split into two groups: **modifiers**  and **non-modifiers**.

A modifier is one of the following:
+ `Shift`
+ `Control`
+ `Alt`

!!! note 
    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.

A non-modifier, then, is any key that is not a modifier.

A 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:

+ `a` (that is the `A` keyboard key) is a shortcut
+ `<Control><Shift>plus` (that is the `+` keyboard key, along with the `Control` and `Shift` modifiers) is a shortcut
+ `<Alt><Control><Shift>` is **not** a shortcut, because it does not contain a non-modifier
+ `<Control>xy` (that is the `X` key *and* the `Y` key) is **not** a shortcut because it contains more than one non-modifier key

Shortcuts 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:

+ "Control + C" is written `<Control>c`
+ "Alt + LeftArrow" is written as `<Alt>Left` (sic, `L` is capitalized)
+ "Shift + 1" is written as `exclam`

That 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`.

!!! tip "Looking up Key Identifiers"

    An example of how to look up the identifier of any key will be performed here.

    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), 
    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`.

    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`

    If we make an error and use the wrong identifier, a soft warning will be printed at runtime, informing us of this. 

    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:
    ```julia
    julia> Mousetrap.key_codes
        2278-element Vector{Symbol}:
        :0
        :1
        :2
        :3
        ...
    ```

!!! warning "Operating System Priority"
    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.

### Assigning Shortcuts to Actions

Now 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):

```julia
shortcut_action = Action("example.shortcut_action", app) do self::Action
    println("shortcut action called")
end
add_shortcut!(shortcut_action, "<Control>M")
```

An action can have multiple shortcuts, and one shortcut can be associated with two or more actions, though the latter is usually not recommended.

We 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.

A complete `main.jl` file showing how to trigger an action using a shortcut is given here:

```julia
using Mousetrap
main() do app::Application

    # create a window
    window = Window(app)

    # create an action that prints `shortcut action called`
    action = Action("example.shortcut_action", app) do action::Action
        println("shortcut action called")
    end

    # add the shortcut `Control + M`
    add_shortcut!(action, "<Control>M")
    
    # make `window` listen for all shortcuts of `action`
    set_listens_for_shortcut_action!(window, action)

    # show the window to the user
    present!(window)
end
```

Pressing "Control + M", we get:

```
shortcut action called
```


================================================
FILE: docs/src/01_manual/04_widgets.md
================================================
# Chapter 4: Widgets

In this chapter, we will learn:
+ What a widget is
+ Properties that all widgets share
+ What widgets are available in Mousetrap and how to use each of them
+ How to create compound widgets

---

!!! note "Running snippets from this Chapter"
    To run any partial code snippet from this section, we can use the following `main.jl` file:
    ```julia
    using Mousetrap
    main() do app::Application
        window = Window(app)

        # snippet here, creates widget, and adds it to `window' using `set_child!`

        present!(window)
    end
    ```

!!! note "Images in this Chapter"
    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).

---

## What is a widget?

Widgets 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.

For 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. 

Having 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.

## Widget Signals

In Mousetrap, [`Widget`](@ref) is an abstract type that all widgets subtype. `Widget` is a subtype of `SignalEmitter`, meaning
all widgets are signal emitters, but not all signal emitters are widgets. 

All widgets **share a number of signals**. These signals are accessible for every subtype of `Widget`:

| Signal ID  | Signature                       |
|------------|---------------------------------|
| `realize`  | `(::T, [::Data_t]) -> Nothing`  |
| `destroy`  | `(::T, [::Data_t]) -> Nothing`  | 
| `show`     | `(::T, [::Data_t]) -> Nothing`  | 
| `hide`     | `(::T, [::Data_t]) -> Nothing`  |
| `map`      | `(::T, [::Data_t]) -> Nothing`  | 
| `unmap`    | `(::T, [::Data_t]) -> Nothing`  | 

Where `T` is the subtype. For example, since `Button` is a `Widget`, the signature of `Button`s signal `realize` is `(::Button, [::Data_t]) -> Nothing`.

By 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. 

---

## Widget Properties

When 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.

Each 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.

### Parent and Children

Widgets 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!`.

How 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. 

Because 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.

```julia
box_a = Box(ORIENTATION_HORIZONTAL)
box_b = Box(ORIENTATION_HORIZONTAL)
button = Button()

# insert `button` into box A
push_back!(box_a, button)

# insert `button` into box B also
push_back!(box_b, button)
```

This latter call will print a warning

```
(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.
```

because `button`s parent is already `box_a`. Instead, we should create two buttons:

```julia
box_a = Box(ORIENTATION_HORIZONTAL)
box_b = Box(ORIENTATION_HORIZONTAL)
button_a = Button()
button_b = Button()

push_back!(box_a, button_a)
push_back!(box_b, button_b)
```

By connecting the same handler to both of these buttons' signals, we have two identically behaving objects, that are still separate widget instances.

### Size Request
 
Moving 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. 

By 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.

Manipulating the size request to influence a widget's minimum size is also called **size-hinting**.

### Accessing Widget Size

We 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`. 

Once realized, [`get_allocated_size`](@ref) and [`get_position`](@ref) return the current size and position of a widget, in pixels.

This 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.

Lastly, [`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.

Layout 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.

On top of a widget's size request, a widget's final allocated size depends on a number of other variables.

### Margin

Any 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.

We use [`set_margin_start!`](@ref), [`set_margin_end!`](@ref), [`set_margin_top!`](@ref) and [`set_margin_bottom!`](@ref) to control each individual margin:

```julia
widget = # ...
set_margin_start!(widget, 10)
set_margin_end!(widget, 10)
set_margin_top!(widget, 10)
set_margin_bottom!(widget, 10)

# equivalent to
set_margin_horizontal(widget, 10)
set_margin_vertical(widget, 10)

# equivalent to
set_margin!(widget, 10)
```

Where [`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.

Margins 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.

### Expansion

If 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.

[`set_expand!`](@ref) is a convenience function that sets expansion along both axes simultaneously.

```julia
widget = # ...
set_expand_horizontally!(widget, true)
set_expand_vertically!(widget, true)

# equivalent to
set_expand!(widget, true)
```

### Alignment

Widget **alignment** governs where inside its container a widget will attempt to position itself.

An 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. 

Alignment, then, governs **where in the window the button is positioned**.

We 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:

+ `ALIGNMENT_START`: left if horizontal, top if vertical
+ `ALIGNMENT_END`: right if horizontal, bottom if vertical
+ `ALIGNMENT_CENTER`: center of axis, regardless of orientation

We note that the horizontal x-axis is oriented from left to right, while the vertical y-axis is oriented from top to bottom.

For our example, the button would take on these locations based on which enum value we chose for each alignment axis:

| Vertical Alignment | Horizontal Alignment | Resulting Position  |
|--------------------|----------------------|---------------------|
| `ALIGNMENT_START`  | `ALIGNMENT_START`    | top left corner     |
| `ALIGNMENT_START`  | `ALIGNMENT_CENTER`   | top center          |
| `ALIGNMENT_START`  | `ALIGNMENT_END`      | top right corner    |
| `ALIGNMENT_CENTER` | `ALIGNMENT_START`    | center left         |
| `ALIGNMENT_CENTER` | `ALIGNMENT_CENTER`   | center              |
| `ALIGNMENT_CENTER` | `ALIGNMENT_END`      | center right        |
| `ALIGNMENT_END`    | `ALIGNMENT_START`    | bottom left corner  |
| `ALIGNMENT_END`    | `ALIGNMENT_CENTER`   | bottom center       |
| `ALIGNMENT_END`    | `ALIGNMENT_END`      | bottom right corner |

```julia
widget = # ...
set_horizontal_alignment!(widget, ALIGNMENT_START)
set_vertical_alignment!(widget, ALIGNMENT_START)

# equivalent to
set_alignment!(widget, ALIGNMENT_START)
```

Using 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.

---

### Visibility & Opacity

Once 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`.

To hide a shown widget or show a hidden widget, we use [`set_is_visible!`](@ref):

```julia
button = Button()
connect_signal_clicked!(button) do self::Button
    set_is_visible!(self, false)
end
set_child!(window, button)
```

In 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)`.

If 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:

```julia
# make a button invisible if it is visible, or visible if it is invisible
button = Button()
connect_signal_clicked!(button) do self::Button
    current = get_opacity(self)
    if current < 1.0
        set_opacity!(button, 1.0)
    else
        set_opacity!(button, 0.0)
    end
end
set_child!(window, button)
```

Setting 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.

---

### Cursor Type

Each 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).

Some 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):

| `CursorType` value             | Appearance                                                                                          |
|--------------------------------|-----------------------------------------------------------------------------------------------------|
| `CURSOR_TYPE_NONE`             | Invisible cursor                                                                                    |
| `CURSOR_TYPE_DEFAULT`          | Default arrow pointer                                                                               |
| `CURSOR_TYPE_POINTER`          | Hand pointing                                                                                       |
| `CURSOR_TYPE_TEXT`             | Caret                                                                                               |
| `CURSOR_TYPE_GRAB`             | Hand, not yet grabbing                                                                              |
| `CURSOR_TYPE_GRABBING`         | Hand, currently grabbing                                                                            |
| `CURSOR_TYPE_CELL`             | Cross, used for selecting cells from a table                                                        |
| `CURSOR_TYPE_CROSSHAIR`        | Crosshair, used for making pixel-perfect selections                                                 |
| `CURSOR_TYPE_HELP`             | Questionmark, instructs the user that clicking or hovering above this element will open a help menu |
| `CURSOR_TYPE_CONTEXT_MENU`     | Questionmark, instructs the user that clicking will open a context menu                             |
| `CURSOR_TYPE_NOT_ALLOWED`      | Instructs the user that this action is currently disabled                                           |
| `CURSOR_TYPE_PROGRESS`         | Spinning animation, signifies that the object is currently busy                                     |
| `CURSOR_TYPE_WAIT`             | Loading animation, Instructs the user that an action will become available soon                     |
| `CURSOR_TYPE_ZOOM_IN`          | Lens, usually with a plus icon                                                                      |
| `CURSOR_TYPE_ALL_SCROLL`       | Omni-directional scrolling                                                                          |
| `CURSOR_TYPE_MOVE`             | 4-directional arrow                                                                                 |
| `CURSOR_TYPE_NORTH_RESIZE`     | Up-arrow                                                                                            |
| `CURSOR_TYPE_NORTH_EAST_RESIZE` | Up-left arrow                                                                                       |
| `CURSOR_TYPE_EAST_RESIZE`      | Left arrow                                                                                          |
| `CURSOR_TYPE_SOUTH_EAST_RESIZE` | Down-left arrow                                                                                     |
| `CURSOR_TYPE_SOUTH_RESIZE`     | Down arrow                                                                                          |
| `CURSOR_TYPE_SOUTH_WEST_RESIZE` | Down-right arrow                                                                                    |
| `CURSOR_TYPE_WEST_RESIZE`      | Right arrow                                                                                         |
| `CURSOR_TYPE_NORTH_WEST_RESIZE` | Up-right arrow                                                                                      |
| `CURSOR_TYPE_ROW_RESIZE`        | Up-down arrow                                                                                       |
| `CURSOR_TYPE_COLUMN_RESIZE`     | Left-right arrow                                                                                    |


`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:

```julia
button = Button()
set_cursor!(button, CURSOR_TYPE_POINTER)
```

The 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:

```julia
widget = # ...
set_cursor_from_image!(widget, Image("/path/to/image.png"))
```

---

### Tooltip

Each 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. 

Tooltips are usually a simple text message. We can set the text directly using [`set_tooltip_text!`](@ref):

```julia
button = Button()
set_tooltip_text!(button, "Click to Open")
```
![](../assets/widget_tooltip_text.png)

If 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.

---

Now 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**.

## Window

For 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.

While 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. 

`Window` has exactly one child, which we set with `set_child!`, as we have so far already.

### Opening / Closing a Window

When 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:

```julia
main() do app::Application

    # create the window
    window = Window(app)
    
    # show the window, this realizes all widgets inside
    present!(window)
end
```

At 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.

### Close Request

`Window` has three signals, only the latter of which is relevant to us for now.

```@eval
using Mousetrap
Mousetrap.@signal_table(Window,
    activate_default_widget,
    activate_focused_widget,
    close_request
)
```

When 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.

```julia
# create a window that cannot be closed
window = Window(app)
connect_signal_close_request!(window) do self::Window
    return WINDOW_CLOSE_REQUEST_RESULT_PREVENT_CLOSE
end
present!(window)
```

If 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.

### Window Properties

Other than its singular child, `Window` has a number of other properties.

#### Title & Header Bar

[`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, "")`.

By 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.

#### Modality & Transience

When 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.

By 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.

Using [`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`. 

---

## Label

In 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:

```julia
label = Label("text")
```

To 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.

### Justify Mode

[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):

![](../assets/text_justify_left.png)

`JUSTIFY_MODE_LEFT`

![](../assets/text_justify_center.png)

`JUSTIFY_MODE_CENTER`

![](../assets/text_justify_right.png)

`JUSTIFY_MODE_RIGHT`

![](../assets/text_justify_fill.png)

`JUSTIFY_MODE_FILL`

Where the fifth mode is `JUSTIFY_MODE_NONE`, which arranges all text in exactly one line.

### Wrapping

Wrapping 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`.

Wrapping modes are values of the enum [`LabelWrapMode`](@ref). We set the wrap mode of a `Label` using [`set_wrap_mode!`](@ref).

| `LabelWrapMode` value  | Meaning                                                 | Example                 |
|------------------------|---------------------------------------------------------|-------------------------|
| `NONE`                 | no wrapping                                             | `"humane mousetrap"`    |
| `ONLY_ON_WORD`         | line will only be split between two words               | `"humane\nmousetrap"`   |
| `ONLY_ON_CHAR`         | line will only be split between syllables, adding a `-` | `"hu-\nmane mouse-\ntrap"` |
| `WORD_OR_CHAR`         | line will be split between words and/or syllables       | `"humane\nmouse-\ntrap"`   |

Where `\n` is the newline character.

### Ellipsize Mode

If 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).

| `EllipsizeMode` value | Meaning                                                  | Example                     |
|-----------------------|----------------------------------------------------------|-----------------------------|
| `NONE`                | text will not be ellipsized                              | `"Humane mousetrap engineer"` |
| `START`               | starts with `...`, showing only the last few words       | `"...engineer"`               |
| `END`                 | ends with `...`, showing only the first few words        | `"Humane mousetrap..."`       |
| `MIDDLE`              | `...` in the center, shows start and beginning           | `"Humane...engineer"`         |

### Markup

Labels 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:

| Tag          | Example                  | Result                  |
|--------------|--------------------------|-------------------------|
| `b`          | `<b>bold</b>`            | <b>bold</b>             |
| `i`          | `<i>italic</i>`          | <i>italic</i>           |
| `u`          | `<u>underline</u>`       | <u>underline</u>        |
| `s`          | `<s>strikethrough</s>`   | <s>strike-through</s>    |
| `tt`         | `<tt>inline_code</tt>`   | <tt>inline_code</tt>    |
| `small`      | `<small>small</small>`   | <small>small</small>    |
| `big`        | `<big>big</big>`         | <h3>big</h3>            |
| `sub`        | `x<sub>subscript</sub>`  | x<sub>subscript</sub>   |
| `sup`        | `x<sup>superscript</sup>` | x<sup>superscript</sup> |
| `&#` and `;` | `&#129700;`              | 🪤                      | 

Where 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.

!!! warning
    Pango only accepts the **decimal** code, not *hexadecimal*. For example, the Mousetrap emoji has the decimal code `129700`, while its hexadecimal code is `x1FAA4`. 
    To use this emoji in text, we choose `&#129700;`, **not** `&#x1FAA4;`. The latter will not work.

!!! note 
    All `<`, `>` will be parsed as style tags, regardless of whether they are escaped. To display them as characters, we use `&lt;` 
    (less-than) and `&gt;` (greater-than) instead of `<` and `>`. For example, we would write `x < y` as `"x &lt; y"`.

Pango 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.

```julia
label = Label("&lt;tt&gt;01234&lt;/tt&gt; is rendered as <tt>01234</tt>")
set_child!(window, label)
```
![](../assets/label_example.png)

---

## Box

[`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):

| `Orientation` Value       | Meaning                                  |
|---------------------------|------------------------------------------|
| `ORIENTATION_HORIZONTAL`  | Oriented left-to-right, along the x-axis |
| `ORIENTATION_VERTICAL`    | Oriented top-to-bottom, along the y-axis |

To add widgets to the `Box`, we use `push_front!`, `push_back!` and `insert_after!`:

```julia
left = Label("LEFT")
set_margin_start!(left, 10)

center = Label("CENTER")
set_margin_horizontal!(center, 10)

right = Label("RIGHT")
set_margin_end!(right, 10)

# create a horizontal box
box = Box(ORIENTATION_HORIZONTAL)

# add `left` to the start
push_front!(box, left)

# add `right to the end
push_back!(box, right)

# insert `center` after `left`
insert_after!(box, center, left)

# add box to window
set_child!(window, box)
```

![](../assets/box_example.png)

In 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.

[`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:

```julia
box = hbox(Label("LEFT"), Label("CENTER"), Label("RIGHT"))
set_spacing!(box, 10)
set_child!(window, box)
```
---

## CenterBox

[`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.

We use [`set_start_child!`](@ref), [`set_center_child!`](@ref), and [`set_end_child!`](@ref) to insert a child widget in the corresponding position:

```julia
center_box = CenterBox(ORIENTATION_HORIZONTAL)
set_start_child!(center_box, Label("start"))
set_center_child!(center_box, Button())
set_end_child!(center_box, Label("end"))
```

![](../assets/center_box.png)

Using `CenterBox`s constructor, we can also write the above as a one-liner:

```julia
center_box = CenterBox(ORIENTATION_HORIZONTAL, Label("start"), Button(), Label("end"))
```

---

## FlowBox

Third 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.

```julia
flow_box = FlowBox(ORIENTATION_VERTICAL)
for i in 1:7 
    push_back!(flow_box, Label(string(i)))
end
```

![](../assets/flow_box.png)

---

## HeaderBar

The 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.

Each `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).

Each `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:

+ `close`
+ `minimize`
+ `maximize`

Each 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.

A few examples:

| `set_layout!` string | close button | minimize button | maximize button |
|----------------------|--------------|----------------|-----------------|
| `:minimize,maximize,close` | right of title | right of title | right of title |
| `close:`             | left of title | hidden        | hidden |
| `minimize:maximize`  | hidden | left of title | right of title |
| `:`                  | hidden | hidden | hidden |

For example, to create a `HeaderBar` that has no elements, meaning no title and none of the title buttons, we would do the following:

```julia
window = Window(app)

# access windows header bar instance
header_bar = get_header_bar(window)

# hide title buttons
set_layout!(header_bar, ":")

# hide default title by replacing it with an empty label
set_title_widget!(header_bar, Label(""))
```
![](../assets/header_bar_blank.png)


---

## Separator

Perhaps the simplest widget is [`Separator`](@ref). It simply fills its allocated area with a solid color:

```julia
separator = Separator()
set_margin!(separator, 20)
set_expand!(separator, true)
set_child!(window, separator)
```

![](../assets/separator_example.png)

This widget is used as a background to another widget, to fill empty space, or as an element visually separating two sections. 

Often, 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:

```julia
hr = Separator()
set_expand_horizontally!(hr, true)
set_expand_vertically!(hr, false)
set_size_request!(hr, Vector2f(
    0,  # width: any 
    3   # height: exactly 3 px
));
```

This will render as a line that has a height of `3` px at all times but will assume the entire width of its parent.

---

## ImageDisplay

[`ImageDisplay`](@ref) is used to display static images.

Assuming we have an image at the absolute path `/assets/image.png`, we can create an `ImageDisplay` like so:

```julia
image_display = ImageDisplay()
create_from_file!(image_display, "/assets/image.png")

# equivalent to
image_display = ImageDisplay("/assets/image.png")
```

The following image formats are supported by `ImageDisplay`:

| Format Name             | File Extensions            |
|-------------------------|----------------------------|
| PNG                     | `.png`                     |
| JPEG                    | `.jpeg` `.jpe` `.jpg`      |
| JPEG XL image           | `.jxl`                     |
| Windows Metafile        | `.wmf` `.apm`              |
| Windows animated cursor | `.ani`                     |
| BMP                     | `.bmp`                     |
| GIF                     | `.gif`                     |
| MacOS X icon            | `.icns`                    |
| Windows icon            | `.ico` `.cur`              |
| PNM/PBM/PGM/PPM         | `.pnm` `.pbm` `.pgm` `.ppm` |
| QuickTime               | `.qtif` `.qif`             |
| Scalable Vector Graphics | `.svg` `.svgz` `.svg.gz`   |
| Targa                   | `.tga` `.targa`            |
| TIFF                    | `.tiff` `.tif`             |
| WebP                    | `.webp`                    |
| XBM                     | `.xbm`                     |
| XPM                     | `.xpm`                     |

After 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.

---

## Button

Familiar from previous chapters, [`Button`](@ref) is commonly used to trigger behavior.

It has one signal, which is emitted when the button is activated:

```@eval
using Mousetrap
Mousetrap.@signal_table(Button,
    clicked
)
```

We 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.

`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:

![](../assets/button_types.png)

!!! details "How to generate this image"
    ```julia
    using Mousetrap
    main() do app::Application
        window = Window(app)
    
        normal = Button()
        set_child!(normal, Label("01"))
    
        no_frame = Button()
        set_has_frame!(no_frame, false)
        set_child!(no_frame, Label("02"))
    
        circular = Button()
        set_is_circular!(circular, true)
        set_child!(circular, Label("03"))
    
        box = CenterBox(ORIENTATION_HORIZONTAL, normal, no_frame, circular)
        set_margin!(box, 75)
    
        set_child!(window, box)
        pesent!(window)
    end
    ```

Where the above-shown buttons have the following properties:

| Button | `set_has_frame!` | `set_is_circular!`|
|--------|------------------|-------------------|
| 01     | `true`             | `false`             |
| 02     | `false`            | `false`             |
| 03     | `true`             | `true`              |

---

## ToggleButton

[`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`.

Unique 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.

```@eval
using Mousetrap
Mousetrap.@signal_table(ToggleButton,
    toggled,
    clicked
)
```

To check whether the button is currently toggled, we use `get_is_active`, which returns `true` if the button is currently depressed, `false` otherwise.

```julia
toggle_button = ToggleButton()
connect_signal_toggled!(toggle_button) do self::ToggleButton
  println("state is now: $(get_is_active(self))")
end
set_child!(window, toggle_button)
```

---

## CheckButton

[`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`. 

```@eval
using Mousetrap
Mousetrap.@signal_table(ToggleButton,
    toggled
)
```

`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:

![](../assets/check_button_states.png)

!!! details "How to generate this image"
    ```julia
    using Mousetrap
    main() do app::Application
    
        window = Window(app)
    
        active = CheckButton()
        set_state!(active, CHECK_BUTTON_STATE_ACTIVE)
        active_box = vbox(active, Label("active"))
    
        inconsistent = CheckButton()
        set_state!(inconsistent, CHECK_BUTTON_STATE_INCONSISTENT)
        inconsistent_box = vbox(inconsistent, Label("inconsistent"))
    
        inactive = CheckButton()
        set_state!(inactive, CHECK_BUTTON_STATE_INACTIVE)
        inactive_box = vbox(inactive, Label("inactive"))
    
        for button in [active, inconsistent, inactive]
            set_horizontal_alignment!(button, ALIGNMENT_CENTER)
        end
    
        box = CenterBox(ORIENTATION_HORIZONTAL, active_box, inconsistent_box, inactive_box)
        set_margin!(box, 75)
    
        set_child!(window, box)
        present!(window)
    end
    ```

Note 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.

---

## Switch

As 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:

```@eval
using Mousetrap
Mousetrap.@signal_table(Switch,
    switched
)
```

![](../assets/switch_states.png)

!!! details "How to generate this image"
    ```julia
    main() do app::Application
    
        window = Window(app)
    
        active = Switch()
        set_is_active!(active, true)
        active_box = vbox(active, Label("active"))
    
        inactive = Switch()
        set_is_active!(inactive, false)
        inactive_box = vbox(inactive, Label("inactive"))
    
        for switch in [active, inactive]
            set_horizontal_alignment!(switch, ALIGNMENT_CENTER)
            set_margin!(switch, 10)
        end
    
        box = CenterBox(ORIENTATION_HORIZONTAL)
        set_start_child!(box, active_box)
        set_end_child!(box, inactive_box)
        set_margin!(box, 75)
    
        set_child!(window, box)
        present!(window)
    end
    ```
---

---

## Adjustment

From 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).

`Adjustment` has four properties:

+ `lower`: lower bound of the range
+ `upper`: upper bound of the range
+ `increment`: step increment
+ `value`: current value, in `[lower, upper]`

For example, the following `Adjustment`:

```julia
adjustment = Adjustment(
    1,      # value
    0,      # lower
    2,      # upper
    0.5     # step increment
)
```
Expresses the range `{0, 0.5, 1, 1.5, 2}`, with `1` being the value on initialization.

We 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. 

`Adjustment` has two signals:

```@eval
using Mousetrap
Mousetrap.@signal_table(Adjustment,
    value_changed,
    properties_changed
)
```

We 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.

---

## SpinButton

`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.

We supply the properties of the range underlying the `SpinButton` to its constructor:

```julia
# create SpinButton with range [0, 2] and increment 0.5
spin_button = SpinButton(0, 2, 0.5)
```

![](../assets/spin_button.png)

!!! details "How to generate this image"
    ```julia
    main() do app::Application
        window = Window(app)
    
        horizontal = SpinButton(0, 2, 0.5)
        set_value!(horizontal, 1)
    
        # Add invisible separator buffers above and below spin button for better symmetry
        horizontal_buffer = CenterBox(
            ORIENTATION_VERTICAL, 
            Separator(; opacity = 0.0),
            horizontal,
            Separator(; opacity = 0.0)
        )
    
        vertical = SpinButton(0, 2, 0.5)
        set_value!(vertical, 1)
        set_orientation!(vertical, ORIENTATION_VERTICAL)
    
        box = CenterBox(ORIENTATION_HORIZONTAL)
        set_start_child!(box, horizontal_buffer)
        set_end_child!(box, vertical)
    
        set_child!(window, box)
        present!(window)
    end             
    ```

We 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`.

Along 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:

```julia
spin_button = SpinButton(0, 2, 0.5)
connect_signal_value_changed!(spin_button) do self::SpinButton
    println("Value is now: $(get_value(self))")
end
```

The other signal is `wrapped`, which is emitted when [`set_should_wrap!`](@ref) is set to `true` and the `SpinButton`'s value under- or overflows.

---

## Scale

![](../assets/scale_no_value.png)

!!! details "How to generate this image"
    ```julia
    main() do app::Application
        window = Window(app)

        horizontal = Scale(0, 2, 0.5)
        set_orientation!(horizontal, ORIENTATION_HORIZONTAL)
        set_value!(horizontal, 1)
        set_size_request!(horizontal, Vector2f(200, 0))

        vertical = Scale(0, 2, 0.5)
        set_orientation!(vertical, ORIENTATION_VERTICAL)
        set_value!(vertical, 1)
        set_size_request!(vertical, Vector2f(0, 200))

        box = CenterBox(ORIENTATION_HORIZONTAL)
        set_start_child!(box, horizontal)
        set_end_child!(box, vertical)

        set_margin_horizontal!(box, 75)
        set_margin_vertical!(box, 40)

        set_child!(window, box)
        present!(window)
    end
    ```

[`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):

![](../assets/scale_with_value.png)

!!! details "How to generate this image"
    ```julia
    main() do app::Application
        window = Window(app)

        window = Window(app)

        horizontal = Scale(0, 2, 0.5)
        set_orientation!(horizontal, ORIENTATION_HORIZONTAL)
        set_value!(horizontal, 1)
        set_size_request!(horizontal, Vector2f(200, 0))
        set_should_draw_value!(horizontal, true)

        vertical = Scale(0, 2, 0.5)
        set_orientation!(vertical, ORIENTATION_VERTICAL)
        set_value!(vertical, 1)
        set_size_request!(vertical, Vector2f(0, 200))
        set_should_draw_value!(vertical, true)

        box = CenterBox(ORIENTATION_HORIZONTAL)
        set_start_child!(box, horizontal)
        set_end_child!(box, vertical)

        set_margin_horizontal!(box, 75)
        set_margin_vertical!(box, 40)

        set_child!(window, box)
        present!(window)
    end
    ```

`Scale` supports most of `SpinButton`'s functions, including querying information about its underlying range, setting the orientation, and signal `value_changed`:

```julia
scale = Scale(0, 2, 0.5)
connect_signal_value_changed!(scale) do self::Scale
    println("Value is now: $(get_value(self))")
end
```
---

## LevelBar

[`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.

To 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:

```julia
# create a LevelBar for range [0, 2]
level_bar = LevelBar(0, 2)
set_value!(level_bar, 1.0) # set to 50%
```

Unlike the previous widgets, `LevelBar` does not have a step increment.

Once the bar reaches 75%, it changes color:

![](../assets/level_bar.png)

!!! details "How to generate this image"
    ```julia
    main() do app::Application
        window = Window(app)

        box = Box(ORIENTATION_VERTICAL)
        set_spacing!(box, 10)
        set_margin!(box, 10)

        n_bars = 5
        for i in 1:n_bars
            fraction = Float32(i) / n_bars
            label = Label(string(Int64(round(fraction * 100))) * "%")
            set_size_request!(label, Vector2f(50, 0))

            bar = LevelBar(0, 1)
            set_value!(bar, fraction)
            set_expand_horizontally!(bar, true)

            row_box = Box(ORIENTATION_HORIZONTAL)
            set_spacing!(box, 10)
            push_back!(row_box, label)
            push_back!(row_box, bar)

            push_back!(box, row_box)
        end

        set_child!(window, box)
        present!(window)
    end
    ```

`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.

---

## ProgressBar

Similarly 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.

`ProgressBar` only expresses values in `[0, 1]`, and [`set_fraction!`](@ref) will only accept values in this range.

Using `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!`

![](../assets/progress_bar.png)

!!! details "How to generate this image"
    ```julia
    main() do app::Application
        window = Window(app)

        box = Box(ORIENTATION_VERTICAL)
    
        progress_bar = ProgressBar()
        set_fraction!(progress_bar, 0.47)
        set_vertical_alignment!(progress_bar, ALIGNMENT_CENTER)
        set_expand!(progress_bar, true)
        set_show_text!(progress_bar, true)
        set_margin!(progress_bar, 10)
        
        set_child!(window, progress_bar)
        present!(window)
    end
    ```
---

## Spinner

To 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.

![](../assets/spinner.png)

!!! details "How to generate this image"
    ```julia
    main() do app::Application

        window = Window(app)

        spinner = Spinner()
        set_is_spinning!(spinner, true)

        set_child!(window, spinner)
        present!(window)
    end
     ```

---

## Entry

Text 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.

The 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).

While 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.

`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. 

To 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.

![](../assets/entry.png)

!!! details "How to generate this image"
    ```julia
    main() do app::Application

        window = Window(app)

        clear = Entry()
        set_text!(clear, "text")

        password = Entry()
        set_text!(password, "text")
        set_text_visible!(password, false)

        box = vbox(clear, password)
        set_spacing!(box, 10)
        set_margin_horizontal!(box, 75)
        set_margin_vertical!(box, 40)

        set_child!(window, box)
        present!(window)
    end
    ```

Lastly, `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: 

```@eval
using Mousetrap
Mousetrap.@signal_table(Entry,
    activate,
    text_changed
)
```

We would therefore connect a handler that reacts to the text of an entry changing like so:

```julia
entry = Entry()
set_text!(entry, "Write here")
connect_signal_text_changed!(entry) do self::Entry
    println("text is now: $(get_text(self))")
end
```

Note 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.

## TextView

[`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).

Much 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.

`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`:

```@eval
using Mousetrap
Mousetrap.@signal_table(TextView,
    text_changed
)
```

```julia
text_view = TextView()
set_text!(text_view, "Write here")
connect_signal_text_changed!(text_view) do self::TextView
    println("text is now: $(get_text(self))")
end
```

---

## Dropdown

We 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.

We add an item using `push_back!`, which takes a string that will be used as the item's label:

```julia
dropdown = DropDown()
item_01_id = push_back!(dropdown, "Item #01")
item_02_id = push_back!(dropdown, "Item #02")
item_03_id = push_back!(dropdown, "Item #03")
```

![](../assets/dropdown_simple.png)

`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).

If 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.

`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:

```
(::DropDown, [::Data_t]) -> Nothing
```

```julia
dropdown = DropDown()
push_back!(dropdown, "Item #01") do self::DropDown
    println("Item #01 selected")
end
push_back!(dropdown, "Item #02") do self::DropDown
    println("Item #03 selected")
end
push_back!(dropdown, "Item #03") do self::DropDown
    println("Item #03 selected")
end
```

This 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.

Lastly, 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:

```julia
dropdown = DropDown()
push_back!(dropdown,
    Label("Item #01"),  # Widget displayed in dropdown menu
    Label("01")         # Widget displayed when item is selected
)

push_back!(dropdown, Label("Item #02"), Label("02"))
push_back!(dropdown, Label("Item #03"), Label("03"))
```

![](../assets/dropdown_separate.png)

Where 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.

---

## Frame

[`Frame`](@ref) is a purely cosmetic widget that displays its singular child in a frame with a small border and rounded corners:

![](../assets/frame.png)

!!! details "How to generate this image"
    ```julia
    main() do app::Application

        window = Window(app)

        left = Separator()
        right = Separator()

        for separator in [left, right]
            set_size_request!(separator, Vector2f(50, 50))
            set_expand!(separator, false)
        end

        box = CenterBox(ORIENTATION_HORIZONTAL)
        set_start_child!(box, left)
        set_end_child!(box, Frame(right))

        set_margin_horizontal!(box, 75)
        set_margin_vertical!(box, 40)
    
        set_child!(window, box)
        present!(window)
    end
    ```

Using [`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.

`Frame` is rarely necessary, but will make GUIs seem more aesthetically pleasing and polished. 

---

## AspectFrame

Not 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.

We 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:

```julia
child_widget = # ...
aspect_frame = AspectFrame(4.0 / 3.0)
set_child!(aspect_frame, child_widget);
```

---

## ClampFrame

Using 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`.

We choose the maximum size in pixels during construction, or using [`set_maximum_size!`](@ref):

```julia
child_widget = # ...
width_clamp_frame = ClampFrame(150, ORIENTATION_HORIZONTAL)
height_clamp_frame = ClampFame(150, ORIENTATION_VERTICAL)

set_child!(width_clamp_frame, child_widget)
set_child!(height_clamp_frame, width_clamp_frame)
```

In which we use two nested `ClampFrame`s, such that `child_widget` can never exceed `150px` for both its width and height.

---


## Overlay

So 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).

`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):

![](../assets/overlay_buttons.png)

!!! details "How to generate this image"
    ```julia
    main() do app::Application

        window = Window(app)

        lower = Button()
        set_horizontal_alignment!(lower, ALIGNMENT_START)
        set_vertical_alignment!(lower, ALIGNMENT_START)

        upper = Button()
        set_horizontal_alignment!(upper, ALIGNMENT_END)
        set_vertical_alignment!(upper, ALIGNMENT_END)

        overlay = Overlay()
        set_child!(overlay, lower)
        add_overlay!(overlay, upper)

        set_child!(window, AspectFrame(1, overlay))
        present!(window)
    end
    ```

Where the position and size of overlayed widgets depend on their expansion and alignment properties.

By 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:

```julia
add_overlay!(overlay, overlaid_widge; include_in_measurement = true)
```

---

## Paned

[`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.

`Paned` is orientable. Depending on its orientation, `set_start_child!` and  `set_end_child!` add a widget to the corresponding side.

![](../assets/paned.png)

!!! details "How to generate this image"
    ```julia
    function generate_child(label::String)
        out = Frame(Overlay(Separator(), Label(label)))
        set_margin!(out, 10)
        return out
    end

    main() do app::Application

        window = Window(app)

        paned = Paned(ORIENTATION_HORIZONTAL)
        set_start_child!(paned, generate_child("Left"))
        set_end_child!(paned, generate_child("Right"))

        set_start_child_shrinkable!(paned, true)
        set_end_child_shrinkable!(paned, true)

        set_child!(window, paned)
        present!(window)
    end
    ```

`Paned` has two per-child properties: whether a child is **resizable** and whether it is **shrinkable**.

Resizable means that if the `Paned` changes size, the allocated area of its child should resize accordingly.

Shrinkable 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:

```julia
set_start_child_shrinkable!(paned, true)
set_end_child_shrinkable!(paned, true)
```

![](../assets/paned_shrinkable.png)

---

## Revealer

While 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.

One of the most common applications for animations is the act of hiding or showing a widget. [`Revealer`](@ref) was made for this purpose.

To 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.

### Transition Animation

We 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:

```julia
revealer = Revealer()
set_child!(revealer, #= widget =#)
set_transition_duration!(revealer, seconds(1));
```

Where `seconds` returns a [`Mousetrap.Time`](@ref).

Apart 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.

![](../assets/revealer.png)

!!! details "How to generate this image"
    ```julia
    main() do app::Application
        window = Window(app)

        # create child
        child = Frame(Overlay(Separator(), Label("<span size='200%'>[Item]</span>")))
        set_margin!(child, 10)
        set_size_request!(child, Vector2f(0, 100))

        # setup revealer
        revealer = Revealer()
        set_child!(revealer, child)
        set_transition_duration!(revealer, seconds(1))
        set_transition_type!(revealer, REVEALER_TRANSITION_TYPE_SLIDE_DOWN)

        # create a button that, when clicked, triggers the revealer animation
        button = Button()
        connect_signal_clicked!(button, revealer) do self::Button, revealer::Revealer
            set_is_revealed!(revealer, !get_revealed(revealer))
        end
    
        set_child!(window, vbox(button, revealer))
        present!(window)
    end
    ```
---

## ActionBar

One 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.

`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!`. 

---

## Expander

[`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.

Expander 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.

![](../assets/expander.png)

!!! details "How to generate this image"
    ```julia
    main() do app::Application
        window = Window(app)

        child = Frame(Overlay(Separator(), Label("Child")))
        set_margin!(child, 10)
        set_size_request!(child, Vector2f(0, 100))

        label = Label("Label")
        set_margin!(label, 10)
    
        expander_and_frame = Frame(Expander(child, label))
        set_margin!(expander_and_frame, 10)

        set_child!(window, expander_and_frame)
        present!(window)
    end
    ```

Note 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.

---

## Viewport

By 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. 

Sometimes, widgets that are this large are unavoidable. In situations like this, we can use [`Viewport`](@ref) to only display part of a widget.

We 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:

![](../assets/viewport.png)

!!! details "How to generate this image"
    ```julia
    main() do app::Application

        window = Window(app)

        child = Frame(Overlay(Separator(), Label("<span size='800%'>CHILD</span>")))
        set_margin!(child, 10);

        viewport = Viewport()
        set_child!(viewport, child)

        set_child!(window, viewport)
        present!(window)
    end
    ```

### Size Propagation

By 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**.

If [`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.

```julia
set_propagate_natural_width!(viewport, true)
set_propagate_natural_height!(viewport, false)
```

![](../assets/viewport_propagation.png)

Here, the viewport will be the same width as the child, but the viewport's height is independent of that of its child. 

### Scrollbar Policy

`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.

If 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:


| `ScrollbarVisibilityPolicy` | Meaning |
|-----------------------------|-----------|
| `SCROLLBAR_VISIBILITY_POLICY_NEVER` | scrollbar is always hidden |
| `SCROLLBAR_VISIBILITY_POLICY_ALWAYS` | scrollbar is always shown |
| `SCROLLBAR_VISIBILITY_POLICY_AUTOMATIC` | scrollbar hides/shows automatically|

If `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.

### Scrollbar Position

Lastly, 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).

| `CornerPlacement` | Meaning |
|-------------------|---------|
| `CORNER_PLACEMENT_TOP_LEFT` | horizontal scrollbar at the top, vertical scrollbar on the left |
| `CORNER_PLACEMENT_TOP_RIGHT` | horizontal at the top, vertical on the right |
| `CORNER_PLACEMENT_BOTTOM_LEFT` | horizontal at the bottom, vertical on the left |
| `CORNER_PLACEMENT_BOTTOM_RIGHT` | horizontal at the bottom, vertical on the right |

### Signals

If 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.

With this, scrollbar policy, and size propagation we have full control over every aspect of `Viewport`.

--- 

## Scrollbar

`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.

To react to the user scrolling, we need to connect to the signals of the `Adjustment`, as `Scrollbar` does not provide any signals itself:

```julia
adjustment = Adjustment(0.5, 0, 1, 0.01)
scrollbar = Scrollbar(ORIENTATION_HORIZONTAL, adjustment)
connect_signal_value_changed!(adjustment) do self::Adjustment
    println("Value is now $(get_value(self))")
end

set_child!(window, scrollbar)
```

Where 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`.

If we lose track of the `Adjustment` instance after constructing the `ScrollBar`, we can retrieve it anytime using `get_adjustment`.

---

## Popover

A [`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).

Showing the popover is called **popup**, closing the popover is called **popdown**, `Popover` correspondingly has [`popup!`](@ref) and [`popdown!`](@ref) to show or hide itself.

![](../assets/popover.png)

!!! details "How to generate this image"
    ```julia
    using Mousetrap

    main() do app::Application
        window = Window(app)

        child = Frame(Overlay(Separator(), Label("Child")))
        set_size_request!(child, Vector2f(100, 75))
        set_margin!(child, 10);

        popover = Popover()
        set_child!(popover, child)
        button = Button()

        # when the button is clicked, the popover is shown. It hides automatically
        connect_signal_clicked!(button, popover) do self::Button, popover::Popover
            popup!(popover)
        end

        # to show the popover, it needs to be inside the same container as the child it should be attached to
        set_child!(window, vbox(popover, button))
        present!(window)
    end
    ```

Manually 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)

## PopoverButton

`PopoverButton` has a single child and one signal: `activate`:

```@eval
using Mousetrap
Mousetrap.@signal_table(PopoverButton,
    activate
)
```

Instead of triggering behavior, `PopoverButton`'s purpose is to reveal and hide a `Popover`.

We first create the `Popover`, then supply it as the argument to `PopoverButton`'s constructor:

```julia
popover = Popover()
popover_button = PopoverButton(popover)
```

Additionally, an arrow is shown next to the label of the `PopoverButton`, indicating to the user that, when it is clicked, a popover will open.

---

## SelectionModel

We will now move on to **selectable widgets**, which tend to be the most complex and powerful widgets in Mousetrap.

All 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:

![](../assets/list_view_selected.png)

Modifying 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.

`SelectionModel` has signal `selection_changed`, which is emitted anytime an item is selected or unselected in any way. This signal requires the signature
```
(::SelectionModel, position::Integer, n_items::Integer, [::Data_t]) -> Nothing
```
Where `position` is the new index of the changed item (1-based), while `n_items` is the number of currently selected items.

Each 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:

| `SelectionMode`           | Number of Items |
|---------------------------|-----------------|
| `SELECTION_MODE_NONE`     | exactly zero    |
| `SELECTION_MODE_SINGLE`   | exactly one     |
| `SELECTION_MODE_MULTIPLE` | zero or more    |


We 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.

## ListView

For 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!`:

```julia
list_view = ListView(ORIENTATION_VERTICAL, SELECTION_MODE_SINGLE)
push_back!(list_view, Label("Child #01"))
push_back!(list_view, Label("Child #02"))
push_back!(list_view, Label("Child #03"))
```

![](../assets/list_view_simple.png)

Where the second child is currently selected.

`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).

### Nested Trees

By default, `ListView` displays its children in a linear list, either horizontally or vertically. `ListView` also supports **nested lists**, sometimes called a **tree view**:

![](../assets/list_view_nested.png)

!!! details "How to generate this image"
    ```julia
    main() do app::Application

        window = Window(app)

        list_view = ListView(ORIENTATION_VERTICAL, SELECTION_MODE_SINGLE)
        push_back!(list_view, Label("Child #01"))
        child_02_it = push_back!(list_view, Label("Child #02"))
        push_back!(list_view, Label("Child #03"))

        push_back!(list_view, Label("Nested Child #01"), child_02_it)
        nested_child_02_it = push_back!(list_view, Label("Nested Child #02"), child_02_it)

        push_back!(list_view, Label("Inner Child #01"), nested_child_02_it)

        frame = Frame(list_view)
        set_margin!(frame, 10)
        set_child!(window, frame)
        present!(window)
    end
    ```

The 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`.

Each 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). 

This iterator identifies which list view to insert the item in. We obtain an iterator like so:

```julia
list_view = ListView()
child_01_it = push_back!(list_view, Label("Child #01"))
child_02_it = push_back!(list_view, Lable("Child #02"))
child_03_it = push_back!(list_view, Lable("Child #03"))
```

If 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:

```julia
nested_child_01_it = push_back!(list_view, Label("Nested Child #01"), child_02_it)
```

To 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.

If 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.

---

## GridView

[`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**.

`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`:

![](../assets/grid_view_horizontal.png)

*A horizontal `GridView`*

![](../assets/grid_view_vertical.png)

*A vertical `GridView`*

!!! details "How to generate this image"
    ```julia
    function generate_child(label::String) ::Widget

        child = Frame(Overlay(Separator(), Label(label)))
        set_size_request!(child, Vector2f(50, 50))
        set_expand!(child, false)
        return AspectFrame(1, child)
    end

    main() do app::Application
        window = Window(app)

        grid_view = GridView(ORIENTATION_VERTICAL) # or `ORIENTATION_HORIZONTAL`
        set_expand!(grid_view, true)

        for i in 1:9
            push_back!(grid_view, generate_child("0$i"))
        end

        separator = Separator()
        set_expand!(separator, true)
        set_expand!(grid_view, false)

        set_child!(window, vbox(separator, grid_view))
        present!(window)
    end
    ```

We 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`).

---

## Column View

[`ColumnView`](@ref) is used to display widgets as a table, with rows and columns. Each column has a title, which uniquely identifies it.

To fill our `ColumnView`, we first instance it, then allocate a non-zero number of columns:

```julia
column_view = ColumnView()

column_01 = push_back_column!(column_view, "Column #01")
column_02 = push_back_column!(column_view, "Column #02")
column_03 = push_back_column!(column_view, "Column #03")
```

Each column requires a title that should be unique to that column.

We 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).

To add a widget into the n-th row (1-based) of a `ColumnViewColumn`, we use `set_widget_at!`:

```julia
# add 3 labels into column 1, rows 1 - 3
set_widget_at!(column_view, column_01, Label("01"))
set_widget_at!(column_view, column_01, Label("02"))
set_widget_at!(column_view, column_01, Label("03"))
```

![](../assets/column_view.png)

!!! details "How to generate this image"
    ```julia
    main() do app::Application

        println("called")

        window = Window(app)
        set_title!(window, "Mousetrap.jl")

        column_view = ColumnView()

        column_01 = push_back_column!(column_view, "Column #01")
        column_02 = push_back_column!(column_view, "Column #02")
        column_03 = push_back_column!(column_view, "Column #03")

        column_i = 1
        for column in [column_01, column_02, column_03]
            for row_i in 1:9
                set_widget_at!(column_view, column, row_i, Label("0$column_i | 0$row_i"))
            end
            column_i = column_i + 1
        end

        set_expand!(column_view, true)
        set_child!(window, column_view)
        present!(window)
    end
    ```

Any rows that do not yet have widgets will be backfilled and appear empty. 

If 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.

Since 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:

```julia
# add 1st widget to 1st column, 2nd widget to 2nd column, etc.
push_back_row!(column_view, Label("Column 01 Child"), Label("Column 02 Child"), Label("Column 03 Child"))
```

This 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.

`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.

---

## Stack

[`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**.

We 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.

```julia
stack = Stack()

id_01 = add_child!(stack, page_01_widget, "Page #01")
id_02 = add_child!(stack, page_02_widget, "Page #02")
id_03 = add_child!(stack, page_03_widget, "Page #03")
```

To 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).

To 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`:

```julia
function on_selection_changed(self::SelectionModel, position::Integer, n_items::Integer, stack::Stack) ::Nothing
    println("Current stack page is now: $(get_child_at(stack, position))")
end

stack = Stack()
stack_model = get_selection_model(stack)
connect_signal_selection_changed!(on_selection_changed, stack_model, stack)
```

Where 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.

While 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
to provide another widget that modifies the stack, or we can use one of two pre-made widgets for this purpose: `StackSwitcher` and `StackSidebar`.

### StackSwitcher

[`StackSwitcher`](@ref) presents the user with a row of buttons, each of which uses the corresponding stack page's title:

![](../assets/stack_switcher.png)

!!! details "How to generate this image"
    ```julia
    function generate_child(label::String) ::Widget
        child = Frame(Overlay(Separator(), Label(label)))
        set_size_request!(child, Vector2f(150, 150))
        set_margin!(child, 10)
        return child
    end

    main() do app::Application
        window = Window(app)

        stack = Stack()

        add_child!(stack, generate_child("Child #01"), "Page #01")
        add_child!(stack, generate_child("Child #02"), "Page #02")
        add_child!(stack, generate_child("Child #03"), "Page #03")

        stack_model = get_selection_model(stack)
        connect_signal_selection_changed!(stack_model, stack) do x::SelectionModel, position::Integer, n_items::Integer, stack::Stack
            println("Current stack page is now: $(get_child_at(stack, position))")
        end

        set_child!(window, vbox(stack, StackSwitcher(stack))) # create StackSwitcher from stack
        present!(window)
    end
    ```

`StackSwitcher` has no other methods or properties, though it provides the signals that all widgets share.

### StackSidebar

[`StackSidebar`](@ref) has the same purpose as `StackSwitcher`, except it displays the list of stack pages as a vertical list:

!!! details "How to generate this image"
    ```julia
    function generate_child(label::String) ::Widget
        child = Frame(Overlay(Separator(), Label(label)))
        set_size_request!(child, Vector2f(150, 150))
        set_margin!(child, 10)
        return child
    end

    main() do app::Application
        window = Window(app)

        stack = Stack()

        add_child!(stack, generate_child("Child #01"), "Page #01")
        add_child!(stack, generate_child("Child #02"), "Page #02")
        add_child!(stack, generate_child("Child #03"), "Page #03")

        stack_model = get_selection_model(stack)
        connect_signal_selection_changed!(stack_model, stack) do x::SelectionModel, position::Integer, n_items::Integer, stack::Stack
            println("Current stack page is now: $(get_child_at(stack, position))")
        end

        set_child!(window, vbox(stack, StackSwitcher(stack))) # Create StackSidebar from stack
        present!(window)
    end
    ```

Other than this visual component, its purpose is identical to that of `StackSwitcher`.

### Transition Animation

When 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:

+ [`set_transition_duration!`](@ref) chooses how long the animation will take to complete
+ [`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
+ [`set_transition_type!`](@ref) governs the type of animation, which is one of the enum values of [`StackTransitionType`](@ref).

If 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.

---

Moving 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.

## Grid

Not to be confused with `GridView`, [`Grid`](@ref) arranges its children in a **non-uniform** grid:

![](../assets/grid.png)

!!! details "How to generate this image"
    ```julia
    function generate_child(label::String) ::Widget
        child = Frame(Overlay(Separator(), Label(label)))
        set_size_request!(child, Vector2f(50, 50))
        return child
    end

    main() do app::Application

        window = Window(app)
        
        grid = Grid()

        Mousetrap.insert_at!(grid, generate_child("01"), 1, 1, 2, 1)
        Mousetrap.insert_at!(grid, generate_child("02"), 3, 1, 1, 2)
        Mousetrap.insert_at!(grid, generate_child("03"), 4, 1, 1, 1)
        Mousetrap.insert_at!(grid, generate_child("04"), 1, 2, 1, 2)
        Mousetrap.insert_at!(grid, generate_child("05"), 2, 2, 1, 1)
        Mousetrap.insert_at!(grid, generate_child("06"), 4, 2, 1, 1)
        Mousetrap.insert_at!(grid, generate_child("07"), 2, 3, 3, 1)
    
        set_margin!(grid, 10)
        set_child!(window, grid)
        present!(window)
    end
    ```

Each widget in the grid has an x- and y-position, along with a widget and height, both measured in **number of cells** . 

For 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. 

```julia
Mousetrap.insert_at!(
    grid, 
    widget_05,
    2,  # x-position
    2,  # y-position
    1,  # width
    1   # height
)
```

Meanwhile, the widget labeled `07` has an x-position of 2, y-position of 3, width of 3 cells, and height of 1 cell.

```julia
Mousetrap.insert_at!(
    grid, 
    widget_07, 
    2,  # x-position
    3,  # y-position
    3,  # width
    1   # height
)
```

When using `insert_at!`, we have to make sure that no two widgets overlap.

For 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:

```julia
child = # ...

# insert `child` at position (1, 1)
insert_at!(grid, child, 1, 1)

new_widget = # ...
# insert new widget left of child
insert_next_to!(grid, new_widget, child, RELATIVE_POSITION_LEFT_OF)
```

Where we omitted the width and height in cells, which default to `1` respectively. 

Note 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.

Lastly, `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.

`Grid` offers a more flexible, but also more manual way of arranging widgets in 2D space.

---

## Notebook

[`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.

Each 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.

We add pages using `push_back!`, `push_front!` or `insert_at!`, which take the child and label widget as their arguments:

```julia
notebook = Notebook()

push_back!(notebook, child_01, Label("Label #01"))
push_back!(notebook, child_02, Label("Label #02"))
push_back!(notebook, child_03, Label("Label #03"))
```
![](../assets/notebook.png)

!!! details "How to generate this image"
    ```julia
    function generate_child(label::String) ::Widget
        child = Frame(Overlay(Separator(), Label(label)))
        set_size_request!(child, Vector2f(150, 150))
        set_margin!(child, 10)
        return child
    end

    main() do app::Application
        window = Window(app)

        notebook = Notebook()

        push_back!(notebook, generate_child("Child #01"), Label("Label #01"))
        push_back!(notebook, generate_child("Child #02"), Label("Label #02"))
        push_back!(notebook, generate_child("Child #03"), Label("Label #03"))

        set_child!(window, notebook)
        present!(window)
    end
    ```

We 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.

If 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.

To 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: 

```@eval
using Mousetrap
Mousetrap.@signal_table(Notebook,
    page_added,
    page_removed,
    page_reordered,
    page_selection_changed
)
```

For example, if we want to react to the currently selected page changing, we would connect to signal `page_selection_changed` like so:

```julia
notebook = Notebook()
connect_signal_page_selection_changed!(notebook) do self::Notebook, index::Integer
    println("Page #$index is now selected")
end
```

---

## Compound Widgets

Now 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**.

A compound widget is a widget that groups many other, pre-made widgets together. Compound widgets give an easy mechanism to 
logically group a collection of widgets and treat them as a whole, instead of having to operate on each of its parts individually.

In order for a Julia object to be considered a `Widget`, that is, all functions that take a `Mousetrap.Widge
Download .txt
gitextract_bwbj8psc/

├── .github/
│   └── workflows/
│       └── CI.yml
├── .gitignore
├── Artifacts.toml
├── LICENSE
├── Manifest.toml
├── Project.toml
├── README.md
├── docs/
│   ├── make.jl
│   └── src/
│       ├── 01_manual/
│       │   ├── 01_installation.md
│       │   ├── 02_signals.md
│       │   ├── 03_actions.md
│       │   ├── 04_widgets.md
│       │   ├── 05_event_handling.md
│       │   ├── 06_image.md
│       │   ├── 07_os_interface.md
│       │   ├── 08_menus.md
│       │   ├── 09_native_rendering.md
│       │   ├── 10_theme_customization.md
│       │   ├── 11_app_distribution.md
│       │   └── 12_opengl_integration.md
│       ├── assets/
│       │   ├── animation_fade_out.webm
│       │   ├── animation_spin.webm
│       │   ├── css_style_animation_spin.webm
│       │   └── mousetrip.webm
│       └── index.md
├── jll/
│   ├── build_tarballs.jl
│   ├── build_tarballs.jl.in
│   └── deploy.jl
├── logo.xcf
├── src/
│   ├── Mousetrap.jl
│   ├── docgen/
│   │   ├── enums.jl
│   │   ├── functions.jl
│   │   ├── signals.jl
│   │   └── types.jl
│   ├── docs.jl
│   └── key_codes.jl
└── test/
    ├── example.jl
    ├── makie_test.jl
    └── runtests.jl
Condensed preview — 39 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,223K chars).
[
  {
    "path": ".github/workflows/CI.yml",
    "chars": 1477,
    "preview": "name: CI\non:\n  push:\n  pull_request:  \n  \ndefaults:\n  run:\n    shell: bash\n    \njobs:\n    test:\n      name: Julia ${{ ma"
  },
  {
    "path": ".gitignore",
    "chars": 151,
    "preview": "\ndocs/build/\ndocs/mousetrap\ndocs/src/02_library/classes.md\ndocs/src/02_library/enums.md\ndocs/src/02_library/functions.md"
  },
  {
    "path": "Artifacts.toml",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "LICENSE",
    "chars": 7652,
    "preview": "                   GNU LESSER GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007"
  },
  {
    "path": "Manifest.toml",
    "chars": 34969,
    "preview": "# This file is machine-generated - editing it directly is not advised\n\njulia_version = \"1.9.3\"\nmanifest_format = \"2.0\"\np"
  },
  {
    "path": "Project.toml",
    "chars": 854,
    "preview": "name = \"Mousetrap\"\nuuid = \"5deeb4b9-6e04-4da7-8b7f-c77fb1eae65e\"\nauthors = [\"C.Cords <mail@clemens-cords.com>\"]\nversion "
  },
  {
    "path": "README.md",
    "chars": 7338,
    "preview": "# Mousetrap\n\n![](docs/src/assets/banner.png)\n\nMousetrap is a GUI library designed for Julia. It fully wraps [GTK4](https"
  },
  {
    "path": "docs/make.jl",
    "chars": 4639,
    "preview": "#\n# Author: C. Cords (mail@clemens-cords.com)\n# https://github.com/clemapfel/mousetrap.jl\n#\n# Copyright © 2023, Licensed"
  },
  {
    "path": "docs/src/01_manual/01_installation.md",
    "chars": 13309,
    "preview": "# Chapter 1: Installation & Workflow\n\nIn this chapter, we will learn:\n+ How to install Mousetrap.jl\n+ How to create our "
  },
  {
    "path": "docs/src/01_manual/02_signals.md",
    "chars": 18282,
    "preview": "# Chapter 2: Signals\n\nIn this chapter, we will learn:\n+ What signals and signal handlers are\n+ How to connect to a signa"
  },
  {
    "path": "docs/src/01_manual/03_actions.md",
    "chars": 11822,
    "preview": "# Chapter 3: Actions\n\nIn this chapter, we will learn:\n+ How and why to use the command pattern to encapsulate applicatio"
  },
  {
    "path": "docs/src/01_manual/04_widgets.md",
    "chars": 104281,
    "preview": "# Chapter 4: Widgets\n\nIn this chapter, we will learn:\n+ What a widget is\n+ Properties that all widgets share\n+ What widg"
  },
  {
    "path": "docs/src/01_manual/05_event_handling.md",
    "chars": 38021,
    "preview": "# Chapter 5: Event Handling\n\nIn this chapter, we will learn:\n+ What input focus is\n+ What an event controller is\n+ How t"
  },
  {
    "path": "docs/src/01_manual/06_image.md",
    "chars": 13944,
    "preview": "# 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 c"
  },
  {
    "path": "docs/src/01_manual/07_os_interface.md",
    "chars": 45396,
    "preview": "# Chapter 7: Operating System Interface\n\nIn this chapter, we will learn:\n+ What other features `Application` has\n+ How t"
  },
  {
    "path": "docs/src/01_manual/08_menus.md",
    "chars": 15356,
    "preview": "# Chapter 8: Menus\n\nIn this chapter, we will learn:\n+ How to create complex, nested menus\n+ How to display menus using `"
  },
  {
    "path": "docs/src/01_manual/09_native_rendering.md",
    "chars": 48468,
    "preview": "# 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"
  },
  {
    "path": "docs/src/01_manual/10_theme_customization.md",
    "chars": 18334,
    "preview": "# Chapter 10: Theme & Widget Customization\n\nIn this chapter, we will learn:\n+ How to swap between light- and dark- mode\n"
  },
  {
    "path": "docs/src/01_manual/11_app_distribution.md",
    "chars": 465,
    "preview": "# Chapter 11: App Distribution\n\nIn this chapter, we will learn:\n+ How to load and store assets\n+ How to bundle our app f"
  },
  {
    "path": "docs/src/01_manual/12_opengl_integration.md",
    "chars": 19090,
    "preview": "# Chapter 12: OpenGL Integration & Makie Support\n\nIn this chapter, we will learn:\n+ How to use `GLArea` to allow foreign"
  },
  {
    "path": "docs/src/index.md",
    "chars": 6021,
    "preview": "# Mousetrap\n\nWelcome to the documentation of Mousetrap.jl, a GUI engine for Julia. \nMousetrap was created and designed b"
  },
  {
    "path": "jll/build_tarballs.jl",
    "chars": 2195,
    "preview": "# Note that this script can accept some limited command-line arguments, run\n# `julia build_tarballs.jl --help` to see a "
  },
  {
    "path": "jll/build_tarballs.jl.in",
    "chars": 2148,
    "preview": "# Note that this script can accept some limited command-line arguments, run\n# `julia build_tarballs.jl --help` to see a "
  },
  {
    "path": "jll/deploy.jl",
    "chars": 1600,
    "preview": "#throw(AssertionError(\"In Mousetrap/jll/deploy.jl: This script is meant for internal use only and should not be tampered"
  },
  {
    "path": "src/Mousetrap.jl",
    "chars": 211918,
    "preview": "#\n# Author: C. Cords (mail@clemens-cords.com)\n# GitHub: https://github.com/clemapfel/mousetrap.jl\n# Documentation: https"
  },
  {
    "path": "src/docgen/enums.jl",
    "chars": 30145,
    "preview": "#\n# Author: C. Cords (mail@clemens-cords.com)\n# GitHub: https://github.com/clemapfel/mousetrap.jl\n# Documentation: https"
  },
  {
    "path": "src/docgen/functions.jl",
    "chars": 146383,
    "preview": "#\n# Author: C. Cords (mail@clemens-cords.com)\n# GitHub: https://github.com/clemapfel/mousetrap.jl\n# Documentation: https"
  },
  {
    "path": "src/docgen/signals.jl",
    "chars": 14747,
    "preview": "#\n# Author: C. Cords (mail@clemens-cords.com)\n# GitHub: https://github.com/clemapfel/mousetrap.jl\n# Documentation: https"
  },
  {
    "path": "src/docgen/types.jl",
    "chars": 57854,
    "preview": "#\n# Author: C. Cords (mail@clemens-cords.com)\n# GitHub: https://github.com/clemapfel/mousetrap.jl\n# Documentation: https"
  },
  {
    "path": "src/docs.jl",
    "chars": 9317,
    "preview": "#\n# Author: C. Cords (mail@clemens-cords.com)\n# GitHub: https://github.com/clemapfel/mousetrap.jl\n# Documentation: https"
  },
  {
    "path": "src/key_codes.jl",
    "chars": 161971,
    "preview": "const KEY_VoidSymbol = detail.KEY_VoidSymbol\nexport KEY_VoidSymbol\n\nconst KEY_BackSpace = detail.KEY_BackSpace\nexport KE"
  },
  {
    "path": "test/example.jl",
    "chars": 19957,
    "preview": "# File used for debugging and for dosc examples, why are you snooping through this file?\n\n# File used for debugging and "
  },
  {
    "path": "test/makie_test.jl",
    "chars": 9856,
    "preview": "\"\"\"\nMinimum working example showing how to display a GLMakie plot using Mousetrap `GLArea`\n\"\"\"\nmodule MousetrapMakie\n\n  "
  },
  {
    "path": "test/runtests.jl",
    "chars": 96924,
    "preview": "#\n# Author: C. Cords (mail@clemens-cords.com)\n# https://github.com/clemapfel/mousetrap.jl\n#\n# Copyright © 2023, Licensed"
  }
]

// ... and 5 more files (download for full content)

About this extraction

This page contains the full source code of the Clemapfel/mousetrap.jl GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 39 files (1.1 MB), approximately 305.2k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!