1
0
forked from brl/citadel-tools

Not used anymore

This commit is contained in:
Bruce Leidl 2021-10-04 06:23:43 -04:00
parent 9760dfc501
commit 79fb3b38dd
17 changed files with 0 additions and 1981 deletions

View File

@ -1,19 +0,0 @@
[package]
name = "citadel-realms-ui"
description = "Citadel Realms UI"
version = "0.1.0"
authors = ["Bruce Leidl <bruce@subgraph.com>"]
edition = "2018"
homepage = "https://subgraph,com"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
dbus = "0.8"
nix = "0.17.0"
fuzzy-matcher = "0.3.7"
gtk = "0.9.0"
gdk = "0.13.2"
gio = "0.9.1"
glib = "0.10.2"
pango = "0.9.1"

View File

@ -1,202 +0,0 @@
# Auto-Generated by cargo-bitbake 0.3.14
#
inherit cargo
# If this is git based prefer versioned ones if they exist
# DEFAULT_PREFERENCE = "-1"
# how to get citadel-realms-ui could be as easy as but default to a git checkout:
# SRC_URI += "crate://crates.io/citadel-realms-ui/0.1.0"
SRC_URI += "git://github.com/mckinney-subgraph/citadel-tools;protocol=https;nobranch=1;branch=installer_ui"
SRCREV = "695dc9da2115f5f3f731b711b4b127b45d7a137f"
S = "${WORKDIR}/git"
CARGO_SRC_DIR = "citadel-realms-ui"
PV_append = ".AUTOINC+695dc9da21"
# please note if you have entries that do not begin with crate://
# you must change them to how that package can be fetched
SRC_URI += " \
crate://crates.io/addr2line/0.12.1 \
crate://crates.io/adler32/1.1.0 \
crate://crates.io/ansi_term/0.11.0 \
crate://crates.io/anyhow/1.0.33 \
crate://crates.io/arc-swap/0.4.7 \
crate://crates.io/array-macro/1.0.3 \
crate://crates.io/atk-sys/0.10.0 \
crate://crates.io/atk/0.9.0 \
crate://crates.io/atty/0.2.14 \
crate://crates.io/autocfg/1.0.0 \
crate://crates.io/backtrace/0.3.49 \
crate://crates.io/bincode/1.2.1 \
crate://crates.io/bitflags/1.2.1 \
crate://crates.io/block-buffer/0.9.0 \
crate://crates.io/block-cipher/0.7.1 \
crate://crates.io/blowfish/0.5.0 \
crate://crates.io/byteorder/1.3.4 \
crate://crates.io/cairo-rs/0.9.1 \
crate://crates.io/cairo-sys-rs/0.10.0 \
crate://crates.io/cc/1.0.54 \
crate://crates.io/cfg-if/0.1.10 \
crate://crates.io/chrono/0.4.11 \
crate://crates.io/clap/2.33.1 \
crate://crates.io/cpuid-bool/0.1.2 \
crate://crates.io/crc32fast/1.2.0 \
crate://crates.io/crossbeam-channel/0.3.9 \
crate://crates.io/crossbeam-utils/0.6.6 \
crate://crates.io/crypto-mac/0.8.0 \
crate://crates.io/cursive/0.11.0 \
crate://crates.io/dbus/0.6.5 \
crate://crates.io/dbus/0.8.4 \
crate://crates.io/digest/0.9.0 \
crate://crates.io/either/1.6.1 \
crate://crates.io/enum-map-derive/0.4.3 \
crate://crates.io/enum-map-internals/0.1.3 \
crate://crates.io/enum-map/0.5.0 \
crate://crates.io/enumset/0.3.19 \
crate://crates.io/enumset_derive/0.3.2 \
crate://crates.io/failure/0.1.8 \
crate://crates.io/failure_derive/0.1.8 \
crate://crates.io/filetime/0.2.10 \
crate://crates.io/futures-channel/0.3.6 \
crate://crates.io/futures-core/0.3.6 \
crate://crates.io/futures-executor/0.3.6 \
crate://crates.io/futures-io/0.3.6 \
crate://crates.io/futures-macro/0.3.6 \
crate://crates.io/futures-sink/0.3.6 \
crate://crates.io/futures-task/0.3.6 \
crate://crates.io/futures-util/0.3.6 \
crate://crates.io/futures/0.3.6 \
crate://crates.io/fuzzy-matcher/0.3.7 \
crate://crates.io/gdk-pixbuf-sys/0.10.0 \
crate://crates.io/gdk-pixbuf/0.9.0 \
crate://crates.io/gdk-sys/0.10.0 \
crate://crates.io/gdk/0.13.2 \
crate://crates.io/generic-array/0.14.4 \
crate://crates.io/getrandom/0.1.15 \
crate://crates.io/gimli/0.21.0 \
crate://crates.io/gio-sys/0.10.1 \
crate://crates.io/gio/0.9.1 \
crate://crates.io/glib-macros/0.10.1 \
crate://crates.io/glib-sys/0.10.1 \
crate://crates.io/glib/0.10.2 \
crate://crates.io/gobject-sys/0.10.0 \
crate://crates.io/gtk-sys/0.10.0 \
crate://crates.io/gtk/0.9.2 \
crate://crates.io/heck/0.3.1 \
crate://crates.io/hermit-abi/0.1.14 \
crate://crates.io/hex/0.4.2 \
crate://crates.io/hmac/0.8.1 \
crate://crates.io/inotify-sys/0.1.3 \
crate://crates.io/inotify/0.8.3 \
crate://crates.io/itertools/0.9.0 \
crate://crates.io/lazy_static/1.4.0 \
crate://crates.io/libc/0.2.71 \
crate://crates.io/libdbus-sys/0.2.1 \
crate://crates.io/libflate/0.1.27 \
crate://crates.io/libsodium-sys/0.2.5 \
crate://crates.io/log/0.4.8 \
crate://crates.io/md-5/0.9.1 \
crate://crates.io/memchr/2.3.3 \
crate://crates.io/miniz_oxide/0.3.7 \
crate://crates.io/nix/0.17.0 \
crate://crates.io/num-complex/0.2.4 \
crate://crates.io/num-integer/0.1.43 \
crate://crates.io/num-iter/0.1.41 \
crate://crates.io/num-rational/0.2.4 \
crate://crates.io/num-traits/0.2.12 \
crate://crates.io/num/0.2.1 \
crate://crates.io/numtoa/0.1.0 \
crate://crates.io/object/0.20.0 \
crate://crates.io/once_cell/1.4.0 \
crate://crates.io/opaque-debug/0.2.3 \
crate://crates.io/opaque-debug/0.3.0 \
crate://crates.io/owning_ref/0.4.1 \
crate://crates.io/pango-sys/0.10.0 \
crate://crates.io/pango/0.9.1 \
crate://crates.io/pin-project-internal/0.4.22 \
crate://crates.io/pin-project/0.4.22 \
crate://crates.io/pin-utils/0.1.0 \
crate://crates.io/pkg-config/0.3.17 \
crate://crates.io/ppv-lite86/0.2.9 \
crate://crates.io/proc-macro-crate/0.1.5 \
crate://crates.io/proc-macro-error-attr/1.0.4 \
crate://crates.io/proc-macro-error/1.0.4 \
crate://crates.io/proc-macro-hack/0.5.16 \
crate://crates.io/proc-macro-nested/0.1.6 \
crate://crates.io/proc-macro2/0.4.30 \
crate://crates.io/proc-macro2/1.0.18 \
crate://crates.io/pwhash/0.3.1 \
crate://crates.io/quote/0.6.13 \
crate://crates.io/quote/1.0.7 \
crate://crates.io/rand/0.7.3 \
crate://crates.io/rand_chacha/0.2.2 \
crate://crates.io/rand_core/0.5.1 \
crate://crates.io/rand_hc/0.2.0 \
crate://crates.io/redox_syscall/0.1.56 \
crate://crates.io/redox_termios/0.1.1 \
crate://crates.io/rle-decode-fast/1.0.1 \
crate://crates.io/rpassword/4.0.5 \
crate://crates.io/rustc-demangle/0.1.16 \
crate://crates.io/same-file/1.0.6 \
crate://crates.io/serde/1.0.112 \
crate://crates.io/serde_derive/1.0.112 \
crate://crates.io/sha-1/0.9.1 \
crate://crates.io/sha2/0.9.1 \
crate://crates.io/signal-hook-registry/1.2.0 \
crate://crates.io/signal-hook/0.1.16 \
crate://crates.io/slab/0.4.2 \
crate://crates.io/sodiumoxide/0.2.5 \
crate://crates.io/stable_deref_trait/1.1.1 \
crate://crates.io/strsim/0.8.0 \
crate://crates.io/strum/0.18.0 \
crate://crates.io/strum_macros/0.18.0 \
crate://crates.io/subtle/2.3.0 \
crate://crates.io/syn/0.15.44 \
crate://crates.io/syn/1.0.31 \
crate://crates.io/synstructure/0.12.4 \
crate://crates.io/system-deps/1.3.2 \
crate://crates.io/take_mut/0.2.2 \
crate://crates.io/tar/0.4.29 \
crate://crates.io/termion/1.5.5 \
crate://crates.io/textwrap/0.11.0 \
crate://crates.io/thiserror-impl/1.0.21 \
crate://crates.io/thiserror/1.0.21 \
crate://crates.io/thread_local/1.0.1 \
crate://crates.io/time/0.1.43 \
crate://crates.io/toml/0.4.10 \
crate://crates.io/toml/0.5.6 \
crate://crates.io/typenum/1.12.0 \
crate://crates.io/unicode-segmentation/1.6.0 \
crate://crates.io/unicode-width/0.1.7 \
crate://crates.io/unicode-xid/0.1.0 \
crate://crates.io/unicode-xid/0.2.0 \
crate://crates.io/vcpkg/0.2.10 \
crate://crates.io/vec_map/0.8.2 \
crate://crates.io/version-compare/0.0.10 \
crate://crates.io/version_check/0.9.2 \
crate://crates.io/void/1.0.2 \
crate://crates.io/walkdir/2.3.1 \
crate://crates.io/wasi/0.9.0+wasi-snapshot-preview1 \
crate://crates.io/winapi-i686-pc-windows-gnu/0.4.0 \
crate://crates.io/winapi-util/0.1.5 \
crate://crates.io/winapi-x86_64-pc-windows-gnu/0.4.0 \
crate://crates.io/winapi/0.3.9 \
crate://crates.io/xattr/0.2.2 \
crate://crates.io/xi-unicode/0.1.0 \
"
# FIXME: update generateme with the real MD5 of the license file
LIC_FILES_CHKSUM = " \
"
SUMMARY = "Citadel Realms UI"
HOMEPAGE = "https://subgraph,com"
LICENSE = "CLOSED"
# includes this file if it exists but does not fail
# this is useful for anything you may want to override from
# what cargo-bitbake generates.
include citadel-realms-ui-${PV}.inc
include citadel-realms-ui.inc

