├── .gitattributes ├── LICENSE ├── Makefile ├── README.md ├── config.conf ├── preview_default.gif ├── preview_fullscreen.gif ├── src ├── config.hpp ├── config_parser.cpp ├── config_parser.hpp ├── css.cpp ├── css.hpp ├── dock.cpp ├── dock.hpp ├── launcher.cpp ├── launcher.hpp ├── main.cpp ├── main.hpp ├── window.cpp └── window.hpp ├── style.css └── style_fullscreen.css /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | BINS = sysmenu 2 | LIBS = libsysmenu.so 3 | PKGS = gtkmm-4.0 gtk4-layer-shell-0 4 | SRCS = $(filter-out src/main.cpp, $(wildcard src/*.cpp)) 5 | OBJS = $(patsubst src/%,$(BUILDDIR)/%,$(SRCS:.cpp=.o)) 6 | 7 | PREFIX ?= /usr/local 8 | BINDIR ?= $(PREFIX)/bin 9 | LIBDIR ?= $(PREFIX)/lib 10 | DATADIR ?= $(PREFIX)/share 11 | BUILDDIR = build 12 | 13 | CXXFLAGS += -Oz -s -Wall -flto -fno-exceptions -fPIC 14 | LDFLAGS += -Wl,--no-as-needed,-z,now,-z,pack-relative-relocs 15 | 16 | CXXFLAGS += $(shell pkg-config --cflags $(PKGS)) 17 | LDFLAGS += $(shell pkg-config --libs $(PKGS)) 18 | 19 | $(shell mkdir -p $(BUILDDIR)) 20 | JOB_COUNT := $(EXEC) $(LIB) $(OBJS) src/git_info.hpp 21 | JOBS_DONE := $(shell ls -l $(JOB_COUNT) 2> /dev/null | wc -l) 22 | 23 | define progress 24 | $(eval JOBS_DONE := $(shell echo $$(($(JOBS_DONE) + 1)))) 25 | @printf "[$(JOBS_DONE)/$(shell echo $(JOB_COUNT) | wc -w)] %s %s\n" $(1) $(2) 26 | endef 27 | 28 | all: $(BINS) $(LIBS) 29 | 30 | install: $(all) 31 | @echo "Installing..." 32 | @install -D -t $(DESTDIR)$(BINDIR) $(BUILDDIR)/$(BINS) 33 | @install -D -t $(DESTDIR)$(LIBDIR) $(BUILDDIR)/$(LIBS) 34 | @install -D -t $(DESTDIR)$(DATADIR)/sys64/menu config.conf style.css style_fullscreen.css 35 | 36 | clean: 37 | @echo "Cleaning up" 38 | @rm -rf $(BUILDDIR) src/git_info.hpp 39 | 40 | $(BINS): src/git_info.hpp $(BUILDDIR)/main.o $(BUILDDIR)/config_parser.o 41 | $(call progress, Linking $@) 42 | @$(CXX) -o $(BUILDDIR)/$(BINS) \ 43 | $(BUILDDIR)/main.o \ 44 | $(BUILDDIR)/config_parser.o \ 45 | $(CXXFLAGS) \ 46 | $(LDFLAGS) -lwayland-client 47 | 48 | $(LIBS): $(OBJS) 49 | $(call progress, Linking $@) 50 | @$(CXX) -o $(BUILDDIR)/$(LIBS) \ 51 | $(OBJS) \ 52 | $(CXXFLAGS) \ 53 | $(LDFLAGS) \ 54 | -shared 55 | 56 | $(BUILDDIR)/%.o: src/%.cpp 57 | $(call progress, Compiling $@) 58 | @$(CXX) $(CFLAGS) -c $< -o $@ \ 59 | $(CXXFLAGS) 60 | 61 | src/git_info.hpp: 62 | $(call progress, Creating $@) 63 | @commit_hash=$$(git rev-parse HEAD); \ 64 | commit_date=$$(git show -s --format=%cd --date=short $$commit_hash); \ 65 | commit_message=$$(git show -s --format="%s" $$commit_hash | sed 's/"/\\\"/g'); \ 66 | echo "#define GIT_COMMIT_MESSAGE \"$$commit_message\"" > src/git_info.hpp; \ 67 | echo "#define GIT_COMMIT_DATE \"$$commit_date\"" >> src/git_info.hpp 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sysmenu 2 | Sysmenu is a simple and efficient application launcher written in gtkmm4
3 | ![default](https://github.com/System64fumo/sysmenu/blob/main/preview_default.gif "default") 4 | ![fullscreen](https://github.com/System64fumo/sysmenu/blob/main/preview_fullscreen.gif "fullscreen") 5 | [![Packaging status](https://repology.org/badge/vertical-allrepos/sysmenu.svg)](https://repology.org/project/sysmenu/versions) 6 | 7 | 8 | # Install 9 | You need the following dependencies: 10 | * gtkmm-4.0 11 | * gtk4-layer-shell 12 | 13 | Customize ``config.hpp`` if you wish to do so.
14 | Then to build all you need to do is run ``make`` 15 | 16 | # Why does this exist? 17 | Mainly because i got bored lol.
18 | But also because i disliked how the current programs i used behaved.
19 | So instead of fixing them i created my own!
20 | 21 | # Configuration 22 | sysmenu can be configured in 3 ways
23 | 1: By changing config.h and recompiling (Suckless style)
24 | 2: Using a config file (~/.config/sys64/menu/config.conf)
25 | 3: Using launch arguments
26 | ``` 27 | arguments: 28 | -S Hide the program on launch 29 | -s Hide the search bar 30 | -i Set launcher icon size 31 | -I Set dock icon size 32 | -m Set launcher margins 33 | -u Show name under icon 34 | -b Show scroll bars 35 | -n Max name length 36 | -p Items per row 37 | -a Anchors ("top right bottom left") 38 | -W Set window width 39 | -H Set window Height 40 | -l Disable use of layer shell 41 | -v Prints version info 42 | -D Set dock items ("Terminal,FileManager,WebBrowser,ect..") 43 | ``` 44 | 45 | # Signals 46 | You can send signals to show/hide the window.
47 | ``pkill -USR1 sysmenu`` to show.
48 | ``pkill -USR2 sysmenu`` to hide.
49 | ``pkill -RTMIN sysmenu`` to toggle.
50 | 51 | # Theming 52 | sysmenu uses your gtk4 theme by default, However it can be also load custom css,
53 | Just copy one of the included style_*.css file to ~/.config/sys64/menu/style.css
54 | 55 | # Credits 56 | [wf-shell](https://github.com/WayfireWM/wf-shell) for showing how to do launcher related stuff
57 | 58 | # Also check out 59 | [wofi](https://hg.sr.ht/~scoopta/wofi)
60 | [nwg-drawer](https://github.com/nwg-piotr/nwg-drawer)
61 | -------------------------------------------------------------------------------- /config.conf: -------------------------------------------------------------------------------- 1 | [main] 2 | start-hidden=false 3 | searchbar=true 4 | icon-size=32 5 | dock-icon-size=64 6 | app-margins=4 7 | name-under-icon=false 8 | scroll-bars=false 9 | name-length=30 10 | items-per-row=1 11 | anchors= 12 | width=400 13 | height=600 14 | monitor=0 15 | layer-shell=true 16 | dock-items= 17 | animation-duration=0 18 | use-uwsm=false 19 | -------------------------------------------------------------------------------- /preview_default.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/System64fumo/sysmenu/e1ea2861fc14987e51933f9c93382887bbfd860a/preview_default.gif -------------------------------------------------------------------------------- /preview_fullscreen.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/System64fumo/sysmenu/e1ea2861fc14987e51933f9c93382887bbfd860a/preview_fullscreen.gif -------------------------------------------------------------------------------- /src/config.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Build time configuration Description 4 | #define CONFIG_RUNTIME // Allow the use of runtime arguments 5 | #define CONFIG_FILE // Allow the use of a config file 6 | -------------------------------------------------------------------------------- /src/config_parser.cpp: -------------------------------------------------------------------------------- 1 | #include "config_parser.hpp" 2 | #include 3 | #include 4 | 5 | config_parser::config_parser(const std::string& filename) { 6 | std::ifstream file(filename); 7 | std::string line; 8 | std::string current_section; 9 | 10 | available = file.is_open(); 11 | 12 | if (available) { 13 | while (std::getline(file, line)) { 14 | line = trim(line); 15 | 16 | if (line.empty() || line[0] == ';' || line[0] == '#') { 17 | continue; 18 | } 19 | else if (line[0] == '[' && line[line.size() - 1] == ']') { 20 | current_section = line.substr(1, line.size() - 2); 21 | } 22 | else { 23 | size_t delimiter_pos = line.find('='); 24 | if (delimiter_pos != std::string::npos) { 25 | std::string key = trim(line.substr(0, delimiter_pos)); 26 | std::string value = trim(line.substr(delimiter_pos + 1)); 27 | data[current_section][key] = value; 28 | } 29 | } 30 | } 31 | file.close(); 32 | } 33 | else { 34 | std::fprintf(stderr, "Unable to open file: %s\n", filename.c_str()); 35 | } 36 | } 37 | 38 | std::string config_parser::trim(const std::string& str) { 39 | const size_t first = str.find_first_not_of(' '); 40 | if (std::string::npos == first) { 41 | return str; 42 | } 43 | const size_t last = str.find_last_not_of(' '); 44 | return str.substr(first, (last - first + 1)); 45 | } 46 | -------------------------------------------------------------------------------- /src/config_parser.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | // INI parser 6 | class config_parser { 7 | public: 8 | config_parser(const std::string&); 9 | std::map> data; 10 | bool available; 11 | 12 | private: 13 | std::string trim(const std::string&); 14 | }; 15 | -------------------------------------------------------------------------------- /src/css.cpp: -------------------------------------------------------------------------------- 1 | #include "css.hpp" 2 | 3 | #include 4 | 5 | css_loader::css_loader(std::string path, Gtk::Window *window) { 6 | if (!std::filesystem::exists(path)) 7 | return; 8 | 9 | auto css = Gtk::CssProvider::create(); 10 | css->load_from_path(path); 11 | auto style_context = window->get_style_context(); 12 | style_context->add_provider_for_display(window->property_display(), css, GTK_STYLE_PROVIDER_PRIORITY_USER); 13 | } 14 | -------------------------------------------------------------------------------- /src/css.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | class css_loader : public Glib::RefPtr { 6 | public: 7 | css_loader(std::string path, Gtk::Window *window); 8 | }; 9 | -------------------------------------------------------------------------------- /src/dock.cpp: -------------------------------------------------------------------------------- 1 | #include "dock.hpp" 2 | 3 | dock::dock(const std::map>& cfg) { 4 | config_main = cfg; 5 | get_style_context()->add_class("dock"); 6 | set_halign(Gtk::Align::CENTER); 7 | property_orientation().set_value(Gtk::Orientation::VERTICAL); 8 | set_selection_mode(Gtk::SelectionMode::NONE); 9 | set_sort_func(sigc::mem_fun(*this, &dock::on_sort)); 10 | signal_child_activated().connect(sigc::mem_fun(*this, &dock::on_child_activated)); 11 | 12 | // Make all items lowercase for easier detection 13 | config_main["main"]["dock-items"] = to_lowercase(config_main["main"]["dock-items"]); 14 | 15 | // Funky sorting 16 | size_t index = 0; 17 | std::stringstream ss(config_main["main"]["dock-items"]); 18 | std::string item; 19 | while (std::getline(ss, item, ',')) { 20 | order_map[item] = index++; 21 | } 22 | } 23 | 24 | void dock::load_items(const std::vector> &items) { 25 | dock_existing_items.clear(); 26 | for (const auto& app_info : items) { 27 | std::string name = to_lowercase(app_info->get_name()); 28 | 29 | if (!app_info->should_show() || !app_info->get_icon()) 30 | continue; 31 | 32 | if (config_main["main"]["dock-items"].find(name) == std::string::npos) 33 | continue; 34 | 35 | dock_existing_items = dock_existing_items + name; 36 | auto item = Gtk::make_managed(app_info, std::stoi(config_main["main"]["dock-icon-size"])); 37 | append(*item); 38 | } 39 | } 40 | 41 | void dock::on_child_activated(Gtk::FlowBoxChild* child) { 42 | dock_item *button = dynamic_cast(child); 43 | button->app_info->launch(std::vector>()); 44 | } 45 | 46 | dock_item::dock_item(const Glib::RefPtr &app, const int &icon_size) { 47 | app_info = app; 48 | get_style_context()->add_class("dock_item"); 49 | set_size_request(icon_size, icon_size); 50 | 51 | set_child(image_icon); 52 | image_icon.set(app->get_icon()); 53 | image_icon.set_pixel_size(icon_size); 54 | 55 | set_focus_on_click(false); 56 | } 57 | 58 | bool dock::on_sort(Gtk::FlowBoxChild *a, Gtk::FlowBoxChild *b) { 59 | // Funky sorting part 2! 60 | auto appinfo1 = dynamic_cast(a)->app_info; 61 | auto appinfo2 = dynamic_cast(b)->app_info; 62 | 63 | std::string name1 = to_lowercase(appinfo1->get_name()); 64 | std::string name2 = to_lowercase(appinfo2->get_name()); 65 | 66 | auto pos1 = order_map.find(name1); 67 | auto pos2 = order_map.find(name2); 68 | 69 | if (pos1 != order_map.end() && pos2 != order_map.end()) { 70 | return pos1->second > pos2->second; 71 | } 72 | 73 | return false; 74 | } 75 | 76 | std::string dock::to_lowercase(const std::string &str) { 77 | std::string result = str; 78 | for (char& c : result) 79 | c = std::tolower(c); 80 | return result; 81 | } 82 | -------------------------------------------------------------------------------- /src/dock.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "config.hpp" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class dock_item : public Gtk::FlowBoxChild { 9 | public: 10 | dock_item(const Glib::RefPtr &app, const int &icon_size); 11 | Glib::RefPtr app_info; 12 | 13 | private: 14 | Gtk::Image image_icon; 15 | }; 16 | 17 | class dock : public Gtk::FlowBox { 18 | public: 19 | dock(const std::map>& cfg); 20 | void load_items(const std::vector> &items); 21 | 22 | private: 23 | std::map> config_main; 24 | std::map order_map; 25 | std::string dock_existing_items; 26 | void on_child_activated(Gtk::FlowBoxChild* child); 27 | bool on_sort(Gtk::FlowBoxChild *a, Gtk::FlowBoxChild *b); 28 | std::string to_lowercase(const std::string &str); 29 | }; 30 | -------------------------------------------------------------------------------- /src/launcher.cpp: -------------------------------------------------------------------------------- 1 | #include "launcher.hpp" 2 | 3 | launcher::launcher(const std::map>& cfg, const Glib::RefPtr &app) : Gtk::Box(), app_info(app), config_main(cfg) { 4 | name = app_info->get_name(); 5 | long_name = app_info->get_display_name(); 6 | progr = app_info->get_executable(); 7 | descr = app_info->get_description(); 8 | 9 | if (config_main["main"]["items-per-row"] == "1") 10 | set_margin_top(std::stoi(config_main["main"]["app-margins"])); 11 | else 12 | set_margin(std::stoi(config_main["main"]["app-margins"])); 13 | 14 | image_program.set(app->get_icon()); 15 | image_program.set_pixel_size(std::stoi(config_main["main"]["icon-size"])); 16 | 17 | if (long_name.length() > std::stoul(config_main["main"]["name-length"])) 18 | label_program.set_text(long_name.substr(0, std::stoi(config_main["main"]["name-length"]) - 2) + ".."); 19 | else 20 | label_program.set_text(long_name); 21 | 22 | int size_request = -1; 23 | if (config_main["main"]["name-under-icon"] == "true") { 24 | set_orientation(Gtk::Orientation::VERTICAL); 25 | size_request = std::stoi(config_main["main"]["name-length"]) * 10; 26 | image_program.set_vexpand(true); 27 | image_program.set_valign(Gtk::Align::END); 28 | label_program.set_margin_top(3); 29 | label_program.set_vexpand(true); 30 | label_program.set_valign(Gtk::Align::START); 31 | } 32 | else 33 | label_program.property_margin_start().set_value(10); 34 | 35 | append(image_program); 36 | append(label_program); 37 | 38 | set_hexpand(true); 39 | set_size_request(size_request, size_request); 40 | 41 | get_style_context()->add_class("launcher"); 42 | set_tooltip_text(descr); 43 | } 44 | 45 | bool launcher::matches(Glib::ustring pattern) { 46 | Glib::ustring text = 47 | name.lowercase() + "$" + 48 | long_name.lowercase() + "$" + 49 | progr.lowercase() + "$" + 50 | descr.lowercase(); 51 | 52 | return text.find(pattern.lowercase()) != text.npos; 53 | } 54 | 55 | bool launcher::operator < (const launcher& other) { 56 | return Glib::ustring(app_info->get_name()).lowercase() 57 | < Glib::ustring(other.app_info->get_name()).lowercase(); 58 | } 59 | -------------------------------------------------------------------------------- /src/launcher.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "config.hpp" 8 | 9 | class launcher : public Gtk::Box { 10 | public: 11 | launcher(const std::map>& cfg, const Glib::RefPtr &app); 12 | Glib::RefPtr app_info; 13 | 14 | bool matches(Glib::ustring text); 15 | bool operator < (const launcher& other); 16 | 17 | private: 18 | std::map> config_main; 19 | Gtk::Image image_program; 20 | Gtk::Label label_program; 21 | 22 | Glib::ustring name; 23 | Glib::ustring long_name; 24 | Glib::ustring progr; 25 | Glib::ustring descr; 26 | }; 27 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "main.hpp" 2 | #include "config.hpp" 3 | #include "config_parser.hpp" 4 | #include "git_info.hpp" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | void handle_signal(int signum) { 12 | sysmenu_handle_signal_ptr(win, signum); 13 | } 14 | 15 | void load_libsysmenu() { 16 | void* handle = dlopen("libsysmenu.so", RTLD_LAZY); 17 | if (!handle) { 18 | std::cerr << "Cannot open library: " << dlerror() << '\n'; 19 | exit(1); 20 | } 21 | 22 | sysmenu_create_ptr = (sysmenu_create_func)dlsym(handle, "sysmenu_create"); 23 | sysmenu_handle_signal_ptr = (sysmenu_handle_signal_func)dlsym(handle, "sysmenu_signal"); 24 | 25 | if (!sysmenu_create_ptr || !sysmenu_handle_signal_ptr) { 26 | std::cerr << "Cannot load symbols: " << dlerror() << '\n'; 27 | dlclose(handle); 28 | exit(1); 29 | } 30 | } 31 | 32 | int main(int argc, char* argv[]) { 33 | // Load the config 34 | #ifdef CONFIG_FILE 35 | std::string config_path; 36 | std::map> config; 37 | std::map> config_usr; 38 | 39 | bool cfg_sys = std::filesystem::exists("/usr/share/sys64/menu/config.conf"); 40 | bool cfg_sys_local = std::filesystem::exists("/usr/local/share/sys64/menu/config.conf"); 41 | bool cfg_usr = std::filesystem::exists(std::string(getenv("HOME")) + "/.config/sys64/menu/config.conf"); 42 | 43 | // Load default config 44 | if (cfg_sys) 45 | config_path = "/usr/share/sys64/menu/config.conf"; 46 | else if (cfg_sys_local) 47 | config_path = "/usr/local/share/sys64/menu/config.conf"; 48 | else 49 | std::fprintf(stderr, "No default config found, Things will get funky!\n"); 50 | 51 | config = config_parser(config_path).data; 52 | 53 | // Load user config 54 | if (cfg_usr) 55 | config_path = std::string(getenv("HOME")) + "/.config/sys64/menu/config.conf"; 56 | else 57 | std::fprintf(stderr, "No user config found\n"); 58 | 59 | config_usr = config_parser(config_path).data; 60 | 61 | // Merge configs 62 | for (const auto& [key, nested_map] : config_usr) 63 | for (const auto& [inner_key, inner_value] : nested_map) 64 | config[key][inner_key] = inner_value; 65 | 66 | // Sanity check 67 | if (!(cfg_sys || cfg_sys_local || cfg_usr)) { 68 | std::fprintf(stderr, "No config available, Something ain't right here."); 69 | return 1; 70 | } 71 | #endif 72 | 73 | // Read launch arguments 74 | #ifdef CONFIG_RUNTIME 75 | while (true) { 76 | switch(getopt(argc, argv, "Ssi:I:m:ubn:p:a:W:H:M:lD:vh")) { 77 | case 'S': 78 | config["main"]["start-hidden"] = "true"; 79 | continue; 80 | 81 | case 's': 82 | config["main"]["searchbar"] = "false"; 83 | continue; 84 | 85 | case 'i': 86 | config["main"]["icon-size"] = optarg; 87 | continue; 88 | 89 | case 'I': 90 | config["main"]["dock-icon-size"] = optarg; 91 | continue; 92 | 93 | case 'm': 94 | config["main"]["app-margin"] = optarg; 95 | continue; 96 | 97 | case 'u': 98 | config["main"]["name-under-icon"] = "true"; 99 | continue; 100 | 101 | case 'b': 102 | config["main"]["scroll-bars"] = "true"; 103 | continue; 104 | 105 | case 'n': 106 | config["main"]["name-length"] = optarg; 107 | continue; 108 | 109 | case 'p': 110 | config["main"]["items-per-row"] = optarg; 111 | continue; 112 | 113 | case 'a': 114 | config["main"]["anchors"] = optarg; 115 | continue; 116 | 117 | case 'W': 118 | config["main"]["width"] = optarg; 119 | continue; 120 | 121 | case 'H': 122 | config["main"]["height"] = optarg; 123 | continue; 124 | 125 | case 'M': 126 | config["main"]["monitor"] = optarg; 127 | continue; 128 | 129 | case 'l': 130 | config["main"]["layer-shell"] = "false"; 131 | continue; 132 | 133 | case 'D': 134 | config["main"]["dock-items"] = optarg; 135 | config["main"]["layer-shell"] = "true"; 136 | config["main"]["anchors"] = "top right bottom left"; 137 | continue; 138 | 139 | case 'v': 140 | std::cout << "Commit: " << GIT_COMMIT_MESSAGE << std::endl; 141 | std::cout << "Date: " << GIT_COMMIT_DATE << std::endl; 142 | return 0; 143 | 144 | case 'h': 145 | default : 146 | std::cout << "usage:" << std::endl; 147 | std::cout << " sysmenu [argument...]:\n" << std::endl; 148 | std::cout << "arguments:" << std::endl; 149 | std::cout << " -S Hide the program on launch" << std::endl; 150 | std::cout << " -s Hide the search bar" << std::endl; 151 | std::cout << " -i Set launcher icon size" << std::endl; 152 | std::cout << " -I Set dock icon size" << std::endl; 153 | std::cout << " -m Set launcher margins" << std::endl; 154 | std::cout << " -u Show name under icon" << std::endl; 155 | std::cout << " -b Show scroll bars" << std::endl; 156 | std::cout << " -n Max name length" << std::endl; 157 | std::cout << " -p Items per row" << std::endl; 158 | std::cout << " -a Set anchors" << std::endl; 159 | std::cout << " -W Set window width" << std::endl; 160 | std::cout << " -H Set window Height" << std::endl; 161 | std::cout << " -M Set primary monitor" << std::endl; 162 | std::cout << " -l Disable use of layer shell" << std::endl; 163 | std::cout << " -D Set dock items" << std::endl; 164 | std::cout << " -v Prints version info" << std::endl; 165 | std::cout << " -h Show this help message" << std::endl; 166 | return 0; 167 | 168 | case -1: 169 | break; 170 | } 171 | 172 | break; 173 | } 174 | #endif 175 | 176 | Glib::RefPtr app = Gtk::Application::create("funky.sys64.sysmenu"); 177 | app->hold(); 178 | 179 | load_libsysmenu(); 180 | win = sysmenu_create_ptr(config); 181 | 182 | // Catch signals 183 | signal(SIGUSR1, handle_signal); 184 | signal(SIGUSR2, handle_signal); 185 | signal(SIGRTMIN, handle_signal); 186 | 187 | return app->run(); 188 | } 189 | -------------------------------------------------------------------------------- /src/main.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "config.hpp" 3 | #include "window.hpp" 4 | 5 | sysmenu* win; 6 | 7 | typedef sysmenu* (*sysmenu_create_func)(const std::map>&); 8 | sysmenu_create_func sysmenu_create_ptr; 9 | 10 | typedef void (*sysmenu_handle_signal_func)(sysmenu*, int); 11 | sysmenu_handle_signal_func sysmenu_handle_signal_ptr; 12 | -------------------------------------------------------------------------------- /src/window.cpp: -------------------------------------------------------------------------------- 1 | #include "main.hpp" 2 | #include "window.hpp" 3 | #include "css.hpp" 4 | #include "config.hpp" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | sysmenu::sysmenu(const std::map>& cfg) : config_main(cfg) { 19 | if (config_main["main"]["layer-shell"] == "true") { 20 | gtk_layer_init_for_window(gobj()); 21 | gtk_layer_set_keyboard_mode(gobj(), GTK_LAYER_SHELL_KEYBOARD_MODE_ON_DEMAND); 22 | gtk_layer_set_namespace(gobj(), "sysmenu"); 23 | 24 | bool edge_top = (config_main["main"]["anchors"].find("top") != std::string::npos); 25 | bool edge_right = (config_main["main"]["anchors"].find("right") != std::string::npos); 26 | bool edge_bottom = (config_main["main"]["anchors"].find("bottom") != std::string::npos); 27 | bool edge_left = (config_main["main"]["anchors"].find("left") != std::string::npos); 28 | 29 | gtk_layer_set_anchor(gobj(), GTK_LAYER_SHELL_EDGE_TOP, edge_top); 30 | gtk_layer_set_anchor(gobj(), GTK_LAYER_SHELL_EDGE_RIGHT, edge_right); 31 | gtk_layer_set_anchor(gobj(), GTK_LAYER_SHELL_EDGE_BOTTOM, edge_bottom); 32 | gtk_layer_set_anchor(gobj(), GTK_LAYER_SHELL_EDGE_LEFT, edge_left); 33 | 34 | if (config_main["main"]["dock-items"] != "") 35 | gtk_layer_set_anchor(gobj(), GTK_LAYER_SHELL_EDGE_TOP, false); 36 | } 37 | 38 | // Dock 39 | if (config_main["main"]["dock-items"] != "") { 40 | sysmenu_dock = Gtk::make_managed(config_main); 41 | gtk_layer_set_layer(gobj(), GTK_LAYER_SHELL_LAYER_BOTTOM); 42 | // TODO: Dragging causes the inner scrollbox to resize, This is bad as 43 | // it uses a lot of cpu power trying to resize things. 44 | // Is this even possible to fix? 45 | 46 | // Set up gestures 47 | gesture_drag = Gtk::GestureDrag::create(); 48 | gesture_drag->signal_drag_begin().connect(sigc::mem_fun(*this, &sysmenu::on_drag_start)); 49 | gesture_drag->signal_drag_update().connect(sigc::mem_fun(*this, &sysmenu::on_drag_update)); 50 | gesture_drag->signal_drag_end().connect(sigc::mem_fun(*this, &sysmenu::on_drag_stop)); 51 | 52 | // Set up revealer 53 | revealer_dock.set_child(*sysmenu_dock); 54 | revealer_dock.set_transition_type(Gtk::RevealerTransitionType::SLIDE_UP); 55 | revealer_dock.set_transition_duration(500); 56 | revealer_dock.set_reveal_child(true); 57 | revealer_search.set_reveal_child(false); 58 | 59 | gtk_layer_set_layer(gobj(), GTK_LAYER_SHELL_LAYER_BOTTOM); 60 | box_layout.append(box_top); 61 | box_top.append(revealer_dock); 62 | box_top.property_orientation().set_value(Gtk::Orientation::VERTICAL); 63 | box_top.add_controller(gesture_drag); 64 | 65 | // Set window height to the dock's height 66 | config_main["main"]["height"] = "30"; 67 | box_layout.set_valign(Gtk::Align::END); 68 | } 69 | else 70 | gtk_layer_set_layer(gobj(), GTK_LAYER_SHELL_LAYER_TOP); 71 | 72 | // Initialize 73 | set_name("sysmenu"); 74 | set_default_size(std::stoi(config_main["main"]["width"]), std::stoi(config_main["main"]["height"])); 75 | set_hide_on_close(true); 76 | 77 | box_layout.property_orientation().set_value(Gtk::Orientation::VERTICAL); 78 | set_child(box_layout); 79 | box_layout.append(scrolled_window_inner); 80 | scrolled_window_inner.set_policy(Gtk::PolicyType::EXTERNAL, Gtk::PolicyType::EXTERNAL); 81 | scrolled_window_inner.set_child(box_layout_inner); 82 | scrolled_window_inner.set_kinetic_scrolling(false); 83 | box_layout_inner.set_orientation(Gtk::Orientation::VERTICAL); 84 | box_layout_inner.get_style_context()->add_class("box_layout_inner"); 85 | 86 | // Sadly there does not seem to be a way to detect what the default monitor is 87 | // Gotta assume or ask the user for their monitor of choice 88 | display = gdk_display_get_default(); 89 | monitors = gdk_display_get_monitors(display); 90 | monitorCount = g_list_model_get_n_items(monitors); 91 | monitor = GDK_MONITOR(g_list_model_get_item(monitors, std::stoi(config_main["main"]["monitor"]))); 92 | 93 | GdkRectangle geometry; 94 | gdk_monitor_get_geometry(monitor, &geometry); 95 | max_height = geometry.height; 96 | 97 | // Keep the values in check 98 | if (std::stoi(config_main["main"]["monitor"]) < 0) 99 | config_main["main"]["monitor"] = "0"; 100 | else if (std::stoi(config_main["main"]["monitor"]) >= monitorCount) 101 | config_main["main"]["monitor"] = std::to_string(monitorCount - 1); 102 | else if (config_main["main"]["layer-shell"] == "true") 103 | gtk_layer_set_monitor(gobj(), GDK_MONITOR(g_list_model_get_item(monitors, std::stoi(config_main["main"]["monitor"])))); 104 | 105 | // Events 106 | auto controller = Gtk::EventControllerKey::create(); 107 | controller->signal_key_pressed().connect( 108 | sigc::mem_fun(*this, &sysmenu::on_key_press), true); 109 | 110 | if (config_main["main"]["searchbar"] == "true") { 111 | entry_search.get_style_context()->add_class("entry_search"); 112 | if (config_main["main"]["dock-items"] != "") { 113 | box_top.append(revealer_search); 114 | revealer_search.get_style_context()->add_class("revealer_search"); 115 | revealer_search.set_child(entry_search); 116 | revealer_search.set_transition_type(Gtk::RevealerTransitionType::SLIDE_UP); 117 | revealer_search.set_transition_duration(500); 118 | } 119 | else { 120 | box_layout.prepend(entry_search); 121 | } 122 | 123 | entry_search.set_halign(Gtk::Align::CENTER); 124 | entry_search.set_icon_from_icon_name("system-search-symbolic", Gtk::Entry::IconPosition::PRIMARY); 125 | entry_search.set_placeholder_text("Search"); 126 | entry_search.set_margin(10); 127 | entry_search.set_size_request(std::stoi(config_main["main"]["width"]) - 20, -1); 128 | 129 | entry_search.signal_changed().connect(sigc::mem_fun(*this, &sysmenu::on_search_changed)); 130 | entry_search.signal_activate().connect([this]() { 131 | if (selected_child) 132 | run_menu_item(selected_child, false); 133 | }); 134 | flowbox_itembox.set_sort_func(sigc::mem_fun(*this, &sysmenu::on_sort)); 135 | flowbox_itembox.set_filter_func(sigc::mem_fun(*this, &sysmenu::on_filter)); 136 | 137 | if (config_main["main"]["dock-items"] == "") 138 | entry_search.grab_focus(); 139 | else 140 | show(); 141 | } 142 | add_controller(controller); 143 | 144 | box_layout.get_style_context()->add_class("box_layout"); 145 | 146 | if (std::stoi(config_main["main"]["items-per-row"]) != 1) { 147 | flowbox_itembox.set_halign(Gtk::Align::CENTER); 148 | history_size = std::stoi(config_main["main"]["items-per-row"]); 149 | } 150 | 151 | // Recently launched 152 | // TODO: Add history size config option 153 | if (history_size > 0) { 154 | if (std::stoi(config_main["main"]["items-per-row"]) != 1) { 155 | box_layout_inner.append(flowbox_recent); 156 | flowbox_recent.set_halign(Gtk::Align::CENTER); 157 | flowbox_recent.set_orientation(Gtk::Orientation::HORIZONTAL); 158 | } 159 | else 160 | box_scrolled_contents.append(flowbox_recent); 161 | 162 | flowbox_recent.set_visible(false); 163 | flowbox_recent.get_style_context()->add_class("flowbox_recent"); 164 | flowbox_recent.set_valign(Gtk::Align::START); 165 | flowbox_recent.set_vexpand_set(true); 166 | flowbox_recent.set_min_children_per_line(std::stoi(config_main["main"]["items-per-row"])); 167 | flowbox_recent.set_max_children_per_line(std::stoi(config_main["main"]["items-per-row"])); 168 | flowbox_recent.signal_child_activated().connect([this](Gtk::FlowBoxChild* child) { 169 | run_menu_item(child, true); 170 | }); 171 | } 172 | 173 | box_layout_inner.append(scrolled_window); 174 | scrolled_window.set_child(box_scrolled_contents); 175 | box_scrolled_contents.set_orientation(Gtk::Orientation::VERTICAL); 176 | box_scrolled_contents.append(flowbox_itembox); 177 | 178 | if (config_main["main"]["searchbar"] != "true") 179 | scrolled_window.set_policy(Gtk::PolicyType::EXTERNAL, Gtk::PolicyType::EXTERNAL); 180 | 181 | flowbox_itembox.set_valign(Gtk::Align::START); 182 | flowbox_itembox.set_min_children_per_line(std::stoi(config_main["main"]["items-per-row"])); 183 | flowbox_itembox.set_max_children_per_line(std::stoi(config_main["main"]["items-per-row"])); 184 | flowbox_itembox.set_vexpand(true); 185 | flowbox_itembox.signal_child_activated().connect([this](Gtk::FlowBoxChild* child) { 186 | run_menu_item(child, false); 187 | }); 188 | 189 | // Load custom css 190 | std::string style_path; 191 | if (std::filesystem::exists(std::string(getenv("HOME")) + "/.config/sys64/menu/style.css")) 192 | style_path = std::string(getenv("HOME")) + "/.config/sys64/menu/style.css"; 193 | else if (std::filesystem::exists("/usr/share/sys64/menu/style.css")) 194 | style_path = "/usr/share/sys64/menu/style.css"; 195 | else 196 | style_path = "/usr/local/share/sys64/menu/style.css"; 197 | 198 | css_loader loader(style_path, this); 199 | 200 | // Load applications 201 | GAppInfoMonitor* app_info_monitor = g_app_info_monitor_get(); 202 | g_signal_connect(app_info_monitor, "changed", G_CALLBACK(+[](GAppInfoMonitor* monitor, gpointer user_data) { 203 | sysmenu* self = static_cast(user_data); 204 | self->app_info_changed(monitor); 205 | }), this); 206 | 207 | std::thread thread_appinfo(&sysmenu::app_info_changed, this, nullptr); 208 | thread_appinfo.detach(); 209 | 210 | if (config_main["main"]["start-hidden"] != "true") 211 | handle_signal(SIGUSR1); 212 | } 213 | 214 | void sysmenu::on_search_changed() { 215 | flowbox_recent.set_visible(entry_search.get_text() == "" && app_list_history.size() > 0); 216 | matches = 0; 217 | match = ""; 218 | selected_child = nullptr; 219 | flowbox_itembox.invalidate_filter(); 220 | } 221 | 222 | bool sysmenu::on_key_press(const guint &keyval, const guint &keycode, const Gdk::ModifierType &state) { 223 | if (keyval == 65307) // Escape key 224 | handle_signal(SIGUSR2); 225 | else if (keyval == 65056) { // Shift Tab 226 | if (config_main["main"]["searchbar"] == "true" && config_main["main"]["dock-items"] == "") 227 | entry_search.grab_focus(); 228 | } 229 | else if (keyval == 65289) { // Tab 230 | auto selected_children = flowbox_itembox.get_selected_children(); 231 | if (selected_children.size() >= 0) 232 | return false; 233 | 234 | auto children = flowbox_itembox.get_children(); 235 | 236 | if (selected_child == nullptr) 237 | selected_child = dynamic_cast(children[0]); 238 | 239 | flowbox_itembox.select_child(*selected_child); 240 | selected_child->grab_focus(); 241 | } 242 | else if (keyval == 65293) // Enter key 243 | return false; 244 | 245 | return true; 246 | } 247 | 248 | bool sysmenu::on_filter(Gtk::FlowBoxChild *child) { 249 | auto button = dynamic_cast (child->get_child()); 250 | auto text = entry_search.get_text(); 251 | 252 | if (button->matches(text)) { 253 | matches++; 254 | if (matches == 1) { 255 | selected_child = child; 256 | match = button->app_info->get_executable(); 257 | } 258 | return true; 259 | } 260 | 261 | return false; 262 | } 263 | 264 | bool sysmenu::on_sort(Gtk::FlowBoxChild* a, Gtk::FlowBoxChild* b) { 265 | auto b1 = dynamic_cast (a->get_child()); 266 | auto b2 = dynamic_cast (b->get_child()); 267 | return *b2 < *b1; 268 | } 269 | 270 | void sysmenu::app_info_changed(GAppInfoMonitor* gappinfomonitor) { 271 | app_list = Gio::AppInfo::get_all(); 272 | flowbox_itembox.remove_all(); 273 | 274 | if (config_main["main"]["dock-items"] != "") 275 | sysmenu_dock->remove_all(); 276 | 277 | // Load applications 278 | for (auto app : app_list) 279 | load_menu_item(app); 280 | 281 | // Load dock items 282 | if (config_main["main"]["dock-items"] != "") 283 | sysmenu_dock->load_items(app_list); 284 | 285 | selected_child = nullptr; 286 | } 287 | 288 | void sysmenu::load_menu_item(const Glib::RefPtr &app_info) { 289 | if (!app_info || !app_info->should_show() || !app_info->get_icon()) 290 | return; 291 | 292 | auto name = app_info->get_name(); 293 | auto exec = app_info->get_executable(); 294 | 295 | // Skip loading empty entries 296 | if (name.empty() || exec.empty()) 297 | return; 298 | 299 | items.push_back(std::unique_ptr(new launcher(config_main, app_info))); 300 | flowbox_itembox.append(*items.back()); 301 | } 302 | 303 | void sysmenu::run_menu_item(Gtk::FlowBoxChild* child, const bool &recent) { 304 | launcher *item = dynamic_cast(child->get_child()); 305 | 306 | if (!item || !item->app_info) 307 | return; 308 | 309 | Glib::ustring cmd = item->app_info->get_executable(); 310 | 311 | bool uwsm_exists = Glib::find_program_in_path("uwsm").empty() == false; 312 | 313 | std::vector args; 314 | if (uwsm_exists && config_main["main"]["use-uwsm"] == "true") 315 | args = { "uwsm", "app", "--", cmd.raw() }; 316 | else 317 | args = { cmd.raw() }; 318 | 319 | Glib::spawn_async("", args, Glib::SpawnFlags::SEARCH_PATH); 320 | 321 | handle_signal(SIGUSR2); 322 | 323 | // Don't add the item again if the click came from the recent's list 324 | if (recent) 325 | return; 326 | 327 | auto it = std::find(app_list_history.begin(), app_list_history.end(), item->app_info); 328 | if (it != app_list_history.end()) 329 | return; 330 | 331 | if (app_list_history.size() >= history_size) { 332 | auto first_child = flowbox_recent.get_child_at_index(0); 333 | flowbox_recent.remove(*first_child); 334 | app_list_history.erase(app_list_history.begin()); 335 | } 336 | 337 | launcher *recent_item = new launcher(config_main, item->app_info); 338 | recent_item->set_size_request(-1, -1); 339 | flowbox_recent.append(*recent_item); 340 | app_list_history.push_back(item->app_info); 341 | flowbox_recent.set_visible(true); 342 | } 343 | 344 | 345 | void sysmenu::handle_signal(const int &signum) { 346 | Glib::signal_idle().connect([this, signum]() { 347 | if (signum == SIGUSR1) { // Showing window 348 | gtk_layer_set_layer(gobj(), GTK_LAYER_SHELL_LAYER_TOP); 349 | flowbox_itembox.unselect_all(); 350 | if (config_main["main"]["dock-items"] != "") { 351 | revealer_search.set_reveal_child(true); 352 | revealer_dock.set_reveal_child(false); 353 | box_layout.set_valign(Gtk::Align::FILL); 354 | box_layout.set_size_request(-1, max_height); 355 | gtk_layer_set_anchor(gobj(), GTK_LAYER_SHELL_EDGE_TOP, true); 356 | } 357 | 358 | // Scroll to the top of the list 359 | scrolled_window.get_vadjustment()->set_value(0); 360 | scrolled_window_inner.get_vadjustment()->set_value(0); 361 | 362 | show(); 363 | get_style_context()->add_class("visible"); 364 | 365 | if (config_main["main"]["searchbar"] == "true" && config_main["main"]["dock-items"] == "") 366 | entry_search.grab_focus(); 367 | 368 | } else if (signum == SIGUSR2) { // Hiding window 369 | get_style_context()->remove_class("visible"); 370 | if (config_main["main"]["dock-items"] != "") { 371 | revealer_search.set_reveal_child(false); 372 | revealer_dock.set_reveal_child(true); 373 | box_layout.set_valign(Gtk::Align::END); 374 | box_layout.set_size_request(-1, -1); 375 | gtk_layer_set_anchor(gobj(), GTK_LAYER_SHELL_EDGE_TOP, false); 376 | gtk_layer_set_layer(gobj(), GTK_LAYER_SHELL_LAYER_BOTTOM); 377 | } 378 | else { 379 | Glib::signal_timeout().connect_once([&]() { 380 | hide(); 381 | gtk_layer_set_layer(gobj(), GTK_LAYER_SHELL_LAYER_BOTTOM); 382 | }, std::stoi(config_main["main"]["animation-duration"])); 383 | } 384 | if (config_main["main"]["searchbar"] == "true") 385 | entry_search.set_text(""); 386 | flowbox_recent.unselect_all(); 387 | flowbox_itembox.unselect_all(); 388 | 389 | } else if (signum == SIGRTMIN) { // Toggling window 390 | if (config_main["main"]["dock-items"] != "") { 391 | starting_height = box_layout.get_height(); 392 | if (box_layout.get_height() < max_height / 2) 393 | handle_signal(SIGUSR1); 394 | else 395 | handle_signal(SIGUSR2); 396 | } 397 | else { 398 | if (is_visible()) 399 | handle_signal(SIGUSR2); 400 | else 401 | handle_signal(SIGUSR1); 402 | } 403 | } 404 | return false; 405 | }); 406 | } 407 | 408 | void sysmenu::on_drag_start(const double &x, const double &y) { 409 | // For now disable swipe gestures on non touch inputs 410 | // since they're broken on on touch devices 411 | if (!gesture_drag->get_current_event()->get_pointer_emulated()) { 412 | gesture_drag->reset(); 413 | return; 414 | } 415 | 416 | GdkRectangle geometry; 417 | gdk_monitor_get_geometry(monitor, &geometry); 418 | max_height = geometry.height; 419 | 420 | starting_height = box_layout.get_height(); 421 | gtk_layer_set_anchor(gobj(), GTK_LAYER_SHELL_EDGE_TOP, false); 422 | box_layout.set_valign(Gtk::Align::END); 423 | } 424 | 425 | void sysmenu::on_drag_update(const double &x, const double &y) { 426 | bool margin_ignore = (-y < std::stoi(config_main["main"]["dock-icon-size"]) / 2); 427 | int height = starting_height - y; 428 | 429 | // This mess right here clamps the height to 0 & max possible height 430 | // It also adds a margin where you have to pull at least 50% of the dock's.. 431 | // height to trigger a pull. 432 | if (starting_height < max_height) { 433 | // Pulled from bottom 434 | if (y > 0 || height >= max_height) 435 | return; 436 | else if (margin_ignore) 437 | height = 0; 438 | } 439 | else { 440 | // Pulled from top 441 | if (y < 0 || height < std::stoi(config_main["main"]["dock-icon-size"])) 442 | return; 443 | } 444 | 445 | box_layout.set_size_request(-1, height); 446 | 447 | gtk_layer_set_layer(gobj(), margin_ignore ? GTK_LAYER_SHELL_LAYER_TOP : GTK_LAYER_SHELL_LAYER_BOTTOM); 448 | revealer_dock.set_reveal_child(margin_ignore); 449 | revealer_search.set_reveal_child(!margin_ignore); 450 | } 451 | 452 | void sysmenu::on_drag_stop(const double &x, const double &y) { 453 | // For now disable swipe gestures on non touch inputs 454 | // since they're broken on on touch devices 455 | if (!gesture_drag->get_current_event()->get_pointer_emulated()) { 456 | gesture_drag->reset(); 457 | return; 458 | } 459 | 460 | // Top position 461 | if (box_layout.get_height() > max_height / 2) 462 | handle_signal(SIGUSR1); 463 | // Bottom Position 464 | else 465 | handle_signal(SIGUSR2); 466 | } 467 | 468 | extern "C" { 469 | sysmenu *sysmenu_create(const std::map>& cfg) { 470 | return new sysmenu(cfg); 471 | } 472 | void sysmenu_signal(sysmenu *window, int signal) { 473 | window->handle_signal(signal); 474 | } 475 | } 476 | -------------------------------------------------------------------------------- /src/window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "launcher.hpp" 3 | #include "dock.hpp" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | class sysmenu : public Gtk::Window { 16 | public: 17 | sysmenu(const std::map>& cfg); 18 | void handle_signal(const int &signum); 19 | 20 | private: 21 | std::map> config_main; 22 | int starting_height = 0; 23 | int max_height; 24 | uint history_size = 3; 25 | 26 | int matches = 0; 27 | Glib::ustring match = ""; 28 | std::vector> app_list; 29 | std::vector> app_list_history; 30 | std::vector> items; 31 | 32 | GdkDisplay *display; 33 | GListModel *monitors; 34 | GdkMonitor *monitor; 35 | int monitorCount; 36 | 37 | dock *sysmenu_dock; 38 | Gtk::Entry entry_search; 39 | Gtk::Box box_layout; 40 | Gtk::Box box_layout_inner; 41 | Gtk::Box box_scrolled_contents; 42 | Gtk::Revealer revealer_dock; 43 | Gtk::Revealer revealer_search; 44 | Gtk::FlowBox flowbox_recent; 45 | Gtk::FlowBox flowbox_itembox; 46 | Gtk::FlowBoxChild *selected_child; 47 | Gtk::ScrolledWindow scrolled_window; 48 | Gtk::ScrolledWindow scrolled_window_inner; 49 | Gtk::Box box_top; 50 | Glib::RefPtr gesture_drag; 51 | 52 | void on_search_changed(); 53 | bool on_key_press(const guint &keyval, const guint &keycode, const Gdk::ModifierType &state); 54 | 55 | bool on_filter(Gtk::FlowBoxChild* child); 56 | bool on_sort(Gtk::FlowBoxChild*, Gtk::FlowBoxChild*); 57 | 58 | void app_info_changed(GAppInfoMonitor* gappinfomonitor); 59 | void load_menu_item(const Glib::RefPtr &app_info); 60 | void run_menu_item(Gtk::FlowBoxChild* child, const bool &recent); 61 | 62 | void on_drag_start(const double &x, const double &y); 63 | void on_drag_update(const double &x, const double &y); 64 | void on_drag_stop(const double &x, const double &y); 65 | }; 66 | 67 | extern "C" { 68 | sysmenu *sysmenu_create(const std::map>&); 69 | void sysmenu_signal(sysmenu *window, int signal); 70 | } 71 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | #sysmenu { 2 | background: transparent; 3 | box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.5); 4 | margin: 10px; 5 | border-radius: 10px; 6 | } 7 | 8 | #sysmenu .entry_search { 9 | } 10 | 11 | #sysmenu .revealer_search { 12 | } 13 | 14 | #sysmenu .box_layout { 15 | background: alpha(@theme_base_color, 0.9); 16 | border: 1px solid @borders; 17 | border-radius: 10px; 18 | } 19 | 20 | #sysmenu .box_layout_inner { 21 | margin: 4px; 22 | } 23 | 24 | #sysmenu .flowbox_recent { 25 | border-bottom: 1px solid @borders; 26 | padding-bottom: 4px; 27 | } 28 | 29 | #sysmenu .dock { 30 | } 31 | #sysmenu .dock_item { 32 | } 33 | #sysmenu .dock_item:hover { 34 | } 35 | #sysmenu .dock_item:nth-child(1) { 36 | } 37 | #sysmenu .dock_item:nth-last-child(1) { 38 | } 39 | -------------------------------------------------------------------------------- /style_fullscreen.css: -------------------------------------------------------------------------------- 1 | #sysmenu { 2 | background: transparent; 3 | } 4 | 5 | #sysmenu .entry_search { 6 | } 7 | 8 | #sysmenu .revealer_search { 9 | background: alpha(@theme_base_color, 0.9); 10 | } 11 | 12 | #sysmenu .box_layout { 13 | } 14 | 15 | #sysmenu .box_layout_inner { 16 | background: alpha(@theme_base_color, 0.9); 17 | } 18 | 19 | #sysmenu .flowbox_recent { 20 | } 21 | 22 | #sysmenu .dock { 23 | background: alpha(@theme_base_color, 0.5); 24 | border: 1px solid @borders; 25 | border-radius: 25px; 26 | margin: 10px; 27 | box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.25); 28 | } 29 | #sysmenu .dock_item { 30 | border-radius: 20px; 31 | margin: 5px 2px 5px 2px; 32 | padding: 5px; 33 | } 34 | #sysmenu .dock_item:hover { 35 | background: alpha(CurrentColor, 0.1); 36 | } 37 | #sysmenu .dock_item:nth-child(1) { 38 | margin-left: 5px; 39 | } 40 | #sysmenu .dock_item:nth-last-child(1) { 41 | margin-right: 5px; 42 | } 43 | 44 | --------------------------------------------------------------------------------