diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0dd1304a00..0d8a032109 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,7 +9,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [macOS-10.14, windows-2019] + os: [macOS-10.14, windows-2019, ubuntu-latest] name: cargo check stable steps: - uses: actions/checkout@v1 @@ -17,6 +17,12 @@ jobs: run: brew install cairo if: contains(matrix.os, 'mac') + - name: install libgtk-dev + run: | + sudo apt update + sudo apt install libgtk-3-dev + if: contains(matrix.os, 'ubuntu') + - name: install stable toolchain uses: actions-rs/toolchain@v1 with: @@ -77,7 +83,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [macOS-10.14, windows-2019] + os: [macOS-10.14, windows-2019, ubuntu-latest] name: cargo test stable steps: - uses: actions/checkout@v1 @@ -86,6 +92,12 @@ jobs: run: brew install cairo if: contains(matrix.os, 'mac') + - name: install libgtk-dev + run: | + sudo apt update + sudo apt install libgtk-3-dev + if: contains(matrix.os, 'ubuntu') + - name: install stable toolchain uses: actions-rs/toolchain@v1 with: @@ -108,7 +120,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [macOS-10.14, windows-2019] + os: [macOS-10.14, windows-2019, ubuntu-latest] name: cargo test nightly steps: - uses: actions/checkout@v1 @@ -117,6 +129,12 @@ jobs: run: brew install cairo if: contains(matrix.os, 'mac') + - name: install libgtk-dev + run: | + sudo apt update + sudo apt install libgtk-3-dev + if: contains(matrix.os, 'ubuntu') + - name: install nightly toolchain uses: actions-rs/toolchain@v1 with: @@ -140,7 +158,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [macOS-10.14, windows-2019] + os: [macOS-10.14, windows-2019, ubuntu-latest] steps: - uses: actions/checkout@v1 @@ -148,6 +166,12 @@ jobs: run: brew install cairo if: contains(matrix.os, 'mac') + - name: install libgtk-dev + run: | + sudo apt update + sudo apt install libgtk-3-dev + if: contains(matrix.os, 'ubuntu') + - name: install nightly toolchain uses: actions-rs/toolchain@v1 with: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 06972548b9..0e7792497b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,6 +12,5 @@ information on using pull requests. If your name does not already appear in the [AUTHORS] file, please feel free to add it as part of your patch. -[gtk-rs dependencies]: http://gtk-rs.org/docs/requirements.html [GitHub Help]: https://help.github.com/articles/about-pull-requests/ [AUTHORS]: AUTHORS diff --git a/Cargo.lock b/Cargo.lock index 0f042ecf74..44ed4ebfc9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,30 @@ dependencies = [ "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "atk" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "atk-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "atk-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "autocfg" version = "0.1.6" @@ -60,6 +84,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "cairo-sys-rs 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -68,6 +95,7 @@ name = "cairo-sys-rs" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -207,6 +235,13 @@ dependencies = [ "core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)", "direct2d 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "directwrite 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "gdk 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gdk-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gio 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gtk 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gtk-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -293,11 +328,176 @@ name = "foreign-types-shared" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "fragile" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "fuchsia-cprng" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "gdk" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cairo-rs 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cairo-sys-rs 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gdk-pixbuf 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gdk-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gio 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gio-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "pango 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gdk-pixbuf-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gio 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gio-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gio-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gdk-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cairo-sys-rs 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gdk-pixbuf-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gio-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "pango-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gio" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "fragile 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gio-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gio-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "glib" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "glib-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gobject-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gtk" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "atk 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cairo-rs 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cairo-sys-rs 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", + "gdk 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gdk-pixbuf 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gdk-pixbuf-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gdk-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gio 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gio-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gtk-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "pango 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pango-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gtk-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "atk-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cairo-sys-rs 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gdk-pixbuf-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gdk-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gio-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "pango-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "heck" version = "0.3.1" @@ -461,6 +661,31 @@ dependencies = [ "malloc_buf 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "pango" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "pango-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pango-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "phf" version = "0.7.24" @@ -861,6 +1086,8 @@ dependencies = [ [metadata] "checksum arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba" +"checksum atk 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "86b7499272acf036bb5820c6e346bbfb5acc5dceb104bc2c4fd7e6e33dfcde6a" +"checksum atk-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067531f752c01027f004032bb676e715aba74b75e904a7340a61ce3fb0b61b0" "checksum autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b671c8fb71b457dd4ae18c4ba1e59aa81793daacc361d82fcd410cef0d491875" "checksum backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)" = "690a62be8920ccf773ee00ef0968649b0e724cda8bd5b12286302b4ae955fdf5" "checksum backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "82a830b4ef2d1124a711c71d263c5abdc710ef8e907bd508c88be475cebc422b" @@ -889,7 +1116,19 @@ dependencies = [ "checksum fluent-syntax 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cd33f0ec4141fae9f6d6183c30504275b8a4c843b02517d17098593244ad4617" "checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" "checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +"checksum fragile 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f8140122fa0d5dcb9fc8627cfce2b37cc1500f752636d46ea28bc26785c2f9" "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" +"checksum gdk 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6243e995f41f3a61a31847e54cc719edce93dd9140c89dca3b9919be1cfe22d5" +"checksum gdk-pixbuf 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9726408ee1bbada83094326a99b9c68fea275f9dbb515de242a69e72051f4fcc" +"checksum gdk-pixbuf-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b1d6778abf5764b9080a9345a16c5d16289426a3b3edd808a29a9061d431c465" +"checksum gdk-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ebe06357212127f50575b535bdb04638f5d375bb41062287abc6c94e5b8067b" +"checksum gio 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6261b5d34c30c2d59f879e643704cf54cb44731f3a2038000b68790c03e360e3" +"checksum gio-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "778b856a70a32e2cc5dd5cc7fa1b0c4b6df924fdf5c82984bc28f30565657cfe" +"checksum glib 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "91a70db179515473b57aaff8b879167f1f8460bc5523e97beacf6d1026a8b99d" +"checksum glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4b86a9169fbc9cf9a0ef315039c2304b09d5c575c5fde7defba3576a0311b863" +"checksum gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "61d55bc9202447ca776f6ad0048c36e3312010f66f82ab478e97513e93f3604b" +"checksum gtk 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "709f1074259d4685b96133f92b75c7f35b504715b0fcdc96ec95de2607296a60" +"checksum gtk-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bbd9395497ae1d1915d1d6e522d51ae8745bf613906c34ac191c411250fc4025" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" "checksum intl_pluralrules 4.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6be21c467bf1cecc9094cb899718581e0f51240407da5626e0efcc2f23e3500f" "checksum js-sys 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)" = "2cc9a97d7cec30128fd8b28a7c1f9df1c001ceb9b441e2b755e24130a6b43c79" @@ -910,6 +1149,8 @@ dependencies = [ "checksum num-rational 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "ee314c74bd753fc86b4780aa9475da469155f3848473a261d2d18e35245a784e" "checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" "checksum objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "31d20fd2b37e07cf5125be68357b588672e8cefe9a96f8c17a9d46053b3e590d" +"checksum pango 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "393fa071b144f8ffb83ede273758983cf414ca3c0b1d2a5a9ce325b3ba3dd786" +"checksum pango-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1ee97abcad820f9875e032656257ad1c790e7b11a0e6ce2516a8f5b0d8f8213f" "checksum phf 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b3da44b85f8e8dfaec21adae67f95d93244b2ecf6ad2a692320598dcc8e6dd18" "checksum phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0" "checksum piet 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "02e36470db0f6e8900c3f2d35ae137c96ebb726af2c070fc4369467ee57ab9bd" diff --git a/Cargo.toml b/Cargo.toml index bcebc4674d..c5b50f6fbd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,9 @@ edition = "2018" [package.metadata.docs.rs] default-target = "x86_64-pc-windows-msvc" +[features] +use_gtk = ["druid-shell/use_gtk"] + [badges] travis-ci = { repository = "xi-editor/druid" } diff --git a/README.md b/README.md index 9dcab5b1bb..9fd8b134a7 100644 --- a/README.md +++ b/README.md @@ -42,9 +42,6 @@ uses Direct2D (and DirectWrite for text). One way forward is to create a [2d graphics] abstraction. ## Build -Currently, druid only builds on Windows and macOS. - -Other options may work, but not tested. #### Windows @@ -59,6 +56,11 @@ You may also need to set your `PKG_CONFIG_PATH`; assuming you have installed `ca ```shell $> PKG_CONFIG_PATH="/usr/local/opt/libffi/lib/pkgconfig" cargo build ``` +#### Linux + +On Linux, druid requires gtk+3; see[gtk-rs dependencies] for installation instructions. + +run `cargo build` ## Alternatives @@ -89,3 +91,4 @@ The main author is Raph Levien. [ggez]: https://github.com/ggez/ggez [CONTRIBUTING.md]: CONTRIBUTING.md [Zulip chat instance]: https://xi.zulipchat.com +[gtk-rs dependencies]: http://gtk-rs.org/docs/requirements.html diff --git a/druid-shell/Cargo.lock b/druid-shell/Cargo.lock index f04287e976..16346c9429 100644 --- a/druid-shell/Cargo.lock +++ b/druid-shell/Cargo.lock @@ -8,6 +8,30 @@ dependencies = [ "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "atk" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "atk-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "atk-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "autocfg" version = "0.1.5" @@ -60,6 +84,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "cairo-sys-rs 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -68,6 +95,7 @@ name = "cairo-sys-rs" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -162,6 +190,14 @@ dependencies = [ "core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)", "direct2d 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "directwrite 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "gdk 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gdk-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gio 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gtk 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gtk-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "kurbo 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -220,11 +256,176 @@ name = "foreign-types-shared" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "fragile" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "fuchsia-cprng" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "gdk" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cairo-rs 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cairo-sys-rs 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gdk-pixbuf 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gdk-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gio 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gio-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "pango 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gdk-pixbuf-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gio 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gio-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gio-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gdk-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cairo-sys-rs 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gdk-pixbuf-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gio-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "pango-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gio" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "fragile 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gio-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gio-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "glib" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "glib-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gobject-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gtk" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "atk 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cairo-rs 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cairo-sys-rs 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", + "gdk 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gdk-pixbuf 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gdk-pixbuf-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gdk-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gio 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gio-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gtk-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "pango 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pango-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gtk-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "atk-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cairo-sys-rs 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gdk-pixbuf-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gdk-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gio-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "pango-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "heck" version = "0.3.1" @@ -241,6 +442,14 @@ dependencies = [ "wasm-bindgen 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "kurbo" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "kurbo" version = "0.5.1" @@ -373,6 +582,31 @@ dependencies = [ "malloc_buf 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "pango" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "pango-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pango-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "piet" version = "0.0.6" @@ -661,6 +895,8 @@ dependencies = [ [metadata] "checksum arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba" +"checksum atk 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "86b7499272acf036bb5820c6e346bbfb5acc5dceb104bc2c4fd7e6e33dfcde6a" +"checksum atk-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067531f752c01027f004032bb676e715aba74b75e904a7340a61ce3fb0b61b0" "checksum autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "22130e92352b948e7e82a49cdb0aa94f2211761117f29e052dd397c1ac33542b" "checksum backtrace 0.3.34 (registry+https://github.com/rust-lang/crates.io-index)" = "b5164d292487f037ece34ec0de2fcede2faa162f085dd96d2385ab81b12765ba" "checksum backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "82a830b4ef2d1124a711c71d263c5abdc710ef8e907bd508c88be475cebc422b" @@ -685,9 +921,22 @@ dependencies = [ "checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" "checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" "checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +"checksum fragile 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f8140122fa0d5dcb9fc8627cfce2b37cc1500f752636d46ea28bc26785c2f9" "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" +"checksum gdk 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6243e995f41f3a61a31847e54cc719edce93dd9140c89dca3b9919be1cfe22d5" +"checksum gdk-pixbuf 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9726408ee1bbada83094326a99b9c68fea275f9dbb515de242a69e72051f4fcc" +"checksum gdk-pixbuf-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b1d6778abf5764b9080a9345a16c5d16289426a3b3edd808a29a9061d431c465" +"checksum gdk-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ebe06357212127f50575b535bdb04638f5d375bb41062287abc6c94e5b8067b" +"checksum gio 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6261b5d34c30c2d59f879e643704cf54cb44731f3a2038000b68790c03e360e3" +"checksum gio-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "778b856a70a32e2cc5dd5cc7fa1b0c4b6df924fdf5c82984bc28f30565657cfe" +"checksum glib 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "be27232841baa43e0fd5ae003f7941925735b2f733a336dc75f07b9eff415e7b" +"checksum glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4b86a9169fbc9cf9a0ef315039c2304b09d5c575c5fde7defba3576a0311b863" +"checksum gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "61d55bc9202447ca776f6ad0048c36e3312010f66f82ab478e97513e93f3604b" +"checksum gtk 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "709f1074259d4685b96133f92b75c7f35b504715b0fcdc96ec95de2607296a60" +"checksum gtk-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bbd9395497ae1d1915d1d6e522d51ae8745bf613906c34ac191c411250fc4025" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" "checksum js-sys 0.3.25 (registry+https://github.com/rust-lang/crates.io-index)" = "da3ea71161651a4cd97d999b2da139109c537b15ab33abc8ae4ead38deac8a03" +"checksum kurbo 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e6076333105a72e8d2c227ba6a6da0dc3c8e5f53f02053f598a6087a1ea8991" "checksum kurbo 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2f0caeb26248a62abf92dea93aad4f8244f54668e2f1060ed9cd9fd1d5545723" "checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" "checksum libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)" = "d44e80633f007889c7eff624b709ab43c92d708caad982295768a7b13ca3b5eb" @@ -704,6 +953,8 @@ dependencies = [ "checksum num-rational 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "ee314c74bd753fc86b4780aa9475da469155f3848473a261d2d18e35245a784e" "checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" "checksum objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "31d20fd2b37e07cf5125be68357b588672e8cefe9a96f8c17a9d46053b3e590d" +"checksum pango 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "393fa071b144f8ffb83ede273758983cf414ca3c0b1d2a5a9ce325b3ba3dd786" +"checksum pango-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1ee97abcad820f9875e032656257ad1c790e7b11a0e6ce2516a8f5b0d8f8213f" "checksum piet 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "02e36470db0f6e8900c3f2d35ae137c96ebb726af2c070fc4369467ee57ab9bd" "checksum piet-cairo 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1afd8ad4a74d1ef1591e0bad7f860841c5a0cab6edeb347fc67e2e37422c01df" "checksum piet-common 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "8e8cb78927118d65d350a677432e459d90ff72ea6fc875cf1ac1478d5570e196" diff --git a/druid-shell/Cargo.toml b/druid-shell/Cargo.toml index 8692cba167..a6a51fb586 100644 --- a/druid-shell/Cargo.toml +++ b/druid-shell/Cargo.toml @@ -8,6 +8,9 @@ repository = "https://github.com/xi-editor/druid" categories = ["os::macos-apis", "os::windows-apis", "gui"] edition = "2018" +[features] +use_gtk = ["gtk", "gio", "gdk", "gdk-sys", "glib", "glib-sys", "cairo-rs"] + [package.metadata.docs.rs] default-target = "x86_64-pc-windows-msvc" @@ -18,6 +21,16 @@ log = "0.4.8" lazy_static = "1.0" time = "0.1.39" +cairo-rs = { version = "0.7.1", default_features = false, optional = true } +gio = { version = "0.7.0", optional = true } +gdk = { version = "0.11.0", optional = true } +gdk-sys = { version = "0.9.0", optional = true } +gtk = { version = "0.7.0", optional = true } +glib = { version = "0.8.1", optional = true } +glib-sys = { version = "0.9.0", optional = true } +gtk-sys = { version = "0.9.0", optional = true } + + [target.'cfg(target_os="windows")'.dependencies] directwrite = "0.1.2" direct2d = "0.2.0" @@ -32,3 +45,19 @@ cocoa = "0.19.0" objc = "0.2.5" core-graphics = "0.17.3" cairo-rs = { version = "0.7.1", default_features = false } + +[target.'cfg(target_os="linux")'.dependencies] +cairo-rs = { version = "0.7.1", default_features = false } +gio = "0.7.0" +gdk = "0.11.0" +gdk-sys = "0.9.0" +glib = "0.8.1" +glib-sys = "0.9.0" +gtk-sys = "0.9.0" + +[target.'cfg(target_os="linux")'.dependencies.gtk] +version = "0.7.0" +features = ["v3_20"] + +[dev-dependencies] +kurbo = "0.2.1" diff --git a/druid-shell/examples/hello.rs b/druid-shell/examples/hello.rs index 20fc9f2e01..1fbe46a595 100644 --- a/druid-shell/examples/hello.rs +++ b/druid-shell/examples/hello.rs @@ -140,6 +140,7 @@ fn main() { builder.set_title("Hello example"); builder.set_menu(menubar); let window = builder.build().unwrap(); + window.show(); run_loop.run(); } diff --git a/druid-shell/src/common_util.rs b/druid-shell/src/common_util.rs new file mode 100644 index 0000000000..5b750802c9 --- /dev/null +++ b/druid-shell/src/common_util.rs @@ -0,0 +1,37 @@ +// Copyright 2019 The xi-editor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Common functions used by the backends + +/// Strip the access keys from the menu string. +/// +/// Changes "E&xit" to "Exit". Actual ampersands are escaped as "&&". +#[cfg(any(target_os = "macos", target_os = "linux"))] +pub fn strip_access_key(raw_menu_text: &str) -> String { + // TODO this is copied from mac/menu.rs maybe this should be moved somewhere common? + let mut saw_ampersand = false; + let mut result = String::new(); + for c in raw_menu_text.chars() { + if c == '&' { + if saw_ampersand { + result.push(c); + } + saw_ampersand = !saw_ampersand; + } else { + result.push(c); + saw_ampersand = false; + } + } + result +} diff --git a/druid-shell/src/error.rs b/druid-shell/src/error.rs index 429fb83940..b0bd275843 100644 --- a/druid-shell/src/error.rs +++ b/druid-shell/src/error.rs @@ -22,7 +22,7 @@ use winapi::shared::winerror::HRESULT; /// Error codes. At the moment, this is little more than HRESULT, but that /// might change. pub enum Error { - Null, + Other(&'static str), #[cfg(target_os = "windows")] Hr(HRESULT), // Maybe include the full error from the direct2d crate. @@ -36,7 +36,7 @@ pub enum Error { impl fmt::Debug for Error { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { match *self { - Error::Null => write!(f, "Null error"), + Error::Other(s) => write!(f, "{}", s), #[cfg(target_os = "windows")] Error::Hr(hr) => write!(f, "HRESULT 0x{:x}", hr), #[cfg(target_os = "windows")] diff --git a/druid-shell/src/gtk/application.rs b/druid-shell/src/gtk/application.rs new file mode 100644 index 0000000000..aceb689b75 --- /dev/null +++ b/druid-shell/src/gtk/application.rs @@ -0,0 +1,36 @@ +// Copyright 2019 The xi-editor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! GTK implementation of features at the application scope. + +use crate::clipboard::ClipboardItem; + +pub struct Application; + +impl Application { + pub fn quit() { + // Nothing to do: if this is called, we're already shutting down and GTK will pick it up (I hope?) + } + + pub fn get_clipboard_contents() -> Option { + let display = gdk::Display::get_default().unwrap(); + let clipboard = gtk::Clipboard::get_default(&display).unwrap(); + + if let Some(gstring) = clipboard.wait_for_text() { + Some(ClipboardItem::Text(gstring.to_string())) + } else { + None + } + } +} diff --git a/druid-shell/src/gtk/dialog.rs b/druid-shell/src/gtk/dialog.rs new file mode 100644 index 0000000000..e970b0d649 --- /dev/null +++ b/druid-shell/src/gtk/dialog.rs @@ -0,0 +1,71 @@ +// Copyright 2019 The xi-editor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! File open/save dialogs, GTK implementation. + +use std::ffi::OsString; + +use crate::dialog::FileDialogOptions; +use gtkrs::{FileChooserAction, FileChooserExt, NativeDialogExt, Window}; + +use crate::Error; + +/// Type of file dialog. +pub enum FileDialogType { + /// File open dialog. + Open, + /// File save dialog. + Save, +} + +pub(crate) fn open_file_sync( + window: &Window, + ty: FileDialogType, + options: FileDialogOptions, +) -> Result { + // TODO: support message localization + let (title, action) = match ty { + FileDialogType::Open => ("Open File", FileChooserAction::Open), + FileDialogType::Save => ("Save File", FileChooserAction::Save), + }; + + let dialog = gtkrs::FileChooserNativeBuilder::new() + .transient_for(window) + .title(title) + .build(); + + dialog.set_action(action); + + dialog.set_show_hidden(options.show_hidden); + + let result = dialog.run(); + + let result = match result { + gtk_sys::GTK_RESPONSE_ACCEPT => match dialog.get_filename() { + Some(path) => Ok(path.into_os_string()), + None => Err(Error::Other("No path received for filename")), + }, + gtk_sys::GTK_RESPONSE_CANCEL => Err(Error::Other("Dialog was deleted")), + _ => { + eprintln!("Unhandled dialog result: {:?}", result); + Err(Error::Other("Unhandled dialog result")) + } + }; + + // TODO properly handle errors into the Error type + + dialog.destroy(); + + result +} diff --git a/druid-shell/src/gtk/menu.rs b/druid-shell/src/gtk/menu.rs new file mode 100644 index 0000000000..aca6fa787a --- /dev/null +++ b/druid-shell/src/gtk/menu.rs @@ -0,0 +1,163 @@ +// Copyright 2019 The xi-editor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! GTK implementation of menus. + +use gdk::ModifierType; +use gtkrs::AccelGroup; +use gtkrs::GtkMenuExt; +use gtkrs::Menu as GtkMenu; +use gtkrs::MenuBar as GtkMenuBar; +use gtkrs::MenuItem as GtkMenuItem; +use gtkrs::{GtkMenuItemExt, MenuShellExt, SeparatorMenuItemBuilder, WidgetExt}; + +use crate::common_util::strip_access_key; +use crate::gtk::WinCtxImpl; +use crate::hotkey::{HotKey, KeyCompare, RawMods}; +use crate::keyboard::KeyModifiers; +use crate::platform::WindowHandle; + +#[derive(Default, Debug)] +pub struct Menu { + items: Vec, +} + +#[derive(Debug)] +enum MenuItem { + Entry(String, u32, Option), + SubMenu(String, Menu), + Separator, +} + +impl Menu { + pub fn new() -> Menu { + Menu { items: Vec::new() } + } + + pub fn new_for_popup() -> Menu { + Menu { items: Vec::new() } + } + + pub fn add_dropdown(&mut self, menu: Menu, text: &str, _enabled: bool) { + // TODO: implement enabled dropdown + self.items + .push(MenuItem::SubMenu(strip_access_key(text), menu)); + } + + pub fn add_item( + &mut self, + id: u32, + text: &str, + key: Option<&HotKey>, + _enabled: bool, + _selected: bool, + ) { + // TODO: implement enabled, selected item + self.items.push(MenuItem::Entry( + strip_access_key(text), + id, + key.map(|k| k.clone()), + )); + } + + pub fn add_separator(&mut self) { + self.items.push(MenuItem::Separator) + } + + fn append_items_to_menu>( + self, + menu: &mut M, + handle: &WindowHandle, + accel_group: &AccelGroup, + ) { + for item in self.items { + match item { + MenuItem::Entry(name, id, key) => { + let item = GtkMenuItem::new_with_label(&name); + + if let Some(k) = key { + register_accelerator(&item, accel_group, k); + } + + let handle = handle.clone(); + item.connect_activate(move |_| { + let mut ctx = WinCtxImpl::from(&handle); + + if let Some(state) = handle.state.upgrade() { + state.handler.borrow_mut().command(id, &mut ctx); + } + }); + + menu.append(&item); + } + MenuItem::SubMenu(name, submenu) => { + let item = GtkMenuItem::new_with_label(&name); + item.set_submenu(Some(&submenu.into_gtk_menu(handle, accel_group))); + + menu.append(&item); + } + MenuItem::Separator => menu.append(&SeparatorMenuItemBuilder::new().build()), + } + } + } + + pub(crate) fn into_gtk_menubar( + self, + handle: &WindowHandle, + accel_group: &AccelGroup, + ) -> GtkMenuBar { + let mut menu = GtkMenuBar::new(); + + self.append_items_to_menu(&mut menu, handle, accel_group); + + menu + } + + pub fn into_gtk_menu(self, handle: &WindowHandle, accel_group: &AccelGroup) -> GtkMenu { + let mut menu = GtkMenu::new(); + menu.set_accel_group(Some(accel_group)); + + self.append_items_to_menu(&mut menu, handle, accel_group); + + menu + } +} + +fn register_accelerator(item: &GtkMenuItem, accel_group: &AccelGroup, menu_key: HotKey) { + let wc = match menu_key.key { + KeyCompare::Code(key_code) => key_code.into(), + KeyCompare::Text(text) => text.chars().next().unwrap() as u32, + }; + + item.add_accelerator( + "activate", + accel_group, + gdk::unicode_to_keyval(wc), + modifiers_to_gdk_modifier_type(menu_key.mods), + gtk::AccelFlags::VISIBLE, + ); +} + +fn modifiers_to_gdk_modifier_type(raw_modifiers: RawMods) -> gdk::ModifierType { + let mut result = ModifierType::empty(); + + let modifiers: KeyModifiers = raw_modifiers.into(); + + result.set(ModifierType::MOD1_MASK, modifiers.alt); + result.set(ModifierType::CONTROL_MASK, modifiers.ctrl); + result.set(ModifierType::SHIFT_MASK, modifiers.shift); + result.set(ModifierType::META_MASK, modifiers.meta); + + result +} diff --git a/druid-shell/src/gtk/mod.rs b/druid-shell/src/gtk/mod.rs new file mode 100644 index 0000000000..2475cc13ff --- /dev/null +++ b/druid-shell/src/gtk/mod.rs @@ -0,0 +1,753 @@ +// Copyright 2019 The xi-editor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! GTK-based platform support + +use std::any::Any; +use std::cell::{Cell, RefCell}; +use std::ffi::c_void; +use std::ffi::OsString; +use std::os::raw::{c_int, c_uint}; +use std::ptr; +use std::slice; +use std::sync::{Arc, Mutex, Weak}; + +use gdk::{EventKey, EventMask, ModifierType, ScrollDirection, WindowExt}; +use gio::ApplicationExt; +use gtk::prelude::*; +use gtk::{AccelGroup, ApplicationWindow}; + +use piet_common::{Piet, RenderContext}; +use util::assert_main_thread; +use win_main::with_application; + +use crate::clipboard::ClipboardItem; +use crate::dialog::FileDialogOptions; +use crate::keyboard; +use crate::kurbo::{Point, Vec2}; +use crate::platform::dialog::FileDialogType; +use crate::platform::menu::Menu; +use crate::window::{self, Cursor, FileInfo, MouseButton, Text, TimerToken, WinCtx, WinHandler}; +use crate::Error; + +pub mod application; +pub mod dialog; +pub mod menu; +pub mod util; +pub mod win_main; + +/// Taken from https://gtk-rs.org/docs-src/tutorial/closures +/// It is used to reduce the boilerplate of setting up gtk callbacks +/// Example: +/// ``` +/// button.connect_clicked(clone!(handle => move |_| { ... })) +/// ``` +/// is equivalent to: +/// ``` +/// { +/// let handle = handle.clone(); +/// button.connect_clicked(move |_| { ... }) +/// } +/// ``` +macro_rules! clone { + (@param _) => ( _ ); + (@param $x:ident) => ( $x ); + ($($n:ident),+ => move || $body:expr) => ( + { + $( let $n = $n.clone(); )+ + move || $body + } + ); + ($($n:ident),+ => move |$($p:tt),+| $body:expr) => ( + { + $( let $n = $n.clone(); )+ + move |$(clone!(@param $p),)+| $body + } + ); +} + +#[derive(Clone, Default)] +pub struct WindowHandle { + state: Weak, +} + +/// Builder abstraction for creating new windows +pub struct WindowBuilder { + handler: Option>, + title: String, + menu: Option, +} + +#[derive(Clone)] +pub struct IdleHandle { + idle_queue: Arc>>>, + state: Weak, +} + +// TODO: move this out of platform-dependent section. +trait IdleCallback: Send { + fn call(self: Box, a: &dyn Any); +} + +impl IdleCallback for F { + fn call(self: Box, a: &dyn Any) { + (*self)(a) + } +} + +struct WindowState { + window: ApplicationWindow, + handler: RefCell>, + idle_queue: Arc>>>, + current_keyval: RefCell>, +} + +struct WinCtxImpl<'a> { + handle: &'a WindowHandle, + text: Text<'static>, +} + +impl WindowBuilder { + pub fn new() -> WindowBuilder { + WindowBuilder { + handler: None, + title: String::new(), + menu: None, + } + } + + pub fn set_handler(&mut self, handler: Box) { + self.handler = Some(handler); + } + + pub fn set_title(&mut self, title: impl Into) { + self.title = title.into(); + } + + pub fn set_menu(&mut self, menu: Menu) { + self.menu = Some(menu); + } + + pub fn build(self) -> Result { + assert_main_thread(); + + let handler = self + .handler + .expect("Tried to build a window without setting the handler"); + + let window = with_application(|app| ApplicationWindow::new(&app)); + + let accel_group = AccelGroup::new(); + window.add_accel_group(&accel_group); + + let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0); + window.add(&vbox); + + let win_state = Arc::new(WindowState { + window, + handler: RefCell::new(handler), + idle_queue: Arc::new(Mutex::new(vec![])), + current_keyval: RefCell::new(None), + }); + + with_application(|app| { + app.connect_shutdown(clone!(win_state => move |_| { + // this ties a clone of Arc to the ApplicationWindow to keep it alive + // when the ApplicationWindow is destroyed, the last Arc is dropped + // and any Weak will be None on upgrade() + let _ = &win_state; + })) + }); + + let handle = WindowHandle { + state: Arc::downgrade(&win_state), + }; + + if let Some(menu) = self.menu { + let menu = menu.into_gtk_menubar(&handle, &accel_group); + vbox.pack_start(&menu, false, false, 0); + } + + let drawing_area = gtk::DrawingArea::new(); + + drawing_area.set_events( + EventMask::EXPOSURE_MASK + | EventMask::POINTER_MOTION_MASK + | EventMask::BUTTON_PRESS_MASK + | EventMask::BUTTON_RELEASE_MASK + | EventMask::KEY_PRESS_MASK + | EventMask::ENTER_NOTIFY_MASK + | EventMask::KEY_RELEASE_MASK + | EventMask::SCROLL_MASK, + ); + + drawing_area.set_can_focus(true); + drawing_area.grab_focus(); + + drawing_area.connect_enter_notify_event(|widget, _| { + widget.grab_focus(); + + Inhibit(true) + }); + + let last_size = Cell::new((0, 0)); + + drawing_area.connect_draw(clone!(handle => move |widget, context| { + if let Some(state) = handle.state.upgrade() { + let mut ctx = WinCtxImpl::from(&handle); + + let extents = context.clip_extents(); + let size = ( + (extents.2 - extents.0) as u32, + (extents.3 - extents.1) as u32, + ); + + if last_size.get() != size { + last_size.set(size); + state.handler.borrow_mut().size(size.0, size.1, &mut ctx); + } + + // For some reason piet needs a mutable context, so give it one I guess. + let mut context = context.clone(); + let mut piet_context = Piet::new(&mut context); + + if let Ok(mut handler_borrow) = state.handler.try_borrow_mut() { + let anim = handler_borrow + .paint(&mut piet_context, &mut ctx); + if let Err(e) = piet_context.finish() { + eprintln!("piet error on render: {:?}", e); + } + + if anim { + widget.queue_draw(); + } + } + + } + + Inhibit(false) + })); + + drawing_area.connect_button_press_event(clone!(handle => move |_widget, button| { + if let Some(state) = handle.state.upgrade() { + let mut ctx = WinCtxImpl::from(&handle); + + state.handler.borrow_mut().mouse_down( + &window::MouseEvent { + pos: Point::from(button.get_position()), + count: get_mouse_click_count(button.get_event_type()), + mods: get_modifiers(button.get_state()), + button: get_mouse_button(button.get_button()), + }, + &mut ctx, + ); + } + + Inhibit(true) + })); + + drawing_area.connect_button_release_event(clone!(handle => move |_widget, button| { + if let Some(state) = handle.state.upgrade() { + let mut ctx = WinCtxImpl::from(&handle); + + state.handler.borrow_mut().mouse_up( + &window::MouseEvent { + pos: Point::from(button.get_position()), + mods: get_modifiers(button.get_state()), + count: 0, + button: get_mouse_button(button.get_button()), + }, + &mut ctx, + ); + } + + Inhibit(true) + })); + + drawing_area.connect_motion_notify_event(clone!(handle=>move |_widget, motion| { + if let Some(state) = handle.state.upgrade() { + let mut ctx = WinCtxImpl::from(&handle); + + let pos = Point::from(motion.get_position()); + let mouse_event = window::MouseEvent { + pos, + mods: get_modifiers(motion.get_state()), + count: 0, + button: get_mouse_button_from_modifiers(motion.get_state()), + }; + + state + .handler + .borrow_mut() + .mouse_move(&mouse_event, &mut ctx); + } + + Inhibit(true) + })); + + drawing_area.connect_scroll_event(clone!(handle => move |_widget, scroll| { + if let Some(state) = handle.state.upgrade() { + let mut ctx = WinCtxImpl::from(&handle); + + let modifiers = get_modifiers(scroll.get_state()); + + // The magic "120"s are from Microsoft's documentation for WM_MOUSEWHEEL. + // They claim that one "tick" on a scroll wheel should be 120 units. + let mut handler = state.handler.borrow_mut(); + match scroll.get_direction() { + ScrollDirection::Up => { + handler.wheel(Vec2::from((0.0, -120.0)), modifiers, &mut ctx); + } + ScrollDirection::Down => { + handler.wheel(Vec2::from((0.0, 120.0)), modifiers, &mut ctx); + } + ScrollDirection::Left => { + // Note: this direction is just a guess, I (bobtwinkles) don't + // have a way to test horizontal scroll events under GTK. + // If it's wrong, the right direction also needs to be changed + handler.wheel(Vec2::from((120.0, 0.0)), modifiers, &mut ctx); + } + ScrollDirection::Right => { + handler.wheel(-Vec2::from((-120.0, 0.0)), modifiers, &mut ctx); + } + ScrollDirection::Smooth => { + // TODO: support smooth scrolling via scroll.get_delta and get_is_stop + eprintln!( + "Warning: somehow the Druid widget got a smooth scroll event" + ); + } + e => { + eprintln!( + "Warning: the Druid widget got some whacky scroll direction {:?}", + e + ); + } + } + } + + Inhibit(true) + })); + + drawing_area.connect_key_press_event(clone!(handle => move |_widget, key| { + if let Some(state) = handle.state.upgrade() { + let mut ctx = WinCtxImpl::from(&handle); + + let mut current_keyval = state.current_keyval.borrow_mut(); + let repeat = *current_keyval == Some(key.get_keyval()); + + *current_keyval = Some(key.get_keyval()); + + let key_event = make_key_event(key, repeat); + state.handler.borrow_mut().key_down(key_event, &mut ctx); + } + + Inhibit(true) + })); + + drawing_area.connect_key_release_event(clone!(handle => move |_widget, key| { + if let Some(state) = handle.state.upgrade() { + let mut ctx = WinCtxImpl::from(&handle); + + *(state.current_keyval.borrow_mut()) = None; + + let key_event = make_key_event(key, false); + state.handler.borrow_mut().key_up(key_event, &mut ctx); + } + + Inhibit(true) + })); + + drawing_area.connect_destroy(clone!(handle => move |_widget| { + if let Some(state) = handle.state.upgrade() { + let mut ctx = WinCtxImpl::from(&handle); + state.handler.borrow_mut().destroy(&mut ctx); + } + })); + + vbox.pack_end(&drawing_area, true, true, 0); + + win_state + .handler + .borrow_mut() + .connect(&window::WindowHandle { + inner: handle.clone(), + }); + + Ok(handle) + } +} + +impl WindowHandle { + pub fn show(&self) { + if let Some(state) = self.state.upgrade() { + state.window.show_all(); + } + } + + /// Close the window. + pub fn close(&self) { + if let Some(state) = self.state.upgrade() { + with_application(|app| { + app.remove_window(&state.window); + }); + } + } + + // Request invalidation of the entire window contents. + pub fn invalidate(&self) { + if let Some(state) = self.state.upgrade() { + state.window.queue_draw(); + } + } + /// Get a handle that can be used to schedule an idle task. + pub fn get_idle_handle(&self) -> Option { + self.state.upgrade().map(|s| IdleHandle { + idle_queue: s.idle_queue.clone(), + state: Arc::downgrade(&s), + }) + } + + /// Get the dpi of the window. + /// + /// TODO: we want to migrate this from dpi (with 96 as nominal) to a scale + /// factor (with 1 as nominal). + pub fn get_dpi(&self) -> f32 { + self.state + .upgrade() + .and_then(|s| s.window.get_window()) + .map(|w| w.get_display().get_default_screen().get_resolution() as f32) + .unwrap_or(96.0) + } + + // TODO: the following methods are cut'n'paste code. A good way to DRY + // would be to have a platform-independent trait with these as methods with + // default implementations. + + /// Convert a dimension in px units to physical pixels (rounding). + pub fn px_to_pixels(&self, x: f32) -> i32 { + (x * self.get_dpi() * (1.0 / 96.0)).round() as i32 + } + + /// Convert a point in px units to physical pixels (rounding). + pub fn px_to_pixels_xy(&self, x: f32, y: f32) -> (i32, i32) { + let scale = self.get_dpi() * (1.0 / 96.0); + ((x * scale).round() as i32, (y * scale).round() as i32) + } + + /// Convert a dimension in physical pixels to px units. + pub fn pixels_to_px>(&self, x: T) -> f32 { + (x.into() as f32) * 96.0 / self.get_dpi() + } + + /// Convert a point in physical pixels to px units. + pub fn pixels_to_px_xy>(&self, x: T, y: T) -> (f32, f32) { + let scale = 96.0 / self.get_dpi(); + ((x.into() as f32) * scale, (y.into() as f32) * scale) + } + + pub fn set_menu(&self, menu: Menu) { + if let Some(state) = self.state.upgrade() { + let window = &state.window; + + let accel_group = AccelGroup::new(); + window.add_accel_group(&accel_group); + + let vbox = window.get_children()[0] + .clone() + .downcast::() + .unwrap(); + + let first_child = &vbox.get_children()[0]; + if first_child.is::() { + vbox.remove(first_child); + } + let menubar = menu.into_gtk_menubar(&self, &accel_group); + vbox.pack_start(&menubar, false, false, 0); + menubar.show_all(); + } + } + + pub fn show_context_menu(&self, menu: Menu, _x: f64, _y: f64) { + if let Some(state) = self.state.upgrade() { + let window = &state.window; + + let accel_group = AccelGroup::new(); + window.add_accel_group(&accel_group); + + let menu = menu.into_gtk_menu(&self, &accel_group); + menu.show_all(); + menu.popup_easy(3, gtkrs::get_current_event_time()); + } + } + + pub fn set_title(&self, title: impl Into) { + if let Some(state) = self.state.upgrade() { + state.window.set_title(&(title.into())); + } + } + + pub fn file_dialog( + &self, + ty: FileDialogType, + options: FileDialogOptions, + ) -> Result { + if let Some(state) = self.state.upgrade() { + dialog::open_file_sync(state.window.upcast_ref(), ty, options) + } else { + Err(Error::Other( + "Cannot upgrade state from weak pointer to arc", + )) + } + } +} + +unsafe impl Send for IdleHandle {} +// WindowState needs to be Send + Sync so it can be passed into glib closures +unsafe impl Send for WindowState {} +unsafe impl Sync for WindowState {} + +impl IdleHandle { + /// Add an idle handler, which is called (once) when the message loop + /// is empty. The idle handler will be run from the main UI thread, and + /// won't be scheduled if the associated view has been dropped. + /// + /// Note: the name "idle" suggests that it will be scheduled with a lower + /// priority than other UI events, but that's not necessarily the case. + pub fn add_idle(&self, callback: F) + where + F: FnOnce(&dyn Any) + Send + 'static, + { + let mut queue = self.idle_queue.lock().unwrap(); + if let Some(state) = self.state.upgrade() { + if queue.is_empty() { + queue.push(Box::new(callback)); + gdk::threads_add_idle(move || run_idle(&state)); + } else { + queue.push(Box::new(callback)); + } + } + } +} + +fn run_idle(state: &Arc) -> bool { + assert_main_thread(); + let mut handler = state.handler.borrow_mut(); + let handler_as_any = handler.as_any(); + + let queue: Vec<_> = std::mem::replace(&mut state.idle_queue.lock().unwrap(), Vec::new()); + + for callback in queue { + callback.call(handler_as_any); + } + false +} + +impl<'a> WinCtx<'a> for WinCtxImpl<'a> { + fn invalidate(&mut self) { + self.handle.invalidate(); + } + + fn text_factory(&mut self) -> &mut Text<'a> { + &mut self.text + } + + fn set_cursor(&mut self, cursor: &Cursor) { + if let Some(gdk_window) = self + .handle + .state + .upgrade() + .and_then(|s| s.window.get_window()) + { + let cursor = make_gdk_cursor(cursor, &gdk_window); + gdk_window.set_cursor(cursor.as_ref()); + } + } + + fn open_file_sync(&mut self, options: FileDialogOptions) -> Option { + if let Some(state) = self.handle.state.upgrade() { + dialog::open_file_sync(state.window.upcast_ref(), FileDialogType::Open, options) + .ok() + .map(|s| FileInfo { path: s.into() }) + } else { + None + } + } + fn set_clipboard_contents(&mut self, item: ClipboardItem) { + let display = gdk::Display::get_default().unwrap(); + let clipboard = gtk::Clipboard::get_default(&display).unwrap(); + + match item { + ClipboardItem::Text(text) => { + clipboard.set_text(&text); + } + other => log::warn!("unhandled clipboard data {:?}", other), + } + } + + fn request_timer(&mut self, deadline: std::time::Instant) -> TimerToken { + let interval = time_interval_from_deadline(deadline); + let token = next_timer_id(); + + let handle = self.handle.clone(); + + gdk::threads_add_timeout(interval, move || { + if let Some(state) = handle.state.upgrade() { + if let Ok(mut handler_borrow) = state.handler.try_borrow_mut() { + let mut ctx = WinCtxImpl::from(&handle); + handler_borrow.timer(TimerToken::new(token), &mut ctx); + return false; + } + } + true + }); + + TimerToken::new(token) + } +} + +impl<'a> From<&'a WindowHandle> for WinCtxImpl<'a> { + fn from(handle: &'a WindowHandle) -> Self { + WinCtxImpl { + handle, + text: Text::new(), + } + } +} + +fn time_interval_from_deadline(deadline: std::time::Instant) -> u32 { + let now = std::time::Instant::now(); + if now >= deadline { + 0 + } else { + (deadline - now).as_millis() as u32 + } +} + +fn next_timer_id() -> usize { + use std::sync::atomic::{AtomicUsize, Ordering}; + static TIMER_ID: AtomicUsize = AtomicUsize::new(1); + TIMER_ID.fetch_add(1, Ordering::Relaxed) +} + +fn make_gdk_cursor(cursor: &Cursor, gdk_window: &gdk::Window) -> Option { + gdk::Cursor::new_from_name( + &gdk_window.get_display(), + match cursor { + // cursor name values from https://www.w3.org/TR/css-ui-3/#cursor + Cursor::Arrow => "default", + Cursor::IBeam => "text", + Cursor::Crosshair => "crosshair", + Cursor::OpenHand => "grab", + Cursor::NotAllowed => "not-allowed", + Cursor::ResizeLeftRight => "ew-resize", + Cursor::ResizeUpDown => "ns-resize", + }, + ) +} + +fn get_mouse_button(button: u32) -> window::MouseButton { + match button { + 1 => MouseButton::Left, + 2 => MouseButton::Middle, + 3 => MouseButton::Right, + 4 => MouseButton::X1, + 5 => MouseButton::X2, + _ => MouseButton::Left, + } +} + +fn get_mouse_button_from_modifiers(modifiers: gdk::ModifierType) -> window::MouseButton { + match modifiers { + modifiers if modifiers.contains(ModifierType::BUTTON1_MASK) => MouseButton::Left, + modifiers if modifiers.contains(ModifierType::BUTTON2_MASK) => MouseButton::Middle, + modifiers if modifiers.contains(ModifierType::BUTTON3_MASK) => MouseButton::Right, + modifiers if modifiers.contains(ModifierType::BUTTON4_MASK) => MouseButton::X1, + modifiers if modifiers.contains(ModifierType::BUTTON5_MASK) => MouseButton::X2, + _ => { + //FIXME: what about when no modifiers match? + MouseButton::Left + } + } +} + +fn get_mouse_click_count(event_type: gdk::EventType) -> u32 { + match event_type { + gdk::EventType::ButtonPress => 1, + gdk::EventType::DoubleButtonPress => 2, + gdk::EventType::TripleButtonPress => 3, + _ => 0, + } +} + +fn get_modifiers(modifiers: gdk::ModifierType) -> keyboard::KeyModifiers { + keyboard::KeyModifiers { + shift: modifiers.contains(ModifierType::SHIFT_MASK), + alt: modifiers.contains(ModifierType::MOD1_MASK), + ctrl: modifiers.contains(ModifierType::CONTROL_MASK), + meta: modifiers.contains(ModifierType::META_MASK), + } +} + +fn make_key_event(key: &EventKey, repeat: bool) -> keyboard::KeyEvent { + let keyval = key.get_keyval(); + let hardware_keycode = key.get_hardware_keycode(); + + let keycode = hardware_keycode_to_keyval(hardware_keycode).unwrap_or(keyval); + + let text = gdk::keyval_to_unicode(keyval); + + keyboard::KeyEvent::new(keycode, repeat, get_modifiers(key.get_state()), text, text) +} + +/// Map a hardware keycode to a keyval by performing a lookup in the keymap and finding the +/// keyval with the lowest group and level +fn hardware_keycode_to_keyval(keycode: u16) -> Option { + unsafe { + let keymap = gdk_sys::gdk_keymap_get_default(); + + let mut nkeys = 0; + let mut keys: *mut gdk_sys::GdkKeymapKey = ptr::null_mut(); + let mut keyvals: *mut c_uint = ptr::null_mut(); + + // call into gdk to retrieve the keyvals and keymap keys + gdk_sys::gdk_keymap_get_entries_for_keycode( + keymap, + c_uint::from(keycode), + &mut keys as *mut *mut gdk_sys::GdkKeymapKey, + &mut keyvals as *mut *mut c_uint, + &mut nkeys as *mut c_int, + ); + + if nkeys > 0 { + let keyvals_slice = slice::from_raw_parts(keyvals, nkeys as usize); + let keys_slice = slice::from_raw_parts(keys, nkeys as usize); + + let resolved_keyval = keys_slice.iter().enumerate().find_map(|(i, key)| { + if key.group == 0 && key.level == 0 { + Some(keyvals_slice[i]) + } else { + None + } + }); + + // notify glib to free the allocated arrays + glib_sys::g_free(keyvals as *mut c_void); + glib_sys::g_free(keys as *mut c_void); + + resolved_keyval + } else { + None + } + } +} diff --git a/druid-shell/src/gtk/util.rs b/druid-shell/src/gtk/util.rs new file mode 100644 index 0000000000..c2329c51c6 --- /dev/null +++ b/druid-shell/src/gtk/util.rs @@ -0,0 +1,28 @@ +// Copyright 2019 The xi-editor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Utilities, GTK specific. + +pub fn init() { + gtk::init().expect("GTK initialization failed"); +} + +pub fn assert_main_thread() { + assert!(gtk::is_initialized_main_thread()); +} + +pub fn get_locale() -> String { + //TODO ahem + "en-US".into() +} diff --git a/druid-shell/src/gtk/win_main.rs b/druid-shell/src/gtk/win_main.rs new file mode 100644 index 0000000000..6f08e3be61 --- /dev/null +++ b/druid-shell/src/gtk/win_main.rs @@ -0,0 +1,100 @@ +// Copyright 2019 The xi-editor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! GTK implementation of runloop. + +use std::cell::RefCell; + +use gio::{ApplicationExt, ApplicationExtManual, ApplicationFlags, Cancellable}; +use gtkrs::{Application, GtkApplicationExt}; + +use crate::util::assert_main_thread; + +// XXX: The application needs to be global because WindowBuilder::build wants +// to construct an ApplicationWindow, which needs the application, but +// WindowBuilder::build does not get the RunLoop +thread_local!( + static GTK_APPLICATION: RefCell> = RefCell::new(None); +); + +/// Container for a GTK runloop +pub struct RunLoop {} + +impl RunLoop { + pub fn new() -> RunLoop { + assert_main_thread(); + + // TODO: we should give control over the application ID to the user + let application = Application::new( + Some("com.github.xi-editor.druid"), + ApplicationFlags::FLAGS_NONE, + ) + .expect("Unable to create GTK application"); + + application.connect_activate(|_app| { + eprintln!("Activated application"); + }); + + application + .register(None as Option<&Cancellable>) + .expect("Could not register GTK application"); + + GTK_APPLICATION.with(move |x| *x.borrow_mut() = Some(application)); + + RunLoop {} + } + + pub fn run(&mut self) { + assert_main_thread(); + + // TODO: should we pass the command line arguments? + GTK_APPLICATION.with(|x| { + x.borrow() + .as_ref() + .unwrap() // Safe because we initialized this in RunLoop::new + .run(&[]) + }); + } +} + +/// Request to quit the application, exiting the runloop. +pub fn request_quit() { + assert_main_thread(); + with_application(|app| { + match app.get_active_window() { + None => { + // no application is running, main is not running + } + Some(_) => { + // we still have an active window, close the runLo + gtk::main_quit(); + } + } + }); +} + +#[inline] +pub(crate) fn with_application(f: F) -> R +where + F: std::ops::FnOnce(Application) -> R, +{ + assert_main_thread(); + GTK_APPLICATION.with(move |app| { + let app = app + .borrow() + .clone() + .expect("Tried to manipulate the application before RunLoop::new was called"); + f(app) + }) +} diff --git a/druid-shell/src/keyboard.rs b/druid-shell/src/keyboard.rs index ccd37684c4..582718ed82 100644 --- a/druid-shell/src/keyboard.rs +++ b/druid-shell/src/keyboard.rs @@ -180,7 +180,6 @@ pub enum KeyCode { LeftMeta, RightMeta, - //FIXME: support lshift/rshift etc? Space, CapsLock, F1, @@ -239,6 +238,7 @@ pub enum KeyCode { pub enum RawKeyCode { Windows(i32), Mac(u16), + Linux(u32), } impl KeyCode { @@ -538,6 +538,264 @@ impl From for KeyCode { } } +#[cfg(any(target_os = "linux", feature = "use_gtk"))] +impl From for KeyCode { + #[allow(non_upper_case_globals)] + fn from(raw: u32) -> KeyCode { + use gdk::enums::key::*; + match raw { + Escape => KeyCode::Escape, + grave => KeyCode::Backtick, + _0 => KeyCode::Key0, + _1 => KeyCode::Key1, + _2 => KeyCode::Key2, + _3 => KeyCode::Key3, + _4 => KeyCode::Key4, + _5 => KeyCode::Key5, + _6 => KeyCode::Key6, + _7 => KeyCode::Key7, + _8 => KeyCode::Key8, + _9 => KeyCode::Key9, + minus => KeyCode::Minus, + equal => KeyCode::Equals, + BackSpace => KeyCode::Backspace, + + Tab => KeyCode::Tab, + q | Q => KeyCode::KeyQ, + w | W => KeyCode::KeyW, + e | E => KeyCode::KeyE, + r | R => KeyCode::KeyR, + t | T => KeyCode::KeyT, + y | Y => KeyCode::KeyY, + u | U => KeyCode::KeyU, + i | I => KeyCode::KeyI, + o | O => KeyCode::KeyO, + p | P => KeyCode::KeyP, + bracketleft => KeyCode::LeftBracket, + bracketright => KeyCode::RightBracket, + Return => KeyCode::Return, + + a | A => KeyCode::KeyA, + s | S => KeyCode::KeyS, + d | D => KeyCode::KeyD, + f | F => KeyCode::KeyF, + g | G => KeyCode::KeyG, + h | H => KeyCode::KeyH, + j | J => KeyCode::KeyJ, + k | K => KeyCode::KeyK, + l | L => KeyCode::KeyL, + semicolon => KeyCode::Semicolon, + quoteright => KeyCode::Quote, + backslash => KeyCode::Backslash, + + z | Z => KeyCode::KeyZ, + x | X => KeyCode::KeyX, + c | C => KeyCode::KeyC, + v | V => KeyCode::KeyV, + b | B => KeyCode::KeyB, + n | N => KeyCode::KeyN, + m | M => KeyCode::KeyM, + comma => KeyCode::Comma, + period => KeyCode::Period, + slash => KeyCode::Slash, + + Control_L => KeyCode::LeftControl, + Control_R => KeyCode::RightControl, + Alt_L => KeyCode::LeftAlt, + Alt_R => KeyCode::RightAlt, + Shift_L => KeyCode::LeftShift, + Shift_R => KeyCode::RightShift, + Meta_L => KeyCode::LeftMeta, + Meta_R => KeyCode::RightMeta, + + space => KeyCode::Space, + Caps_Lock => KeyCode::CapsLock, + F1 => KeyCode::F1, + F2 => KeyCode::F2, + F3 => KeyCode::F3, + F4 => KeyCode::F4, + F5 => KeyCode::F5, + F6 => KeyCode::F6, + F7 => KeyCode::F7, + F8 => KeyCode::F8, + F9 => KeyCode::F9, + F10 => KeyCode::F10, + F11 => KeyCode::F11, + F12 => KeyCode::F12, + + Print => KeyCode::PrintScreen, + Scroll_Lock => KeyCode::ScrollLock, + // Pause/Break not audio. + Pause => KeyCode::Pause, + + Insert => KeyCode::Insert, + Delete => KeyCode::Delete, + Home => KeyCode::Home, + End => KeyCode::End, + Page_Up => KeyCode::PageUp, + Page_Down => KeyCode::PageDown, + + KP_0 => KeyCode::Numpad0, + KP_1 => KeyCode::Numpad1, + KP_2 => KeyCode::Numpad2, + KP_3 => KeyCode::Numpad3, + KP_4 => KeyCode::Numpad4, + KP_5 => KeyCode::Numpad5, + KP_6 => KeyCode::Numpad6, + KP_7 => KeyCode::Numpad7, + KP_8 => KeyCode::Numpad8, + KP_9 => KeyCode::Numpad9, + + KP_Equal => KeyCode::NumpadEquals, + KP_Subtract => KeyCode::NumpadSubtract, + KP_Add => KeyCode::NumpadAdd, + KP_Decimal => KeyCode::NumpadDecimal, + KP_Multiply => KeyCode::NumpadMultiply, + KP_Divide => KeyCode::NumpadDivide, + Num_Lock => KeyCode::NumLock, + KP_Enter => KeyCode::NumpadEnter, + + Up => KeyCode::ArrowUp, + Down => KeyCode::ArrowDown, + Left => KeyCode::ArrowLeft, + Right => KeyCode::ArrowRight, + _ => { + eprintln!("Warning: unknown keyval {}", raw); + KeyCode::Unknown(RawKeyCode::Linux(raw)) + } + } + } +} + +#[cfg(any(target_os = "linux", feature = "use_gtk"))] +impl Into for KeyCode { + #[allow(non_upper_case_globals)] + fn into(self) -> u32 { + use gdk::enums::key::*; + match self { + KeyCode::Escape => Escape, + KeyCode::Backtick => grave, + KeyCode::Key0 => _0, + KeyCode::Key1 => _1, + KeyCode::Key2 => _2, + KeyCode::Key3 => _3, + KeyCode::Key4 => _4, + KeyCode::Key5 => _5, + KeyCode::Key6 => _6, + KeyCode::Key7 => _7, + KeyCode::Key8 => _8, + KeyCode::Key9 => _9, + KeyCode::Minus => minus, + KeyCode::Equals => equal, + KeyCode::Backspace => BackSpace, + + KeyCode::Tab => Tab, + KeyCode::KeyQ => q | Q, + KeyCode::KeyW => w | W, + KeyCode::KeyE => e | E, + KeyCode::KeyR => r | R, + KeyCode::KeyT => t | T, + KeyCode::KeyY => y | Y, + KeyCode::KeyU => u | U, + KeyCode::KeyI => i | I, + KeyCode::KeyO => o | O, + KeyCode::KeyP => p | P, + KeyCode::LeftBracket => bracketleft, + KeyCode::RightBracket => bracketright, + KeyCode::Return => Return, + + KeyCode::KeyA => a | A, + KeyCode::KeyS => s | S, + KeyCode::KeyD => d | D, + KeyCode::KeyF => f | F, + KeyCode::KeyG => g | G, + KeyCode::KeyH => h | H, + KeyCode::KeyJ => j | J, + KeyCode::KeyK => k | K, + KeyCode::KeyL => l | L, + KeyCode::Semicolon => semicolon, + KeyCode::Quote => quoteright, + KeyCode::Backslash => backslash, + + KeyCode::KeyZ => z | Z, + KeyCode::KeyX => x | X, + KeyCode::KeyC => c | C, + KeyCode::KeyV => v | V, + KeyCode::KeyB => b | B, + KeyCode::KeyN => n | N, + KeyCode::KeyM => m | M, + KeyCode::Comma => comma, + KeyCode::Period => period, + KeyCode::Slash => slash, + + KeyCode::LeftControl => Control_L, + KeyCode::RightControl => Control_R, + KeyCode::LeftAlt => Alt_L, + KeyCode::RightAlt => Alt_R, + KeyCode::LeftShift => Shift_L, + KeyCode::RightShift => Shift_R, + KeyCode::LeftMeta => Meta_L, + KeyCode::RightMeta => Meta_R, + + KeyCode::Space => space, + KeyCode::CapsLock => Caps_Lock, + KeyCode::F1 => F1, + KeyCode::F2 => F2, + KeyCode::F3 => F3, + KeyCode::F4 => F4, + KeyCode::F5 => F5, + KeyCode::F6 => F6, + KeyCode::F7 => F7, + KeyCode::F8 => F8, + KeyCode::F9 => F9, + KeyCode::F10 => F10, + KeyCode::F11 => F11, + KeyCode::F12 => F12, + + KeyCode::PrintScreen => Print, + KeyCode::ScrollLock => Scroll_Lock, + // Pause/Break not audio. + KeyCode::Pause => Pause, + + KeyCode::Insert => Insert, + KeyCode::Delete => Delete, + KeyCode::Home => Home, + KeyCode::End => End, + KeyCode::PageUp => Page_Up, + KeyCode::PageDown => Page_Down, + + KeyCode::Numpad0 => KP_0, + KeyCode::Numpad1 => KP_1, + KeyCode::Numpad2 => KP_2, + KeyCode::Numpad3 => KP_3, + KeyCode::Numpad4 => KP_4, + KeyCode::Numpad5 => KP_5, + KeyCode::Numpad6 => KP_6, + KeyCode::Numpad7 => KP_7, + KeyCode::Numpad8 => KP_8, + KeyCode::Numpad9 => KP_9, + + KeyCode::NumpadEquals => KP_Equal, + KeyCode::NumpadSubtract => KP_Subtract, + KeyCode::NumpadAdd => KP_Add, + KeyCode::NumpadDecimal => KP_Decimal, + KeyCode::NumpadMultiply => KP_Multiply, + KeyCode::NumpadDivide => KP_Divide, + KeyCode::NumLock => Num_Lock, + KeyCode::NumpadEnter => KP_Enter, + + KeyCode::ArrowUp => Up, + KeyCode::ArrowDown => Down, + KeyCode::ArrowLeft => Left, + KeyCode::ArrowRight => Right, + KeyCode::Unknown(_) => unreachable!( + "Unreachable: converting unknown KeyCode {:?} to a keyval", + self + ), + } + } +} + /// Should realistically be (8 * N) - 1; we need one byte for the length. const TINY_STR_CAPACITY: usize = 15; diff --git a/druid-shell/src/lib.rs b/druid-shell/src/lib.rs index 10f280729c..243e2620af 100644 --- a/druid-shell/src/lib.rs +++ b/druid-shell/src/lib.rs @@ -23,14 +23,27 @@ pub use piet_common::kurbo; #[macro_use] extern crate winapi; -#[cfg(target_os = "macos")] +#[cfg(all(target_os = "macos", not(feature = "use_gtk")))] #[macro_use] extern crate objc; -#[macro_use] +#[cfg(any(feature = "use_gtk", target_os = "linux"))] +extern crate cairo; +#[cfg(any(feature = "use_gtk", target_os = "linux"))] +extern crate gdk; +#[cfg(any(feature = "use_gtk", target_os = "linux"))] +extern crate gio; +#[cfg(any(feature = "use_gtk", target_os = "linux"))] +extern crate glib; +#[cfg(any(feature = "use_gtk", target_os = "linux"))] +extern crate gtk as gtkrs; + +#[cfg_attr(not(any(feature = "use_gtk", target_os = "linux")), macro_use)] +#[cfg(not(any(feature = "use_gtk", target_os = "linux")))] extern crate lazy_static; pub mod clipboard; +mod common_util; pub mod dialog; pub mod error; pub mod hotkey; @@ -38,18 +51,23 @@ pub mod keyboard; pub mod keycodes; pub mod window; -#[cfg(target_os = "windows")] +#[cfg(all(target_os = "windows", not(feature = "use_gtk")))] pub mod windows; -#[cfg(target_os = "windows")] +#[cfg(all(target_os = "windows", not(feature = "use_gtk")))] pub use windows as platform; -#[cfg(target_os = "windows")] +#[cfg(all(target_os = "windows", not(feature = "use_gtk")))] pub use windows::paint; -#[cfg(target_os = "macos")] +#[cfg(all(target_os = "macos", not(feature = "use_gtk")))] pub mod mac; -#[cfg(target_os = "macos")] +#[cfg(all(target_os = "macos", not(feature = "use_gtk")))] pub use mac as platform; +#[cfg(any(feature = "use_gtk", target_os = "linux"))] +pub mod gtk; +#[cfg(any(feature = "use_gtk", target_os = "linux"))] +pub use crate::gtk as platform; + pub use error::Error; pub use platform::application; diff --git a/druid-shell/src/mac/menu.rs b/druid-shell/src/mac/menu.rs index ae0395d810..26a999a08f 100644 --- a/druid-shell/src/mac/menu.rs +++ b/druid-shell/src/mac/menu.rs @@ -14,38 +14,19 @@ //! macOS implementation of menus. -use crate::util::make_nsstring; use cocoa::appkit::{NSEventModifierFlags, NSMenu, NSMenuItem}; use cocoa::base::{id, nil, NO}; use cocoa::foundation::NSAutoreleasePool; +use crate::common_util::strip_access_key; use crate::hotkey::{HotKey, KeyCompare}; use crate::keyboard::{KeyCode, KeyModifiers}; +use crate::util::make_nsstring; pub struct Menu { pub menu: id, } -/// Strip the access keys from the menu strong. -/// -/// Changes "E&xit" to "Exit". Actual ampersands are escaped as "&&". -fn strip_access_key(raw_menu_text: &str) -> String { - let mut saw_ampersand = false; - let mut result = String::new(); - for c in raw_menu_text.chars() { - if c == '&' { - if saw_ampersand { - result.push(c); - } - saw_ampersand = !saw_ampersand; - } else { - result.push(c); - saw_ampersand = false; - } - } - result -} - fn make_menu_item(id: u32, text: &str, key: Option<&HotKey>, enabled: bool, selected: bool) -> id { let key_equivalent = key.map(HotKey::key_equivalent).unwrap_or(""); let stripped_text = strip_access_key(text); diff --git a/druid-shell/src/mac/mod.rs b/druid-shell/src/mac/mod.rs index 39bd883174..77a21a99df 100644 --- a/druid-shell/src/mac/mod.rs +++ b/druid-shell/src/mac/mod.rs @@ -720,9 +720,10 @@ impl WindowHandle { ) -> Result { match ty { FileDialogType::Open => unsafe { - dialog::show_open_file_dialog_sync(options).ok_or(Error::Null) + dialog::show_open_file_dialog_sync(options) + .ok_or(Error::Other("failed to open file dialog")) }, - _ => Err(Error::Null), + _ => Err(Error::Other("unhandled FileDialogType")), } } } diff --git a/druid-shell/src/windows/mod.rs b/druid-shell/src/windows/mod.rs index b27d4a84ba..bb2b78bc9c 100644 --- a/druid-shell/src/windows/mod.rs +++ b/druid-shell/src/windows/mod.rs @@ -849,7 +849,7 @@ impl WindowBuilder { win.clone(), ); if hwnd.is_null() { - return Err(Error::Null); + return Err(Error::Other("hwnd.is_null() == true")); } let dcomp_state = create_dcomp_state(self.present_strategy, hwnd).unwrap_or_else(|e| { @@ -1154,7 +1154,7 @@ impl WindowHandle { ty: FileDialogType, options: FileDialogOptions, ) -> Result { - let hwnd = self.get_hwnd().ok_or(Error::Null)?; + let hwnd = self.get_hwnd().ok_or(Error::Other("get_hwnd() is None"))?; unsafe { get_file_dialog_path(hwnd, ty, options) } }