View File

@ -1,216 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.2 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkWindow" id="config-dialog">
<property name="width_request">600</property>
<property name="can_focus">False</property>
<property name="modal">True</property>
<child type="titlebar">
<placeholder/>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">20</property>
<property name="margin_right">20</property>
<property name="margin_top">20</property>
<property name="margin_bottom">30</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_top">10</property>
<property name="margin_bottom">20</property>
<property name="spacing">10</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">10</property>
<property name="icon_name">computer</property>
<property name="icon_size">3</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="config-realm-name">
<property name="visible">True</property>
<property name="can_focus">False</property>
<style>
<class name="config-realm-name"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="margin_bottom">10</property>
<property name="label" translatable="yes">Options</property>
<style>
<class name="config-heading"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkBox" id="config-option-list">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">20</property>
<property name="margin_right">20</property>
<property name="margin_bottom">20</property>
<property name="orientation">vertical</property>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="column_spacing">60</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">RealmFS</property>
<style>
<class name="config-heading"/>
</style>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkComboBoxText" id="config-realmfs-combo">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Overlay</property>
<style>
<class name="config-heading"/>
</style>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkComboBoxText" id="config-overlay-combo">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<items>
<item id="overlay-none" translatable="yes">None</item>
<item id="overlay-tmpfs" translatable="yes">TmpFS</item>
<item id="overlay-storage" translatable="yes">Storage Partition</item>
</items>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Terminal Theme</property>
<style>
<class name="config-heading"/>
</style>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkButton" id="theme-choose-button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<style>
<class name="config-main-box"/>
</style>
</object>
</child>
<style>
<class name="config-dialog"/>
<class name="main-window"/>
</style>
</object>
<object class="GtkSizeGroup">
<widgets>
<widget name="config-realmfs-combo"/>
<widget name="config-overlay-combo"/>
<widget name="theme-choose-button"/>
</widgets>
</object>
</interface>

View File

@ -1,40 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.2 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkBox" id="config-option">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkCheckButton" id="config-option-check">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="config-option-label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Hehehehe</property>
<style>
<class name="config-option-description"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<style>
<class name="config-option"/>
</style>
</object>
</interface>

View File

@ -1,21 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk+" version="3.24"/>
<object class="GtkWindow" id="config-dialog">
<style>
<class name="config-dialog" />
</style>
<child>
<object class="GtkBox" id="main-box">
<property name="orientation">vertical</property>
<child>
<object class="GtkBox" id="option-list">
<property name="orientation">vertical</property>
</object>
</child>
</object>
</child>
</object>
</interface>

View File

@ -1,95 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk+" version="3.24"/>
<object class="GtkWindow" id="main-window">
<property name="width_request">600</property>
<property name="decorated">False</property>
<property name="modal">True</property>
<property name="window_position">center</property>
<style>
<class name="main-window" />
</style>
<child>
<object class="GtkBox" id="main-box">
<property name="orientation">vertical</property>
<property name="spacing">20</property>
<property name="margin_left">20</property>
<property name="margin_right">20</property>
<property name="margin_top">20</property>
<property name="margin_bottom">20</property>
<child>
<object class="GtkBox" id="current-realm-box">
<property name="orientation">horizontal</property>
<property name="spacing">10</property>
<child>
<object class="GtkImage" id="current-realm-icon">
<property name="icon-name">computer</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="current-realm">
<property name="halign">start</property>
<style>
<class name="current-realm" />
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="input-box">
<child>
<object class="GtkEntry" id="input-entry">
<style>
<class name="input-entry" />
</style>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<child>
<object class="GtkBox" id="result-box">
<property name="orientation">vertical</property>
<property name="homogeneous">True</property>
<style>
<class name="result-box"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
</child>
</object>
</interface>

View File

@ -1,63 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk+" version="3.24"/>
<object class="GtkBox" id="item-entry">
<property name="spacing">25</property>
<style>
<class name="item-entry"/>
</style>
<child>
<object class="GtkImage" id="item-icon">
<property name="margin-left">20</property>
</object>
<packing>
<property name="fill">False</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="spacing">5</property>
<!--
<property name="margin-top">10</property>
<property name="margin-bottom">10</property>
-->
<child>
<object class="GtkLabel" id="item-name">
<!-- <property name="hexpand">True</property> -->
<property name="halign">start</property>
<style>
<class name="item-name"/>
</style>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="item-description">
<property name="hexpand">True</property>
<property name="halign">start</property>
<property name="margin-bottom">10</property>
<style>
<class name="item-description"/>
</style>
</object>
<packing>
<property name="fill">False</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
</object>
</interface>

View File

@ -1,121 +0,0 @@
/**
* App Window
*/
@define-color bg_color #3C4141;
@define-color window_bg @bg_color;
@define-color window_border_color #3A3A3A;
/**
* Current realm
*/
@define-color realm_name #FFFDF7;
/**
* Input
*/
@define-color selected_bg_color #4675ab;
@define-color selected_fg_color #d5eaff;
@define-color input_color #ddd;
@define-color caret_color darker(@input_color);
/**
* Result items
*/
@define-color item_name #ddd;
@define-color item_text #999;
@define-color item_box_selected #285C99;
@define-color item_text_selected #99ccff;
@define-color item_name_selected #eee;
@binding-set ConfigFocus
{
bind "j" { "move-focus" (down) };
bind "k" { "move-focus" (up) };
}
checkbutton:focus + label {
border-bottom-style: solid;
border-bottom-width: 1px;
border-bottom-color: #008cb3;
}
.config-main-box {
-gtk-key-bindings: ConfigFocus;
}
.config-dialog {
-gtk-key-bindings: ConfigFocus;
}
.main-window {
background-color: @window_bg;
border-color: @window_border_color;
border-radius: 5px;
}
.input-entry {
color: @input_color;
}
/**
* Selected text in input
*/
.input-entry *:selected,
.input-entry *:focus,
*:selected:focus {
background-color: alpha (@selected_bg_color, 0.9);
color: @selected_fg_color;
}
.config-heading {
/*
padding-top: 10px;
padding-bottom: 10px;
*/
font: 20px Sans;
}
.config-realm-name {
font: 20px Sans;
color: @realm_name;
}
.current-realm {
font: 20px Sans;
color: @realm_name;
}
.item-text {
color: @item_text;
}
.item-name {
color: @item_name;
}
.config-option.selected {
background-color: #56f9a0;
}
.selected.item-entry {
background-color: @item_box_selected;
}
.selected.item-entry .item-text {
color: @item_text_selected;
}
.selected.item-entry .item-name {
color: @item_name_selected;
}
.no-window-shadow {
margin: -20px;
}
window entry {
font: 30px Sans
}
.item-name {
font: 30px Sans
}

View File

@ -1,55 +0,0 @@
use gtk::prelude::*;
use crate::{Error, Result};
pub struct Builder {
builder: gtk::Builder,
}
impl Builder {
pub fn new(source: &str) -> Self {
let builder = gtk::Builder::from_string(source);
Builder { builder }
}
fn ok_or_err<T>(type_name: &str, name: &str, object: Option<T>) -> Result<T> {
object.ok_or(Error::Builder(format!("failed to load {} {}", type_name, name)))
}
pub fn get_window(&self, name: &str) -> Result<gtk::Window> {
Self::ok_or_err("GtkWindow", name, self.builder.get_object(name))
}
pub fn get_entry(&self, name: &str) -> Result<gtk::Entry> {
Self::ok_or_err("GtkEntry", name, self.builder.get_object(name))
}
pub fn get_box(&self, name: &str) -> Result<gtk::Box> {
Self::ok_or_err("GtkBox", name, self.builder.get_object(name))
}
pub fn get_check_button(&self, name: &str) -> Result<gtk::CheckButton> {
Self::ok_or_err("GtkCheckButton", name, self.builder.get_object(name))
}
pub fn get_button(&self, name: &str) -> Result<gtk::Button> {
Self::ok_or_err("GtkButton", name, self.builder.get_object(name))
}
pub fn get_grid(&self, name: &str) -> Result<gtk::Grid> {
Self::ok_or_err("GtkGrid", name, self.builder.get_object(name))
}
pub fn get_label(&self, name: &str) -> Result<gtk::Label> {
Self::ok_or_err("GtkLabel", name, self.builder.get_object(name))
}
pub fn get_image(&self, name: &str) -> Result<gtk::Image> {
Self::ok_or_err("GtkImage", name, self.builder.get_object(name))
}
pub fn get_combo_box_text(&self, name: &str) -> Result<gtk::ComboBoxText> {
Self::ok_or_err("GtkComboBoxText", name, self.builder.get_object(name))
}
}

View File

@ -1,121 +0,0 @@
use gtk::prelude::*;
use gdk::ModifierType;
use gdk::keys::constants;
use crate::{Result,Builder};
use crate::realms::Entity;
use std::collections::HashMap;
static CONFIG_FLAGS: &[(&str, &str)] = &[
("use-gpu", "Use GPU in Realm"),
("use-wayland", "Use Wayland in Realm"),
("use-x11", "Use X11 in Realm"),
("use-sound", "Use Sound in Realm"),
("use-shared-dir", "Mount /Shared directory in Realm"),
("use-network", "Realm has network access"),
("use-kvm", "Use KVM (/dev/kvm) in Realm"),
("use-ephemeral-home", "Use ephemeral tmpfs mount for home directory"),
];
const CONFIG_DIALOG: &str = include_str!("../data/config-dialog.ui");
const CONFIG_OPTION: &str = include_str!("../data/config-option.ui");
#[allow(dead_code)]
struct ConfigOption {
name: &'static str,
option: gtk::Box,
check: gtk::CheckButton,
style: gtk::StyleContext,
}
impl ConfigOption {
fn create(name: &'static str, description: &str, val: bool) -> Result<Self> {
let builder = Builder::new(CONFIG_OPTION);
let option = builder.get_box("config-option")?;
let check = builder.get_check_button("config-option-check")?;
check.set_active(val);
let label = builder.get_label("config-option-label")?;
label.set_text(description);
let style = option.get_style_context();
Ok(ConfigOption { name, option, check, style })
}
}
#[allow(dead_code)]
pub struct ConfigDialog {
options: Vec<ConfigOption>,
}
impl ConfigDialog {
pub fn open(realm: &Entity, config: HashMap<String,String>, parent: &gtk::Window) -> Result<Self> {
let builder = Builder::new(CONFIG_DIALOG);
let window = builder.get_window("config-dialog")?;
let option_list = builder.get_box("config-option-list")?;
let name_label = builder.get_label("config-realm-name")?;
name_label.set_text(realm.name());
window.set_decorated(false);
let mut options = Vec::new();
for (name,desc) in CONFIG_FLAGS {
let val = match config.get(*name).map(|s| s.as_str()) {
Some("true") => true,
Some("false") => false,
_ => false,
};
let option = ConfigOption::create(name, desc, val)?;
option_list.pack_start(&option.option, false, false, 5);
options.push(option);
}
let overlay = builder.get_combo_box_text("config-overlay-combo")?;
println!("config: {:?}", config);
let overlay_id = match config.get("overlay").map(|s| s.as_str()) {
Some("tmpfs") => "overlay-tmpfs",
Some("storage") => "overlay-storage",
_ => "overlay-none"
};
overlay.set_active_id(Some(overlay_id));
let realmfs = builder.get_combo_box_text("config-realmfs-combo")?;
for fs in realm.realmfs_list() {
println!("adding {}", fs.name());
// realmfs.append(Some(fs.name()), fs.name());
ComboBoxTextExt::append(&realmfs, Some(fs.name()), fs.name());
}
let scheme = builder.get_button("theme-choose-button")?;
if let Some(name) = config.get("terminal-scheme") {
// scheme.set_label(name);
ButtonExt::set_label(&scheme, name);
}
window.set_opacity(0.85);
window.set_transient_for(Some(parent));
parent.hide();
window.show_all();
window.connect_key_press_event({
let win = window.clone();
let parent = parent.clone();
move |_,key| {
let state = key.get_state();
let keyval = key.get_keyval();
let esc = keyval == constants::Escape ||
(state == ModifierType::CONTROL_MASK && keyval.to_unicode().unwrap() == '[');
if esc {
parent.show();
unsafe {
win.destroy();
}
}
Inhibit(false)
}
});
Ok(ConfigDialog { options })
}
}

View File

@ -1,13 +0,0 @@
use std::result;
use dbus;
pub type Result<T> = result::Result<T, Error>;
#[derive(Debug)]
pub enum Error {
Dbus(dbus::Error),
Nix(nix::Error),
Builder(String),
}

View File

@ -1,110 +0,0 @@
use std::os::unix::io::RawFd;
use std::thread;
use glib::Continue;
use nix::unistd::close;
use nix::errno::Errno;
use nix::sys::socket::{
socket,listen,bind,connect,accept,AddressFamily,SockType,SockFlag,SockAddr,UnixAddr
};
use crate::{Error,Result};
static SOCKET_NAME: &[u8] = b"citadel-realms-ui";
enum BindResult {
BindOk,
BindFailed(Error),
BindAddrInUse,
}
///
/// Determine if another instance is already running and if so signal it to quit.
///
/// This window is launched from a GNOME shortcut key that is meant to 'toggle' the
/// window so that if the shortcut key is used while the window is already open the
/// running instance will close.
///
/// This class will attempt to create a Unix domain stream socket in the abstract
/// namespace bound to the fixed name `SOCKET_NAME`. If no other instance is running
/// then this name will be available and the bind will succeed. In this case a thread
/// is spawned to listen for connections to the socket and the process will exit
/// the main GTK loop by calling `gtk::main_quit()` upon a connection to the listening
/// socket.
///
/// If the bind fails because the socket name is already in use, then another instance is
/// running. A connection is then made to the socket to signal the running instance to exit.
///
pub struct InstanceTracker {
fd: RawFd,
}
impl InstanceTracker {
pub fn create() -> Result<Self> {
let fd = socket(AddressFamily::Unix, SockType::Stream, SockFlag::empty(), None)
.map_err(Error::Nix)?;
Ok(InstanceTracker { fd } )
}
fn addr() -> SockAddr {
SockAddr::Unix(UnixAddr::new_abstract(SOCKET_NAME)
.expect("UnixAddr::new_abstract()"))
}
fn try_bind(&self) -> BindResult {
let addr = Self::addr();
match bind(self.fd, &addr) {
Err(nix::Error::Sys(Errno::EADDRINUSE)) => BindResult::BindAddrInUse,
Err(err) => BindResult::BindFailed(Error::Nix(err)),
Ok(()) => BindResult::BindOk,
}
}
fn connect(&self) -> bool {
let addr = Self::addr();
if let Err(err) = connect(self.fd, &addr) {
println!("Failed to connect to instance socket: {}", err);
return false;
}
if let Err(err) = close(self.fd) {
println!("error closing socket: {}", err);
}
true
}
fn spawn_reader(&self) {
thread::spawn({
let fd = self.fd;
move || {
let _ = listen(fd, 1);
let _ = accept(fd);
glib::idle_add(|| {
gtk::main_quit();
Continue(false)
});
}
});
}
pub fn bind(&self, toggle: bool) -> bool {
match self.try_bind() {
BindResult::BindAddrInUse => {
if toggle {
self.connect();
}
false
},
BindResult::BindOk => {
self.spawn_reader();
true
}
BindResult::BindFailed(err) => {
println!("error binding: {:?}", err);
false
}
}
}
}

View File

@ -1,40 +0,0 @@
mod config;
mod error;
mod builder;
mod instance;
mod matcher;
mod realms;
mod results;
mod ui;
use ui::Ui;
pub use error::{Result,Error};
pub use builder::Builder;
pub use config::ConfigDialog;
fn main() {
let tracker = match instance::InstanceTracker::create() {
Ok(tracker) => tracker,
Err(err) => {
eprintln!("Failed to create instance tracker: {:?}", err);
return;
}
};
if !tracker.bind(true) {
return;
}
if let Err(err) = gtk::init() {
eprintln!("Failed to initialize GTK: {:?}", err);
return;
}
let ui = match Ui::build() {
Ok(ui) => ui,
Err(err) => {
eprintln!("Error: {:?}", err);
return;
}
};
ui.run();
}

View File

@ -1,177 +0,0 @@
use std::rc::Rc;
use fuzzy_matcher::FuzzyMatcher;
use fuzzy_matcher::skim::SkimMatcherV2;
use crate::results::{ResultList, ResultType};
use crate::realms::{Realms,Entity};
use crate::Result;
use std::cmp::Ordering;
struct RealmMatcher<'a> {
matcher: SkimMatcherV2,
query: &'a str,
rtype: ResultType,
match_all: bool,
match_current: bool,
match_system: bool,
match_running_only: bool,
}
impl <'a> RealmMatcher<'a> {
fn new(query: &'a str, rtype: ResultType, match_all: bool, match_current: bool, match_running_only: bool) -> Self {
RealmMatcher {
matcher: SkimMatcherV2::default(),
query, rtype, match_all, match_current, match_running_only,
match_system: false,
}
}
fn terminal_matcher(query: &'a str) -> Self {
RealmMatcher::new(query, ResultType::Terminal, false, true, false)
}
fn stop_realm_matcher(query: &'a str) -> Self {
RealmMatcher::new(query, ResultType::StopRealm, false, true, true)
}
fn restart_realm_matcher(query: &'a str) -> Self {
RealmMatcher::new(query, ResultType::RestartRealm, false, true, true)
}
fn config_realm_matcher(query: &'a str) -> Self {
RealmMatcher::new(query, ResultType::ConfigRealm, false, true, false)
}
fn update_realmfs_matcher(query: &'a str) -> Self {
RealmMatcher::new(query, ResultType::UpdateRealmFS, false, true, false)
}
fn all_realms_matcher() -> Self {
RealmMatcher::new("", ResultType::Realm, true, false, false)
}
fn realms_matcher(query: &'a str) -> Self {
RealmMatcher::new(query, ResultType::Realm, false, false, false)
}
fn match_realm_query(&self, realm: &Entity) -> Option<Entity> {
self.matcher.fuzzy_indices(realm.name(), self.query)
.map(|(score, indices)|
realm.clone_with_match_info(score, indices))
}
fn match_realm_flags(&self, realm: &Entity) -> bool {
if !self.match_current && realm.is_current() {
false
} else if !self.match_system && realm.is_system_realm() {
false
} else if self.match_running_only && !realm.is_running() {
false
} else {
true
}
}
fn match_realm(&self, realm: &Entity) -> Option<Entity> {
let flags_ok = self.match_realm_flags(realm);
if self.match_all && flags_ok {
Some(realm.clone())
} else if flags_ok {
self.match_realm_query(realm)
} else {
None
}
}
fn sort_realms(&self, realms: &mut Vec<Entity>) {
realms.sort_by(|a, b| {
if a.is_running() && !b.is_running() {
Ordering::Less
} else if b.is_running() && !a.is_running() {
Ordering::Greater
} else {
a.match_score().cmp(&b.match_score())
}
})
}
fn is_realmfs_update(&self) -> bool {
self.rtype == ResultType::UpdateRealmFS
}
fn _match_realmfs(&self, _realms: &[Entity], _realmfs: &[Entity]) -> (Vec<Entity>, Vec<Entity>) {
(Vec::new(), Vec::new())
}
fn match_realm_list(&self, realms: &[Entity]) -> Vec<Entity> {
let mut matched = Vec::new();
for r in realms {
if let Some(realm) = self.match_realm(r) {
matched.push(realm);
}
}
self.sort_realms(&mut matched);
matched
}
fn result_type(&self) -> ResultType {
self.rtype
}
}
#[derive(Clone)]
pub struct Matcher {
realms: Rc<Realms>,
}
impl Matcher {
pub fn new() -> Result<Self> {
let mut realms = Realms::connect()?;
realms.reload_realms()?;
let realms = Rc::new(realms);
Ok(Matcher { realms })
}
pub fn current_realm(&self) -> Option<&Entity> {
self.realms.current_realm()
}
fn parse(text: &str) -> RealmMatcher {
if text == "*" {
return RealmMatcher::all_realms_matcher();
}
if let Some(idx) = text.find(' ') {
let (a, b) = text.split_at(idx);
let b = &b[1..];
match a {
"t" => RealmMatcher::terminal_matcher(b),
"s" => RealmMatcher::stop_realm_matcher(b),
"r" => RealmMatcher::restart_realm_matcher(b),
"c" => RealmMatcher::config_realm_matcher(b),
"u" => RealmMatcher::update_realmfs_matcher(b),
_ => RealmMatcher::realms_matcher(text)
}
} else {
RealmMatcher::realms_matcher(text)
}
}
pub fn update(&self, text: &str, results: &ResultList) {
results.clear_list();
if text.is_empty() {
return;
}
let matcher = Self::parse(text);
if matcher.is_realmfs_update() {
let realms = matcher.match_realm_list(self.realms.realmfs());
results.create_result_items(matcher.result_type(), realms);
} else {
let realms = matcher.match_realm_list(self.realms.realms());
results.create_result_items(matcher.result_type(), realms);
}
}
}

View File

@ -1,244 +0,0 @@
use std::time::Duration;
use std::rc::Rc;
use std::cell::RefCell;
use dbus::blocking::{Connection,Proxy};
use crate::{Result, Error, ConfigDialog};
use std::collections::HashMap;
#[derive(Clone,PartialEq)]
enum EntityType{
Realm,
RealmFS,
}
#[derive(Clone)]
pub struct Entity {
realms: RefCell<Realms>,
etype: EntityType,
name: String,
description: Option<String>,
realmfs: Option<String>,
flags: Option<usize>,
match_score: i64,
match_indices: Option<Vec<usize>>,
}
impl Entity {
fn new_realm(realms: Realms, (name, description, realmfs, flags): (String, String, String, u8)) -> Self {
Self::new(realms, EntityType::Realm, name, Some(description), Some(realmfs), Some(flags as usize))
}
fn new_realmfs(realms: Realms, name: String) -> Self {
Self::new(realms, EntityType::RealmFS, name, None, None, None)
}
fn new(realms: Realms, etype: EntityType, name: String, description: Option<String>, realmfs: Option<String>, flags: Option<usize>) -> Self {
let realms = RefCell::new(realms);
let match_score = 0;
let match_indices = None;
Entity { realms, etype, name, description, realmfs, flags, match_score, match_indices }
}
pub fn name(&self) -> &str {
&self.name
}
pub fn description(&self) -> &str {
self.description.as_ref().map(|s| s.as_str()).unwrap_or("")
}
fn has_flag(&self, flag: usize) -> bool {
self.flags.map(|v| v & flag != 0).unwrap_or(false)
}
pub fn is_running(&self) -> bool {
self.has_flag(0x01)
}
pub fn is_system_realm(&self) -> bool {
self.has_flag(0x04)
}
pub fn is_current(&self) -> bool {
self.has_flag(0x02)
}
pub fn is_realm(&self) -> bool {
self.etype == EntityType::Realm
}
pub fn realmfs_list(&self) -> Vec<Entity> {
self.realms.borrow().cached_realmfs.clone()
}
fn with_realm<F>(&self, f: F) -> bool
where F: Fn(&str) -> Result<()>
{
if !self.is_realm() {
return false;
}
if let Err(err) = f(self.name()) {
println!("error calling dbus method: {:?}", err);
}
true
}
pub fn activate(&self) -> bool {
self.with_realm(|name| self.realms.borrow().set_current_realm(name))
}
pub fn open_terminal(&self) -> bool {
self.with_realm(|name| self.realms.borrow().open_terminal(name))
}
pub fn stop_realm(&self) -> bool {
self.with_realm(|name| self.realms.borrow().stop_realm(name))
}
pub fn restart_realm(&self) -> bool {
self.with_realm(|name| self.realms.borrow().restart_realm(name))
}
pub fn config_realm(&self, window: &gtk::Window) -> bool {
if !self.is_realm() {
return false;
}
let config = match self.realms.borrow().get_realm_config(self.name()) {
Ok(config) => config,
Err(err) => {
println!("Error requesting realm config for {}: {:?}", self.name(), err);
return false;
}
};
let config: HashMap<String,String> = config.into_iter().collect();
let _c = ConfigDialog::open(self, config, window);
false
}
pub fn update_realmfs(&self) -> bool {
if self.is_realm() {
return false;
}
if let Err(err) = self.realms.borrow().update_realmfs(self.name()) {
println!("error calling dbus method: {:?}", err);
}
true
}
pub fn clone_with_match_info(&self, score: i64, indices: Vec<usize>) -> Self {
let mut e = self.clone();
e.match_score = score;
e.match_indices = Some(indices);
e
}
pub fn match_indices(&self) -> Option<&[usize]> {
self.match_indices.as_ref().map(|v| v.as_slice())
}
pub fn match_score(&self) -> i64 {
self.match_score
}
}
#[derive(Clone)]
pub struct Realms {
conn: Rc<Connection>,
cached_realms: Vec<Entity>,
cached_realmfs: Vec<Entity>,
}
impl Realms {
pub fn connect() -> Result<Self> {
let conn = Connection::new_system().map_err(Error::Dbus)?;
let conn = Rc::new(conn);
let cached_realms = Vec::new();
let cached_realmfs = Vec::new();
Ok(Realms { conn, cached_realms, cached_realmfs })
}
pub fn current_realm(&self) -> Option<&Entity> {
self.cached_realms.iter().find(|r| r.is_current())
}
fn with_proxy<'a>(&self) -> Proxy<'a, &Connection> {
self.conn.with_proxy("com.subgraph.realms",
"/com/subgraph/realms",
Duration::from_millis(5000))
}
pub fn realms(&self) -> &[Entity] {
&self.cached_realms
}
pub fn realmfs(&self) -> &[Entity] {
&self.cached_realmfs
}
pub fn reload_realms(&mut self) -> Result<()> {
let realms = self.list()?;
self.cached_realms.clear();
self.cached_realms.extend_from_slice(&realms);
let realmfs = self.get_realmfs_list()?;
self.cached_realmfs.clear();
self.cached_realmfs.extend_from_slice(&realmfs);
Ok(())
}
pub fn list(&self) -> Result<Vec<Entity>> {
let (list,): (Vec<(String, String, String, u8)>,) = self.with_proxy().method_call("com.subgraph.realms.Manager", "List", ()).map_err(Error::Dbus)?;
let realms = list.into_iter()
.map(|(n,d,fs, f)| Entity::new_realm(self.clone(), (n,d,fs,f)))
.collect();
Ok(realms)
}
pub fn open_terminal(&self, realm: &str) -> Result<()> {
self.with_proxy().method_call("com.subgraph.realms.Manager", "Terminal", (realm,))
.map_err(Error::Dbus)?;
Ok(())
}
pub fn stop_realm(&self, realm: &str) -> Result<()> {
self.with_proxy().method_call("com.subgraph.realms.Manager", "Stop", (realm,))
.map_err(Error::Dbus)?;
Ok(())
}
pub fn restart_realm(&self, realm: &str) -> Result<()> {
self.with_proxy().method_call("com.subgraph.realms.Manager", "Restart", (realm,))
.map_err(Error::Dbus)?;
Ok(())
}
pub fn set_current_realm(&self, realm: &str) -> Result<()> {
self.with_proxy().method_call("com.subgraph.realms.Manager", "SetCurrent", (realm,))
.map_err(Error::Dbus)?;
Ok(())
}
pub fn update_realmfs(&self, realmfs: &str) -> Result<()> {
self.with_proxy().method_call("com.subgraph.realms.Manager", "UpdateRealmFS", (realmfs,))
.map_err(Error::Dbus)?;
Ok(())
}
pub fn get_realm_config(&self, realm: &str) -> Result<Vec<(String,String)>> {
let (config,): (Vec<(String,String)>,) = self.with_proxy().method_call("com.subgraph.realms.Manager", "RealmConfig", (realm, ))
.map_err(Error::Dbus)?;
Ok(config)
}
pub fn get_realmfs_list(&self) -> Result<Vec<Entity>> {
let (list,): (Vec<String>,) = self.with_proxy().method_call("com.subgraph.realms.Manager", "ListRealmFS", ())
.map_err(Error::Dbus)?;
Ok(list.into_iter().map(|name| Entity::new_realmfs(self.clone(), name)).collect())
}
}

View File

@ -1,292 +0,0 @@
use std::rc::Rc;
use std::cell::{RefCell,RefMut};
use gtk::prelude::*;
use gtk::{IconSize};
use crate::realms::Entity;
use crate::{Result,Builder};
const UI: &str = include_str!("../data/result.ui");
#[derive(Debug,Copy,Clone,PartialEq)]
pub enum ResultType {
ConfigRealm,
Realm,
Terminal,
StopRealm,
RestartRealm,
UpdateRealmFS,
}
#[derive(Clone)]
struct ResultItem {
entity: Entity,
item: gtk::Box,
style: gtk::StyleContext,
result_type: ResultType,
}
impl ResultItem {
pub fn create(result_type: ResultType, entity: &Entity, parent: &gtk::Box) -> Result<Self> {
let entity = entity.clone();
let builder = Builder::new(UI);
let item = builder.get_box("item-entry")?;
let icon = builder.get_image("item-icon")?;
let name = builder.get_label("item-name")?;
let desc = builder.get_label("item-description")?;
if entity.is_realm() {
name.set_text(entity.name());
} else {
name.set_text(&format!("{}-realmfs", entity.name()));
}
match result_type {
ResultType::ConfigRealm => {
icon.set_from_icon_name(Some("emblem-system"), IconSize::Dialog);
desc.set_text("Configure Realm");
if let Some(indices) = entity.match_indices() {
Self::highlight_indices(&name, indices);
}
},
ResultType::Realm => {
icon.set_from_icon_name(Some("computer"), IconSize::Dialog);
icon.set_sensitive(entity.is_running());
if let Some(indices) = entity.match_indices() {
Self::highlight_indices(&name, indices);
}
if entity.description().is_empty() {
unsafe {
desc.destroy();
}
} else {
desc.set_text(entity.description());
}
},
ResultType::Terminal => {
desc.set_text("Open Terminal");
icon.set_from_icon_name(Some("utilities-terminal"), IconSize::Dialog);
icon.set_sensitive(entity.is_running());
if let Some(indices) = entity.match_indices() {
Self::highlight_indices(&name, indices);
}
}
ResultType::StopRealm => {
desc.set_text("Stop Realm");
icon.set_from_icon_name(Some("system-shutdown-symbolic"), IconSize::Dialog);
if let Some(indices) = entity.match_indices() {
Self::highlight_indices(&name, indices);
}
}
ResultType::RestartRealm => {
desc.set_text("Restart Realm");
icon.set_from_icon_name(Some("system-reboot-symbolic"), IconSize::Dialog);
if let Some(indices) = entity.match_indices() {
Self::highlight_indices(&name, indices);
}
}
ResultType::UpdateRealmFS => {
desc.set_text("Update RealmFS");
icon.set_from_icon_name(Some("drive-harddisk-symbolic"), IconSize::Dialog);
if let Some(indices) = entity.match_indices() {
Self::highlight_indices(&name, indices);
}
}
}
parent.pack_start(&item, false, true, 0);
let style = item.get_style_context();
item.show_all();
Ok(ResultItem { entity, item, style, result_type })
}
fn highlight_range(attrs: &pango::AttrList, start: u32, end: u32) {
let mut a = pango::Attribute::new_foreground(40000, 40000, 40000).unwrap();
a.set_start_index(start);
a.set_end_index(end);
attrs.insert(a);
}
fn indices_to_ranges(indices: &[usize]) -> Vec<(u32, u32)> {
let mut ranges = Vec::new();
if indices.is_empty() {
return ranges;
}
let first = indices[0] as u32;
let mut current = (first, first);
for i in &indices[1..] {
let idx = *i as u32;
if current.1 + 1 == idx {
current.1 = idx;
} else {
ranges.push(current);
current = (idx, idx);
}
}
ranges.push(current);
ranges
}
fn highlight_indices(label: &gtk::Label, indices: &[usize]) {
if indices.is_empty() {
return;
}
let ranges = Self::indices_to_ranges(indices);
let attrs = pango::AttrList::new();
for (start, end) in ranges {
Self::highlight_range(&attrs, start, end + 1);
}
LabelExt::set_attributes(label, Some(&attrs));
}
fn set_selected(&self) {
self.style.add_class("selected");
}
fn set_unselected(&self) {
self.style.remove_class("selected");
}
fn activate(&self, window: &gtk::Window) -> bool {
match self.result_type {
ResultType::Realm => self.entity.activate(),
ResultType::Terminal => self.entity.open_terminal(),
ResultType::StopRealm => self.entity.stop_realm(),
ResultType::RestartRealm => self.entity.restart_realm(),
ResultType::ConfigRealm => self.entity.config_realm(window),
ResultType::UpdateRealmFS => self.entity.update_realmfs(),
}
}
}
struct ResultItems {
items: Vec<ResultItem>,
selected: Option<usize>,
}
impl ResultItems {
fn new() -> Self {
ResultItems {
items: Vec::new(),
selected: None,
}
}
fn clear(&mut self, parentbox: &gtk::Box) {
self.selected = None;
for item in self.items.drain(..) {
ContainerExt::remove(parentbox, &item.item);
}
}
pub fn create_item(&mut self, rtype: ResultType, realm: &Entity, parent: &gtk::Box) -> Result<()> {
let item = ResultItem::create(rtype, realm, parent)?;
self.items.push(item);
if self.selected.is_none() {
self.select(0);
}
Ok(())
}
fn select(&mut self, idx: usize) {
if let Some(selected) = self.selected {
if let Some(item) = self.items.get(selected) {
item.set_unselected();
}
}
if let Some(item) = self.items.get(idx) {
item.set_selected();
self.selected = Some(idx);
}
}
fn is_empty(&self) -> bool {
self.items.is_empty()
}
fn selection_down(&mut self) {
if self.is_empty() {
return;
}
let idx = match self.selected {
Some(idx) => (idx + 1) % self.items.len(),
None => 0
};
self.select(idx);
}
fn selection_up(&mut self) {
if self.is_empty() {
return;
}
let idx = match self.selected {
Some(0) => self.items.len() - 1,
Some(idx) => idx - 1,
None => self.items.len() - 1,
};
self.select(idx);
}
fn activate_selected(&self, window: &gtk::Window) -> bool {
if let Some(idx) = self.selected {
if let Some(item) = self.items.get(idx) {
return item.activate(window);
}
}
false
}
}
#[derive(Clone)]
pub struct ResultList {
result_box: gtk::Box,
items: Rc<RefCell<ResultItems>>,
}
impl ResultList {
pub fn new(result_box: gtk::Box) -> Self {
ResultList {
result_box,
items: Rc::new(RefCell::new(ResultItems::new())),
}
}
fn items_mut(&self) -> RefMut<ResultItems> {
self.items.borrow_mut()
}
pub fn clear_list(&self) {
self.items_mut().clear(&self.result_box);
self.result_box.set_margin_top(0);
self.result_box.set_margin_bottom(0);
}
pub fn selection_down(&self) {
self.items_mut().selection_down();
}
pub fn selection_up(&self) {
self.items_mut().selection_up();
}
pub fn create_result_items(&self, rtype: ResultType, entities: Vec<Entity>) {
for r in &entities {
if let Err(err) = self.items.borrow_mut().create_item(rtype, r, &self.result_box) {
println!("failed to create {:?} item for realm {}: {:?}", rtype, r.name(), err);
}
}
}
pub fn activate_selected(&self, window: &gtk::Window) -> bool {
self.items.borrow().activate_selected(window)
}
}

View File

@ -1,152 +0,0 @@
use gtk::prelude::*;
use gtk::StyleContext;
use gdk::ModifierType;
use gdk::keys::constants;
use crate::matcher::Matcher;
use crate::results::ResultList;
use crate::{Result,Builder};
const STYLE: &str = include_str!("../data/style.css");
const MAIN_UI: &str = include_str!("../data/main.ui");
#[derive(Clone)]
pub struct Ui {
window: gtk::Window,
window_size: (i32, i32),
input: gtk::Entry,
result_list: ResultList,
matcher: Matcher,
}
impl Ui {
pub fn run(&self) {
gtk::main();
}
pub fn build() -> Result<Self> {
let builder = Builder::new(MAIN_UI);
let window = builder.get_window("main-window")?;
let current = builder.get_label("current-realm")?;
let input = builder.get_entry("input-entry")?;
let result_box = builder.get_box("result-box")?;
window.set_opacity(0.85);
window.set_icon_name(Some("cs-privacy"));
window.show_all();
let window_size = window.get_size();
let matcher = Matcher::new()?;
if let Some(realm) = matcher.current_realm() {
current.set_text(realm.name());
} else {
current.hide();
}
let result_list = ResultList::new(result_box);
let ui = Ui {
window, window_size, input, result_list, matcher,
};
ui.setup_signals();
ui.setup_style();
Ok(ui)
}
fn setup_signals(&self) {
let ui = self.clone();
self.input.connect_activate(move |_| { ui.on_activate() });
let ui = self.clone();
self.input.connect_changed(move |e| {
let s = e.get_text();
ui.on_entry_changed(s.to_string().as_str());
});
let ui = self.clone();
self.input.connect_key_press_event(move |_,k| {
ui.on_key_press(k);
Inhibit(false)
});
/*
self.window.connect_focus_out_event(move |_,_| {
gtk::idle_add(|| {
gtk::main_quit();
Continue(false)
});
Inhibit(false)
});
*/
}
fn setup_style(&self) {
if let Some(settings) = gtk::Settings::get_default() {
settings.set_property_gtk_application_prefer_dark_theme(true);
}
let css = gtk::CssProvider::new();
if let Err(err) = css.load_from_data(STYLE.as_bytes()) {
println!("Error parsing CSS style: {}", err);
return;
}
if let Some(screen) = gdk::Screen::get_default() {
StyleContext::add_provider_for_screen(&screen, &css, gtk::STYLE_PROVIDER_PRIORITY_USER);
}
}
fn on_activate(&self) {
if self.result_list.activate_selected(&self.window) {
println!("activated");
self.input.set_text("");
glib::idle_add_local({
let (w,h) = self.window_size;
let window = self.window.clone();
move || {
window.resize(w, h);
gtk::main_quit();
Continue(false)
}
});
}
}
fn on_entry_changed(&self, text: &str) {
self.matcher.update(text, &self.result_list);
let (w,h) = self.window_size;
self.window.resize(w, h);
}
fn is_escape_key(keyval: gdk::keys::Key, state: ModifierType) -> bool {
keyval == constants::Escape ||
(state == ModifierType::CONTROL_MASK && keyval.to_unicode().unwrap() == '[')
}
fn on_key_press(&self, key: &gdk::EventKey) {
let state = key.get_state();
let keyval = key.get_keyval();
if Self::is_escape_key(key.get_keyval(), key.get_state()) {
gtk::main_quit();
}
if keyval == constants::Up {
self.result_list.selection_up();
} else if keyval == constants::Down {
self.result_list.selection_down();
} else if state == ModifierType::CONTROL_MASK {
match keyval.to_unicode().unwrap() {
'n'|'j' => self.result_list.selection_down(),
'p'|'k' => self.result_list.selection_up(),
_ => {},
}
}
}
}