From 457e3e847647d2ac2b57d5c2d6af79dec860c242 Mon Sep 17 00:00:00 2001 From: Kasper Seweryn Date: Fri, 10 Jan 2020 17:07:33 +0100 Subject: [PATCH 01/44] Initialize rust project --- .gitignore | 73 +-------- .idea/.gitignore | 2 + .idea/misc.xml | 1 - .idea/modules.xml | 2 +- .idea/waffy-rs.iml | 14 ++ .idea/waffy.iml | 2 - CMakeLists.txt | 28 ---- Cargo.lock | 6 + Cargo.toml | 9 ++ LICENSE | 21 --- README.md | 35 ---- assets/main.css | 57 ------- docs/project.md | 21 --- src/colors.c | 27 ---- src/colors.h | 12 -- src/config.c | 114 ------------- src/config.h | 23 --- src/desktop_entry.c | 167 ------------------- src/desktop_entry.h | 62 ------- src/filter.c | 54 ------- src/filter.h | 26 --- src/find_desktop.c | 116 -------------- src/find_desktop.h | 27 ---- src/main.c | 382 -------------------------------------------- src/main.rs | 3 + src/utils.c | 78 --------- src/utils.h | 41 ----- 27 files changed, 37 insertions(+), 1366 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/waffy-rs.iml delete mode 100644 .idea/waffy.iml delete mode 100644 CMakeLists.txt create mode 100644 Cargo.lock create mode 100644 Cargo.toml delete mode 100644 LICENSE delete mode 100644 README.md delete mode 100644 assets/main.css delete mode 100644 docs/project.md delete mode 100644 src/colors.c delete mode 100644 src/colors.h delete mode 100644 src/config.c delete mode 100644 src/config.h delete mode 100644 src/desktop_entry.c delete mode 100644 src/desktop_entry.h delete mode 100644 src/filter.c delete mode 100644 src/filter.h delete mode 100644 src/find_desktop.c delete mode 100644 src/find_desktop.h delete mode 100644 src/main.c create mode 100644 src/main.rs delete mode 100644 src/utils.c delete mode 100644 src/utils.h diff --git a/.gitignore b/.gitignore index 37be74a..53eaa21 100644 --- a/.gitignore +++ b/.gitignore @@ -1,71 +1,2 @@ -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm -# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 - -# User-specific stuff -.idea/**/workspace.xml -.idea/**/tasks.xml -.idea/**/usage.statistics.xml -.idea/**/dictionaries -.idea/**/shelf - -# Generated files -.idea/**/contentModel.xml - -# Sensitive or high-churn files -.idea/**/dataSources/ -.idea/**/dataSources.ids -.idea/**/dataSources.local.xml -.idea/**/sqlDataSources.xml -.idea/**/dynamic.xml -.idea/**/uiDesigner.xml -.idea/**/dbnavigator.xml - -# Gradle -.idea/**/gradle.xml -.idea/**/libraries - -# Gradle and Maven with auto-import -# When using Gradle or Maven with auto-import, you should exclude module files, -# since they will be recreated, and may cause churn. Uncomment if using -# auto-import. -# .idea/artifacts -# .idea/compiler.xml -# .idea/jarRepositories.xml -# .idea/modules.xml -# .idea/*.iml -# .idea/modules -# *.iml -# *.ipr - -# CMake -cmake-build-*/ - -# Mongo Explorer plugin -.idea/**/mongoSettings.xml - -# File-based project format -*.iws - -# IntelliJ -out/ - -# mpeltonen/sbt-idea plugin -.idea_modules/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Cursive Clojure plugin -.idea/replstate.xml - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties - -# Editor-based Rest Client -.idea/httpRequests - -# Android studio 3.1+ serialized cache file -.idea/caches/build_file_checksums.ser +/target +**/*.rs.bk diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..5c98b42 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,2 @@ +# Default ignored files +/workspace.xml \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 8822db8..28a804d 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,5 @@ - diff --git a/.idea/modules.xml b/.idea/modules.xml index 116be26..8f9b256 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,7 +2,7 @@ - + \ No newline at end of file diff --git a/.idea/waffy-rs.iml b/.idea/waffy-rs.iml new file mode 100644 index 0000000..b7b4242 --- /dev/null +++ b/.idea/waffy-rs.iml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/waffy.iml b/.idea/waffy.iml deleted file mode 100644 index f08604b..0000000 --- a/.idea/waffy.iml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index 5d5fc7f..0000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,28 +0,0 @@ -cmake_minimum_required(VERSION 3.16) -project(waffy C) - -set(CMAKE_C_STANDARD 11) -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "$ENV{HOME}/.local/share/${PROJECT_NAME}") - -file(COPY assets DESTINATION "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}") - -find_package(PkgConfig REQUIRED) -pkg_check_modules(GTK3 REQUIRED gtk+-wayland-3.0) -pkg_check_modules(GTK3_LAYER_SHELL REQUIRED gtk-layer-shell-0) - -pkg_search_module(WAYLAND wayland-client) -pkg_search_module(WAYLAND_SCANNER wayland-scanner) -pkg_search_module(WAYLAND_PROTOCOLS wayland-protocols) - - -include_directories(${GTK3_INCLUDE_DIRS}) -link_directories(${GTK3_LIBRARY_DIRS}) - -#include_directories(${WAYLAND_INCLUDE_DIR}) -#link_directories(${WAYLAND_LIBRARIES}) - -add_definitions(${GTK3_CFLAGS_OTHER}) -#add_definitions(${WAYLAND_DEFINITIONS}) - -add_executable(waffy src/main.c src/utils.h src/utils.c src/find_desktop.c src/find_desktop.h src/desktop_entry.c src/desktop_entry.h src/filter.c src/filter.h src/config.c src/config.h src/colors.c src/colors.h) -target_link_libraries(waffy ${GTK3_LAYER_SHELL_LIBRARIES} ${WAYLAND_LIBRARIES} ${GTK3_LIBRARIES}) diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..bf09174 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,6 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "waffy-rs" +version = "0.1.0" + diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..dad8f13 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "waffy-rs" +version = "0.1.0" +authors = ["Kasper Seweryn "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/LICENSE b/LICENSE deleted file mode 100644 index d587413..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2020 Kasper Seweryn - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md deleted file mode 100644 index 1034d50..0000000 --- a/README.md +++ /dev/null @@ -1,35 +0,0 @@ -# waffy -Wayland compatible, touch friendly application launcher - -![waffy-screenshot](https://i.imgur.com/iyEQVqo.png) - -## Features -- sway dedicated launcher -- Touch support -- Read all application locations -- Respect hidden apps -- Fuzzy find apps - -## Requirements -- [gtk-layer-shell](https://github.com/wmww/gtk-layer-shell) - A library to write GTK applications that use Layer Shell - -## Building -### Build with git and cmake -```shell script -git clone https://github.com/wvffle/waffy.git -cd waffy -cmake -Bcmake-build-release -H. -DCMAKE_BUILD_TYPE=Release -cmake --build cmake-build-release --target all -``` - -### Add to path -```shell script -echo "export PATH=$HOME/.local/share/waffy:$PATH" >> $HOME/.bash_profile -# or -sudo ln -s $HOME/.local/share/waffy/waffy /usr/local/bin/waffy -``` - - - -## Other information -- [Studies project info](/docs/project.md) diff --git a/assets/main.css b/assets/main.css deleted file mode 100644 index e21668c..0000000 --- a/assets/main.css +++ /dev/null @@ -1,57 +0,0 @@ - -label { - color: #fff; -} - -.textview-label { - padding-right: 0.5em; -} - -.textview-label, -textview { - font-size: 2em; - padding-top: 47px; - padding-bottom: 47px; -} - -.textview-label, -textview text { - color: #fff; -} - -* { - background-color: transparent; - border: none; -} - -#apps > button { - min-width: 270px; - transition: none; - border: none; -} - -/* default colors */ -window { - background: alpha(#000, .7); -} - -#apps > button { - background: alpha(#fff, .1); -} - -#apps > button.active { - background: alpha(#fff, .3); -} - -/* pywal colors */ -window.pywal { - background: alpha(@background, .7); -} - -window.pywal #apps > button { - background: @background; -} - -window.pywal #apps > button.active { - background: @color5; -} diff --git a/docs/project.md b/docs/project.md deleted file mode 100644 index 7f9569c..0000000 --- a/docs/project.md +++ /dev/null @@ -1,21 +0,0 @@ -### Project information - -Key | Value ---- | --- -Author | Kasper Seweryn -Album number | 107920 -Group | 8 (PS16) -Year | 2019 / 2020 - -### Implementation information -Everything is inside the `*.h` files - -- [Structures example](/src/desktop_entry.h#L14) -- [File operation example](/src/config.c#L18) -- [List example](/src/desktop_entry.h#L28) -- [Function example](/src/utils.c#L36) -- [Recursive function example](/src/config.c#L59) - -### Open source -Well it turned out that I've found a bug in sway which is my main wayland client -- https://github.com/swaywm/sway/issues/4840 \ No newline at end of file diff --git a/src/colors.c b/src/colors.c deleted file mode 100644 index acc4283..0000000 --- a/src/colors.c +++ /dev/null @@ -1,27 +0,0 @@ -// -// Created by waff on 1/8/20. -// - -#include "colors.h" -#include "config.h" - -char* get_css () { - open_config(); - - char* css_file = str_concat(get_user_home(), "/.config/waffy/main.css"); - - FILE* fp = fopen(css_file, "r"); - if (fp == NULL) { - fp = fopen(css_file, "w"); - fprintf(fp, "%s", read_file("../assets/main.css")); - } - - fclose(fp); - - if (config_wal == 1) { - char* wal_file = str_concat(get_user_home(), "/.cache/wal/colors-waybar.css"); - return str_concat(read_file(wal_file), read_file(css_file)); - } - - return read_file(css_file); -} \ No newline at end of file diff --git a/src/colors.h b/src/colors.h deleted file mode 100644 index 0ca5dd9..0000000 --- a/src/colors.h +++ /dev/null @@ -1,12 +0,0 @@ -// -// Created by waff on 1/8/20. -// - -#ifndef WAFFY_COLORS_H -#define WAFFY_COLORS_H - -#include "utils.h" - -char* get_css (); - -#endif //WAFFY_COLORS_H diff --git a/src/config.c b/src/config.c deleted file mode 100644 index 06e4c24..0000000 --- a/src/config.c +++ /dev/null @@ -1,114 +0,0 @@ -// -// Created by waff on 1/7/20. -// - -#include "config.h" - -char* config_dir = NULL; - -// Default config -long config_columns = 4; -long config_wal = 0; -char* config_prompt = "search:"; - -void get_long (char* value, long* res); -void get_str(char* value, char** res); -void get_bool(char* value, long* res); - -void open_config() { - if (config_dir != NULL) return; - config_dir = str_concat(get_user_home(), "/.config/waffy"); - mkdir(config_dir, 0755); - - char* file = str_concat(get_user_home(), "/.config/waffy/config"); - FILE* fp = fopen(file, "r"); - - if (fp == NULL) { - write_config(); - return; - } - - size_t line_number = 0; - while (!feof(fp)) { - char line[MAX_CONFIG_LINE_LENGTH]; - fscanf(fp, "%[^\n]\n", line); - line_number += 1; - - char* opline = str_trim(line); - if (opline[0] == '#') { - // pass - } else { - char* key = strtok(line, " "); - char* value = strtok(NULL, " "); - - if (strcmp(key, "columns") == 0) { - get_long(value, &config_columns); - } else if (strcmp(key, "prompt") == 0) { - get_str(value, &config_prompt); - } else if (strcmp(key, "wal_enable") == 0) { - get_bool(value, &config_wal); - } else { - fprintf(stderr, "Unknown config option '%s' on line %ld", key, line_number); - } - } - } - - fclose(fp); -} - -void get_str(char* value, char** res) { - if (value[0] != '"') { - char* buf = malloc(sizeof(char)* strlen(value)); - strcpy(buf, value); - *res = buf; - return; - } - - if (str_ends_with(value, "\"")) { - char* buf = malloc(sizeof(char)* (strlen(value) - 2)); - value[strlen(value) - 1] = '\0'; - strcpy(buf, value + 1); - *res = buf; - return; - } - - char* buf = strtok(NULL, " "); - get_str(str_concat(value, str_concat(" ", buf)), res); -} - -void get_bool(char* value, long* res) { - if (strcmp(value, "1") == 0 || strcmp(value, "yes") == 0 || strcmp(value, "on") == 0 || strcmp(value, "true") == 0) { - *res = 1; - return; - } - - *res = 0; -} - -void get_long(char* value, long* res) { - *res = strtol(value, NULL, 10); -} - -void write_config() { - char* file = str_concat(config_dir, "/config"); - FILE* fp = fopen(file, "w"); - - fprintf(fp, "# ___ ___ #\n"); - fprintf(fp, "# /'___\\ /'___\\ #\n"); - fprintf(fp, "# __ __ __ __ /\\ \\__//\\ \\__/ __ __ #\n"); - fprintf(fp, "# /\\ \\/\\ \\/\\ \\ /'__`\\ \\ \\ ,__\\ \\ ,__\\/\\ \\/\\ \\ #\n"); - fprintf(fp, "# \\ \\ \\_/ \\_/ \\/\\ \\L\\.\\_\\ \\ \\_/\\ \\ \\_/\\ \\ \\_\\ \\ #\n"); - fprintf(fp, "# \\ \\___x___/'\\ \\__/.\\_\\\\ \\_\\ \\ \\_\\ \\/`____ \\ #\n"); - fprintf(fp, "# \\/__//__/ \\/__/\\/_/ \\/_/ \\/_/ `/___/> \\ #\n"); - fprintf(fp, "# /\\___/ #\n"); - fprintf(fp, "# config \\/__/ #\n\n"); - fprintf(fp, "# Number of columns in launcher\n"); - fprintf(fp, "columns %ld\n\n", config_columns); - fprintf(fp, "# Enable pywal colors\n"); - fprintf(fp, "wal_enable false\n\n"); - fprintf(fp, "# Run prompt text\n"); - fprintf(fp, "prompt \"%s\"\n", config_prompt); - - fclose(fp); -} - diff --git a/src/config.h b/src/config.h deleted file mode 100644 index 5245cb0..0000000 --- a/src/config.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// Created by waff on 1/7/20. -// - -#ifndef WAFFY_CONFIG_H -#define WAFFY_CONFIG_H - -#define MAX_CONFIG_LINE_LENGTH 256 - -#include -#include "utils.h" - -long config_columns; -long config_wal; -char* config_prompt; - -// Write current config to the file -void write_config(); - -// Read config from the file -void open_config (); - -#endif //WAFFY_CONFIG_H diff --git a/src/desktop_entry.c b/src/desktop_entry.c deleted file mode 100644 index 425d30c..0000000 --- a/src/desktop_entry.c +++ /dev/null @@ -1,167 +0,0 @@ -// -// Created by waff on 12/20/19. -// - -#include "desktop_entry.h" - -size_t deb_push(desktop_entry_batch* batch, desktop_entry* entry) { - desktop_entry_batch_node* node = malloc(sizeof(desktop_entry_batch_node)); - node->entry = entry; - node->next = NULL; - node->prev = NULL; - - if (batch->length == 0) { - batch->first = node; - batch->last = node; - return ++batch->length; - } - - batch->last->next = node; - node->prev = batch->last; - batch->last = node; - - return ++batch->length; -} - -size_t deb_unshift(desktop_entry_batch* batch, desktop_entry* entry) { - desktop_entry_batch_node* node = malloc(sizeof(desktop_entry_batch_node)); - node->entry = entry; - node->next = NULL; - node->prev = NULL; - - if (batch->length == 0) { - batch->first = node; - batch->last = node; - return ++batch->length; - } - - node->next = batch->first; - batch->first->prev = node; - batch->first = node; - - return ++batch->length; -} - -desktop_entry* deb_pop(desktop_entry_batch* batch) { - if (batch->length == 0) return NULL; - - desktop_entry_batch_node* node = batch->last; - batch->last = node->prev; - node->prev->next = NULL; - node->prev = NULL; - - if (--batch->length == 0) { - batch->first = NULL; - } - - desktop_entry* entry = node->entry; - - free(node); - return entry; -} - -desktop_entry* deb_shift(desktop_entry_batch* batch) { - if (batch->length == 0) return NULL; - - desktop_entry_batch_node* node = batch->first; - batch->first = node->next; - node->next->prev = NULL; - node->next = NULL; - - if (--batch->length == 0) { - batch->last = NULL; - } - - desktop_entry* entry = node->entry; - - free(node); - return entry; -} - -void deb_concat(desktop_entry_batch* target, desktop_entry_batch* source) { - if (target == NULL) { - fprintf(stderr, "[ERROR] target is NULL"); - return; - } - - if (source == NULL) { - return; - }; - - if (source->length == 0) { - deb_destructor(source); - return; - } - - if (target->length == 0) { - target->first = source->first; - target->last = source->last; - target->length = source->length; - - source->first = NULL; - source->last = NULL; - deb_destructor(source); - return; - } - - source->first->prev = target->last; - target->last->next = source->first; - target->last = source->last; - - target->length += source->length; - - source->first = NULL; - source->last = NULL; - deb_destructor(source); -} - -desktop_entry_batch* deb_constructor() { - desktop_entry_batch* batch = malloc(sizeof(desktop_entry_batch)); - - batch->first = NULL; - batch->last = NULL; - batch->length = 0; - - return batch; -} - -void deb_destructor(desktop_entry_batch* batch) { - if (batch->length) { - desktop_entry_batch_node* curr = batch->first; - - while (curr != NULL) { - free(curr->entry); - - desktop_entry_batch_node* next = curr->next; - free(curr); - - curr = next; - } - - } - - free(batch); -} - -void deb_destructor_no_entry(desktop_entry_batch* batch) { - if (batch->length) { - desktop_entry_batch_node* curr = batch->first; - - while (curr != NULL) { - desktop_entry_batch_node* next = curr->next; - free(curr); - - curr = next; - } - - } - - free(batch); -} - -desktop_entry* de_constructor(const char* gtk_launch_name) { - desktop_entry* entry = malloc(sizeof(desktop_entry)); - strcpy(entry->gtk_launch_name, gtk_launch_name); - - return entry; -} diff --git a/src/desktop_entry.h b/src/desktop_entry.h deleted file mode 100644 index be88004..0000000 --- a/src/desktop_entry.h +++ /dev/null @@ -1,62 +0,0 @@ -// -// Created by waff on 12/20/19. -// - -#ifndef WAFFY_DESKTOP_ENTRY_H -#define WAFFY_DESKTOP_ENTRY_H - -#include -#include -#include -#include - -// Desktop entry structure -typedef struct desktop_entry { - char gtk_launch_name[FILENAME_MAX]; // Used to find GtkAppInfo - char name[32]; // App display name - char icon[PATH_MAX]; // App icon -} desktop_entry; - -// Two-way list node -typedef struct desktop_entry_batch_node { - struct desktop_entry* entry; // Desktop entry - struct desktop_entry_batch_node* next; // Next node - struct desktop_entry_batch_node* prev; // Previous node -} desktop_entry_batch_node; - -// Two-way list starter -typedef struct desktop_entry_batch { - size_t length; // List length - struct desktop_entry_batch_node* first; // First node on the list - struct desktop_entry_batch_node* last; // Last node on the list -} desktop_entry_batch; - -// Helper function to create new desktop entry and allocate memory -desktop_entry* de_constructor (const char* gtk_launch_name); - -// Helper function to create new list and allocate memory -desktop_entry_batch* deb_constructor (); - -// Helper function to clean the list -void deb_destructor (desktop_entry_batch* batch); - -// Helper function to clean the list without cleaning entries -void deb_destructor_no_entry(desktop_entry_batch* batch); - - -// Helper function to add entry to the end of a list -size_t deb_push (desktop_entry_batch* batch, desktop_entry* entry); - -// Helper function to add entry to the beginning of a list -size_t deb_unshift (desktop_entry_batch* batch, desktop_entry* entry); - -// Helper function to remove entry from the end of a list -desktop_entry* deb_pop (desktop_entry_batch* batch); - -// Helper function to remove entry from the beginning of a list -desktop_entry* deb_shift (desktop_entry_batch* batch); - -// Helper function to join source to target list. Invalidates source list -void deb_concat (desktop_entry_batch* target, desktop_entry_batch* source); - -#endif //WAFFY_DESKTOP_ENTRY_H diff --git a/src/filter.c b/src/filter.c deleted file mode 100644 index 8f6beb1..0000000 --- a/src/filter.c +++ /dev/null @@ -1,54 +0,0 @@ -// -// Created by waff on 12/22/19. -// - -#include "filter.h" - -desktop_entry_batch* filter_apps(desktop_entry_batch* hay, const char* needle, enum filter_mode mode) { - switch (mode) { - case CASE_INSENSITIVE: return filter_case_insensitive(hay, needle); - case FUZZY: return filter_fuzzy(hay, needle); - default: return NULL; - } -} - -desktop_entry_batch* filter_fuzzy(desktop_entry_batch* hay, const char* needle) { - if (hay == NULL) return NULL; - if (needle == NULL || !strlen(needle)) return hay; - - desktop_entry_batch_node* curr = hay->first; - if (curr == NULL) return NULL; - - desktop_entry_batch* res = deb_constructor(); - while (curr != NULL) { - if (str_fuzzy_match(curr->entry->name, needle)) { - deb_push(res, curr->entry); - } - - curr = curr->next; - } - - if (res->length == 0) return NULL; - return res; -} - -desktop_entry_batch* filter_case_insensitive(desktop_entry_batch* hay, const char* needle) { - if (hay == NULL) return NULL; - if (needle == NULL || !strlen(needle)) return hay; - - desktop_entry_batch_node* curr = hay->first; - if (curr == NULL) return NULL; - - desktop_entry_batch* res = deb_constructor(); - while (curr != NULL) { - - if (strcasestr(curr->entry->name, needle) != NULL) { - deb_push(res, curr->entry); - } - - curr = curr->next; - } - - if (res->length == 0) return NULL; - return res; -} diff --git a/src/filter.h b/src/filter.h deleted file mode 100644 index aba2cf0..0000000 --- a/src/filter.h +++ /dev/null @@ -1,26 +0,0 @@ -// -// Created by waff on 12/22/19. -// - -#ifndef WAFFY_FILTER_H -#define WAFFY_FILTER_H - -#define _GNU_SOURCE -#include "desktop_entry.h" -#include "utils.h" - -enum filter_mode { - CASE_INSENSITIVE, - FUZZY -}; - -// Filter apps by needle using provided mode -desktop_entry_batch* filter_apps (desktop_entry_batch* hay, const char* needle, enum filter_mode mode); - -// Fuzzy filter apps by needle -desktop_entry_batch* filter_fuzzy (desktop_entry_batch* hay, const char* needle); - -// Case insensitive filter apps by needle -desktop_entry_batch* filter_case_insensitive (desktop_entry_batch* hay, const char* needle); - -#endif //WAFFY_FILTER_H diff --git a/src/find_desktop.c b/src/find_desktop.c deleted file mode 100644 index 54bd239..0000000 --- a/src/find_desktop.c +++ /dev/null @@ -1,116 +0,0 @@ -// -// Created by waff on 12/20/19. -// - -#include "find_desktop.h" - -char* get_user_desktop_files_directory() { - char* homedir = get_user_home(); - char* local_desktop_directory = "/.local/share/applications/"; - return str_concat(homedir, local_desktop_directory); -} - -desktop_entry* read_desktop_file(const char* file_path, const char* gtk_launch_name) { - FILE* fp = fopen(file_path, "r"); - if (!fp) return NULL; - - desktop_entry* entry = de_constructor(gtk_launch_name); - - char in_entry_section = 0; - char line[PATH_MAX]; - while (!feof(fp)) { - fscanf(fp, "%[^\n]\n", line); - - // Skip comments and empty lines - if (line[0] == '\0' || line[0] == '#') continue; - - if (in_entry_section) { - // Another section occured, stop parsing - if (line[0] == '[') break; - - char* key = strtok(line, "="); - char* value = strtok(NULL, "="); - - // Skip malformed field - if (!value) continue; - - if (!strcmp(key, "NoDisplay") && !strcmp(value, "true")) { - free(entry); - return NULL; - } - - if (!strcmp(key, "Name")) { - strcpy(entry->name, value); - continue; - } - - if (!strcmp(key, "Icon")) { - strcpy(entry->icon, value); - continue; - } - - } else if (!strcmp(line, "[Desktop Entry]")) { - in_entry_section = 1; - } - } - - fclose(fp); - return entry; -} - -desktop_entry_batch* find_desktop_files(const char* directory) { - DIR* dir_handle = opendir(directory); - - // Skip non-existant directory - if (dir_handle == NULL) { - return NULL; - } - - struct dirent* dp; - desktop_entry_batch* batch = deb_constructor(); - while ((dp = readdir(dir_handle)) != NULL) { - if (!str_ends_with(dp->d_name, ".desktop")) continue; - - char* file_path = str_concat(directory, dp->d_name); - - // Remove .desktop from end -// size_t len = strlen(dp->d_name) - 8; -// char gtk_launch_name[len + 1]; -// strncpy(gtk_launch_name, dp->d_name, len); -// gtk_launch_name[len] = '\0'; - - desktop_entry* entry = read_desktop_file(file_path, dp->d_name); - -// desktop_entry* entry = read_desktop_file(file_path, gtk_launch_name); - if (entry != NULL) deb_push(batch, entry); - - free(file_path); - } - - closedir(dir_handle); - return batch; -} - -desktop_entry_batch* find_all_desktop_files() { - char* user_dir = get_user_desktop_files_directory(); - char* desktop_entry_dirs[DESKTOP_ENTRY_DIRS_NUM] = { - "/usr/share/applications/", - "/usr/local/share/applications/", - user_dir - }; - - - desktop_entry_batch* all_entries = deb_constructor(); - - for (size_t i = 0; i < DESKTOP_ENTRY_DIRS_NUM; ++i) { - desktop_entry_batch* entries = find_desktop_files(desktop_entry_dirs[i]); - if (entries == NULL) continue; - - deb_concat(all_entries, entries); - } - - free(user_dir); - - return all_entries; -} - diff --git a/src/find_desktop.h b/src/find_desktop.h deleted file mode 100644 index e48f6d3..0000000 --- a/src/find_desktop.h +++ /dev/null @@ -1,27 +0,0 @@ -// -// Created by waff on 12/20/19. -// - -#ifndef WAFFY_FIND_DESKTOP_H -#define WAFFY_FIND_DESKTOP_H - -#include -#include "utils.h" -#include "desktop_entry.h" - -// 2 system directories and 1 user directory -#define DESKTOP_ENTRY_DIRS_NUM 3 - -// Returns ~/.local/share/applications -char* get_user_desktop_files_directory (); - -// Opens a desktop file and creates a desktop entry -desktop_entry* read_desktop_file (const char* file_path, const char* gtk_launch_name); - -// Reads all desktop files in a directory -desktop_entry_batch* find_desktop_files (const char* directory); - -// Reads all desktop files in all of the directories -desktop_entry_batch* find_all_desktop_files (); - -#endif //WAFFY_FIND_DESKTOP_H diff --git a/src/main.c b/src/main.c deleted file mode 100644 index cc4db6f..0000000 --- a/src/main.c +++ /dev/null @@ -1,382 +0,0 @@ -#include - -#include -#include -#include -#include - -#include "find_desktop.h" -#include "filter.h" -#include "config.h" -#include "colors.h" - -#define ICON_SIZE 48 -#define MAX_CHARS 16 -#define COL_PADDING 17 - -desktop_entry_batch* all_desktop_entries = NULL; -desktop_entry_batch* filtered = NULL; -GtkGrid* app_grid = NULL; -GtkGrid* fav_grid = NULL; -GtkWidget* window; -GtkWidget* search_input; -uint current_items = 0; -int current_item = 0; -int current_item_kb = 0; - -// HACK: Get over with the fact that after changing CSS class, button gets rehovered -int last_current_item = -1; -int last_current_item_kb = 0; - -GdkCursor* arrow; -GdkCursor* pointer; - -int get_monitor_width () { - GdkWindow* gdk_window = gtk_widget_get_window(window); - GdkDisplay* display = gtk_widget_get_display(window); - GdkMonitor* monitor = gdk_display_get_monitor_at_window(display, gdk_window); - GdkRectangle rect; - gdk_monitor_get_geometry(monitor, &rect); - return rect.width; -} - -void add_class (GtkWidget* widget, const char* class_name) { - GtkStyleContext* context = gtk_widget_get_style_context(widget); - gtk_style_context_add_class(context, class_name); -} - -void window_destroy (GtkWidget* widget, gpointer* data) { - deb_destructor(all_desktop_entries); - gtk_main_quit(); -} - -void update_current () { - int idx = -1; - - if (current_item != last_current_item) { - last_current_item = current_item; - last_current_item_kb = current_item; - current_item_kb = current_item; - idx = current_item; - } else if (current_item_kb != last_current_item_kb) { - last_current_item_kb = current_item_kb; - idx = current_item_kb; - } - - if (idx == -1) return; - for (size_t i = 0; i < current_items; ++i) { - GtkWidget* app = gtk_grid_get_child_at(app_grid, i % config_columns, i / config_columns); - if (app == NULL) continue; - GtkStyleContext* ctx = gtk_widget_get_style_context(app); - - if (gtk_style_context_has_class(ctx, "active") == TRUE) { - gtk_style_context_remove_class(ctx, "active"); - } - - if (i == idx) { - gtk_style_context_add_class(ctx, "active"); - } - } -} - -void window_enter (GtkWidget* widget, GdkEvent* event, int* data) { - gtk_layer_set_keyboard_interactivity(GTK_WINDOW(window), TRUE); -} - -void window_leave (GtkWidget* widget, GdkEventCrossing* event, int* data) { - // Check if we leave the window itself - if (event->detail != GDK_NOTIFY_NONLINEAR) return; - gtk_layer_set_keyboard_interactivity(GTK_WINDOW(window), FALSE); -} - -void app_enter (GtkWidget* widget, GdkEvent* event, int* data) { - gdk_window_set_cursor(gtk_widget_get_window(widget), pointer); - current_item =* data; - update_current(); -} - -void app_leave (GtkWidget* widget, GdkEvent* event, int* data) { - gdk_window_set_cursor(gtk_widget_get_window(widget), arrow); - current_item =* data; - update_current(); -} - -void app_clicked (GtkButton* button, desktop_entry* entry) { - printf("run: %s\n", entry->name); - GDesktopAppInfo* info = g_desktop_app_info_new(entry->gtk_launch_name); - - if (info != NULL) { - g_autoptr(GError) error = NULL; - g_app_info_launch(G_APP_INFO(info), NULL, NULL, &error); - - if (filtered != all_desktop_entries && filtered != NULL) { - deb_destructor_no_entry(filtered); - } - - if (error != NULL) { - window_destroy(NULL, NULL); - g_error("Could not run program '%s': %s", entry->name, error->message); - } - - window_destroy(NULL, NULL); - exit(EXIT_SUCCESS); - } - - if (filtered != all_desktop_entries && filtered != NULL) { - deb_destructor_no_entry(filtered); - } - - window_destroy(NULL, NULL); - g_error("Could not run program '%s': Cannot fetch AppInfo", entry->name); -} - -void update_apps (desktop_entry_batch* apps) { - // Reset grid - for (size_t i = 0; i < current_items; ++i) { - GtkWidget* app = gtk_grid_get_child_at(app_grid, i % config_columns, i / config_columns); - gtk_container_remove(GTK_CONTAINER(app_grid), app); - } - - int old_current_items = current_items; - if (apps == NULL || (current_items = apps->length) == 0) { - current_items = 0; - return; - } - - if (old_current_items != current_items) { - current_item = 0; - current_item_kb = 0; - update_current(); - } - - int n = 0; - desktop_entry_batch_node* curr = apps->first; - while (curr != NULL) { - GtkIconTheme* theme = gtk_icon_theme_get_default(); - GtkWidget* icon = NULL; - GdkPixbuf* pixbuf = NULL; - - if (is_path(curr->entry->icon)) { - GtkWidget* image = gtk_image_new_from_file(curr->entry->icon); - pixbuf = gtk_image_get_pixbuf(GTK_IMAGE(image)); -// gtk_widget_destroy(image); - } else { - const gchar** icons = g_themed_icon_get_names((GThemedIcon* ) g_themed_icon_new(curr->entry->icon)); - GtkIconInfo* info = gtk_icon_theme_choose_icon(theme, icons, ICON_SIZE, GTK_ICON_LOOKUP_FORCE_SIZE); - - if (info != NULL) { - GtkWidget* image = gtk_image_new_from_file(gtk_icon_info_get_filename(info)); - pixbuf = gtk_image_get_pixbuf(GTK_IMAGE(image)); - } - - } - - if (GDK_IS_PIXBUF(pixbuf)) { - pixbuf = gdk_pixbuf_scale_simple(pixbuf, ICON_SIZE, ICON_SIZE, GDK_INTERP_TILES); - icon = gtk_image_new_from_pixbuf(pixbuf); - } - - if (icon == NULL) { - icon = gtk_image_new_from_icon_name("application-x-executable", GTK_ICON_SIZE_DIALOG); -// pixbuf = gtk_image_get_pixbuf(GTK_IMAGE(image)); -// pixbuf = gdk_pixbuf_scale_simple(pixbuf, ICON_SIZE, ICON_SIZE, GDK_INTERP_TILES); -// icon = gtk_image_new_from_pixbuf(pixbuf); - } - - GtkWidget* app = gtk_button_new(); - GtkWidget* app_content = gtk_grid_new(); - GtkWidget* label = gtk_label_new(curr->entry->name); - - - gtk_grid_insert_column(GTK_GRID(app_content), 0); - gtk_grid_insert_column(GTK_GRID(app_content), 1); - - gtk_grid_set_column_spacing(GTK_GRID(app_content), COL_PADDING); - - gtk_grid_attach(GTK_GRID(app_content), icon, 0, 0, 1, 1); - gtk_grid_attach(GTK_GRID(app_content), label, 1, 0, 1, 1); - - - gtk_label_set_max_width_chars(GTK_LABEL(label), MAX_CHARS); - gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_END); - - gtk_container_add(GTK_CONTAINER(app), app_content); - - uint col = n % config_columns; - uint row = n / config_columns; - gtk_grid_attach(app_grid, app, col, row, 1, 1); - gtk_widget_show_all(app); - - int* pos = malloc(sizeof(int)); - memcpy(pos, &n, sizeof(int)); - g_signal_connect(app, "clicked", G_CALLBACK(app_clicked), curr->entry); - g_signal_connect(app, "enter-notify-event", G_CALLBACK(app_enter), pos); - g_signal_connect(app, "leave-notify-event", G_CALLBACK(app_leave), pos); - - curr = curr->next; - n += 1; - } - - update_current(); -} - -gboolean key_pressed (GtkWidget* window, GdkEventKey* event, gpointer data) { - if (event->keyval == GDK_KEY_Escape) { - window_destroy(window, data); - return TRUE; - } - - if (event->keyval == GDK_KEY_Return) { - desktop_entry_batch* apps = filtered; - if (apps == NULL) apps = all_desktop_entries; - - desktop_entry_batch_node* curr = apps->first; - - int idx = (current_item != last_current_item) - ? current_item - : current_item_kb; - - while (curr != NULL) { - if (idx-- == 0) break; - curr = curr->next; - } - - app_clicked(NULL, curr->entry); - return TRUE; - } - - if (event->keyval == GDK_KEY_Left) { - if (current_item_kb == 0) return TRUE; - current_item_kb -= 1; -// update_current(); - return TRUE; - } - - if (event->keyval == GDK_KEY_Right) { - if (current_item_kb == current_items - 1) return TRUE; - current_item_kb += 1; -// update_current(); - return TRUE; - } - - if (event->keyval == GDK_KEY_Up) { - current_item_kb -= config_columns; - if (current_item_kb < 0) current_item_kb += config_columns; - -// update_current(); - return TRUE; - } - - if (event->keyval == GDK_KEY_Down) { - current_item_kb += config_columns; - if (current_item_kb > current_items - 1) current_item_kb -= config_columns; - -// update_current(); - return TRUE; - } - - return FALSE; -} -gboolean key_released (GtkWidget* window, GdkEventKey* event, gpointer data) { - // Normal key input - GtkTextBuffer* buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(search_input)); - GtkTextIter start; - GtkTextIter end; - gtk_text_buffer_get_bounds (buf, &start, &end); - - char* needle = gtk_text_buffer_get_text(buf, &start, &end, FALSE); - filtered = filter_apps(all_desktop_entries, needle, FUZZY); - update_apps(filtered); -} - -int main (int argc, char* argv[]) { - open_config(); - all_desktop_entries = find_all_desktop_files(); - - gtk_init(&argc, &argv); - - window = gtk_window_new(GTK_WINDOW_TOPLEVEL); - gtk_layer_init_for_window(GTK_WINDOW(window)); - gtk_layer_set_layer (GTK_WINDOW(window), GTK_LAYER_SHELL_LAYER_TOP); -// gtk_layer_set_keyboard_interactivity(GTK_WINDOW(window), TRUE); - - // HACK: Set window fullscreen - gtk_layer_set_anchor (GTK_WINDOW(window), 0, TRUE); - gtk_layer_set_anchor (GTK_WINDOW(window), 1, TRUE); - gtk_layer_set_anchor (GTK_WINDOW(window), 2, TRUE); - gtk_layer_set_anchor (GTK_WINDOW(window), 3, TRUE); - - gtk_widget_realize(window); - - // Set window options - gtk_window_set_resizable(GTK_WINDOW(window), FALSE); - gtk_window_set_decorated(GTK_WINDOW(window), FALSE); - - // Add event handlers - g_signal_connect(window, "destroy", G_CALLBACK(window_destroy), NULL); - g_signal_connect(window, "key_press_event", G_CALLBACK(key_pressed), NULL); - g_signal_connect(window, "key_release_event", G_CALLBACK(key_released), NULL); - g_signal_connect(window, "enter-notify-event", G_CALLBACK(window_enter), NULL); - g_signal_connect(window, "leave-notify-event", G_CALLBACK(window_leave), NULL); - - // Main layout - GtkBox* layout = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 0)); - gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(layout)); - - // Add search field - GtkBox* search_box = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0)); - gtk_box_pack_start(layout, GTK_WIDGET(search_box), FALSE, FALSE, 0); - - // -- Add spacing - int grid_width = 270* config_columns + (config_columns + 2)* COL_PADDING; - GtkWidget* spacer = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); - gtk_widget_set_size_request(spacer, (get_monitor_width() - grid_width) / 2, 1); - gtk_box_pack_start(search_box, spacer, FALSE, FALSE, 0); - - // -- Add label - GtkWidget* search_label = gtk_label_new(NULL); - gtk_label_set_markup(GTK_LABEL(search_label), config_prompt); - gtk_box_pack_start(search_box, search_label, FALSE, FALSE, 0); - - add_class(search_label, "textview-label"); - - // -- Add input - search_input = gtk_text_view_new(); - gtk_widget_set_name(search_input, "search"); - gtk_box_pack_start(search_box, search_input, TRUE, TRUE, 0); - - // Apps grid layout - app_grid = (GtkGrid* ) gtk_grid_new(); - gtk_box_pack_start(layout, GTK_WIDGET(app_grid), TRUE, TRUE, 0); - for (size_t i = 0; i < config_columns; ++i) { - gtk_grid_insert_column(app_grid, i); - } - gtk_grid_set_column_spacing(app_grid, COL_PADDING); - gtk_grid_set_row_spacing(app_grid, COL_PADDING); - gtk_widget_set_name(GTK_WIDGET(app_grid), "apps"); - - gtk_widget_set_halign(app_grid, GTK_ALIGN_CENTER); - - update_apps(all_desktop_entries); - - GtkCssProvider* css_provider = gtk_css_provider_new(); - - if (config_wal == 1) { - add_class(window, "pywal"); - } - - char* css = get_css(); - if (gtk_css_provider_load_from_data(css_provider, css, strlen(css), NULL)) { - gtk_style_context_add_provider_for_screen(gtk_widget_get_screen(window), - GTK_STYLE_PROVIDER(css_provider), - GTK_STYLE_PROVIDER_PRIORITY_USER); - } - - GdkDisplay* display = gtk_widget_get_display(window); - arrow = GDK_CURSOR(gdk_cursor_new_from_name(display, "default")); - pointer = GDK_CURSOR(gdk_cursor_new_from_name(display, "pointer")); - - gtk_window_set_title(GTK_WINDOW(window), "Waffy"); - gtk_widget_show_all(window); - gtk_main(); -} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..e7a11a9 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/src/utils.c b/src/utils.c deleted file mode 100644 index 8d84650..0000000 --- a/src/utils.c +++ /dev/null @@ -1,78 +0,0 @@ -// -// Created by waff on 12/20/19. -// - -#include "utils.h" - -char* read_file(const char* path) { - FILE* fp = fopen(path, "r"); - if (fp == NULL) return ""; - - size_t len; - char* res = NULL; - ssize_t bytes_read = getdelim(&res, &len, '\0', fp); - fclose(fp); - - if (bytes_read == -1) return ""; - return res; -} - -int str_starts_with (const char* hay, const char* needle) { - return strncmp(needle, hay, strlen(needle)) == 0; -} - -char* str_trim (const char* str) { - char* buf = malloc(strlen(str)* sizeof(char)); - sscanf(str, "%s", buf); - return buf; -} - -int str_ends_with (const char* hay, const char* needle) { - size_t hay_len = strlen(hay); - size_t needle_len = strlen(needle); - return hay_len >= needle_len && !strcmp(hay + (hay_len - needle_len), needle); -} - -char* str_concat (const char* string1, const char* string2) { - // NOTE: Additional 1 for \0 at the end - size_t length = 1 + strlen(string1) + strlen(string2); - - char* sum = malloc(length* sizeof(char)); - sprintf(sum, "%s%s", string1, string2); - - return sum; -} - -char* get_user_home () { - char* homedir; - if ((homedir = getenv("HOME")) == NULL) { - homedir = getpwuid(getuid())->pw_dir; - } - - return homedir; -} - -int str_fuzzy_match(const char* hay, const char* needle) { - char tokenized_needle[strlen(needle)]; - strcpy(tokenized_needle, needle); - - char* token = strtok(tokenized_needle, " "); - char* str = (char*) hay; - - while (token != NULL) { - if ((str = strcasestr(str, token)) == NULL) return 0; - token = strtok(NULL, " "); - } - - return 1; -} - -uint is_path(const char* path) { - FILE* fp; - if ((fp = fopen(path, "r")) != NULL) { - fclose(fp); - return 1; - } - - return 0; -} diff --git a/src/utils.h b/src/utils.h deleted file mode 100644 index 3f8f53e..0000000 --- a/src/utils.h +++ /dev/null @@ -1,41 +0,0 @@ -// -// Created by waff on 12/20/19. -// - -#ifndef WAFFY_UTILS_H -#define WAFFY_UTILS_H -#define _GNU_SOURCE - -#include -#include -#include -#include -#include -#include -#include - -// Read whole file into a string -char* read_file (const char* path); - -// Removes whitespaces from start and end of string -char* str_trim (const char* str); - -// Checks if hay starts with needle -int str_starts_with (const char* hay, const char* needle); - -// Checks if hay ends with needle -int str_ends_with (const char* hay, const char* needle); - -// Checks if hay contains needle substrings (substrings are determined by spaces in needle) -int str_fuzzy_match (const char* hay, const char* needle); - -// Joins two strings together -char* str_concat (const char* string1, const char* string2); - -// Returns relative path to user home -char* get_user_home (); - -// Check if path is real path/exists -uint is_path (const char* path); - -#endif //WAFFY_UTILS_H From 7fd016ce1c158e300876617aa802e4b72b450edc Mon Sep 17 00:00:00 2001 From: insertt Date: Fri, 10 Jan 2020 17:46:18 +0100 Subject: [PATCH 02/44] Init --- .gitignore | 1 + Cargo.lock | 536 +++++++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 3 + src/main.rs | 109 ++++++++++- 4 files changed, 647 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 53eaa21..577f287 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target **/*.rs.bk +.idea/ \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index bf09174..d098c7b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,540 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] -name = "waffy-rs" +name = "anyhow" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7825f6833612eb2414095684fcf6c635becf3ce97fe48cf6421321e93bfbd53c" + +[[package]] +name = "atk" +version = "0.8.0" +source = "git+https://github.com/gtk-rs/atk#b56689dea48ac579b53041d89544ff18a72ec08b" +dependencies = [ + "atk-sys", + "bitflags", + "glib", + "glib-sys", + "gobject-sys", + "libc", +] + +[[package]] +name = "atk-sys" +version = "0.9.1" +source = "git+https://github.com/gtk-rs/sys#bac293d7ce7547bd238a7f5e3e130c4f66c8df3d" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "pkg-config", +] + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "cairo-rs" +version = "0.8.0" +source = "git+https://github.com/gtk-rs/cairo#b555469778becf3ae547dbf08f2ddc8ffd1c0617" +dependencies = [ + "bitflags", + "cairo-sys-rs", + "glib", + "glib-sys", + "gobject-sys", + "libc", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.9.2" +source = "git+https://github.com/gtk-rs/cairo#b555469778becf3ae547dbf08f2ddc8ffd1c0617" +dependencies = [ + "glib-sys", + "libc", + "pkg-config", +] + +[[package]] +name = "cc" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" + +[[package]] +name = "either" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" + +[[package]] +name = "futures-channel" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcae98ca17d102fd8a3603727b9259fcf7fa4239b603d2142926189bc8999b86" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79564c427afefab1dfb3298535b21eda083ef7935b4f0ecbfcb121f0aec10866" + +[[package]] +name = "futures-executor" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e274736563f686a837a0568b478bdabfeaec2dca794b5649b04e2fe1627c231" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e676577d229e70952ab25f3945795ba5b16d63ca794ca9d2c860e5595d20b5ff" + +[[package]] +name = "futures-macro" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e7c56c15537adb4f76d0b7a76ad131cb4d2f4f32d3b0bcabcbe1c7c5e87764" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-task" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bae52d6b29cf440e298856fec3965ee6fa71b06aa7495178615953fd669e5f9" + +[[package]] +name = "futures-util" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0d66274fb76985d3c62c886d1da7ac4c0903a8c9f754e8fe0f35a6a6cc39e76" +dependencies = [ + "futures-core", + "futures-macro", + "futures-task", + "pin-utils", + "proc-macro-hack", + "proc-macro-nested", + "slab", +] + +[[package]] +name = "gdk" +version = "0.12.0" +source = "git+https://github.com/gtk-rs/gdk#63d16649441dba756936c993761d6917c077e10c" +dependencies = [ + "bitflags", + "cairo-rs", + "cairo-sys-rs", + "gdk-pixbuf", + "gdk-sys", + "gio", + "gio-sys", + "glib", + "glib-sys", + "gobject-sys", + "libc", + "pango", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.8.0" +source = "git+https://github.com/gtk-rs/gdk-pixbuf#060b57509cc106904ce1d9ff142bbfcde224a463" +dependencies = [ + "gdk-pixbuf-sys", + "gio", + "gio-sys", + "glib", + "glib-sys", + "gobject-sys", + "libc", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.9.1" +source = "git+https://github.com/gtk-rs/sys#bac293d7ce7547bd238a7f5e3e130c4f66c8df3d" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pkg-config", +] + +[[package]] +name = "gdk-sys" +version = "0.9.1" +source = "git+https://github.com/gtk-rs/sys#bac293d7ce7547bd238a7f5e3e130c4f66c8df3d" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "pkg-config", +] + +[[package]] +name = "gio" +version = "0.8.0" +source = "git+https://github.com/gtk-rs/gio#06558414d906c77c540d8b3caf44a8409f9730f8" +dependencies = [ + "bitflags", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys", + "glib", + "glib-sys", + "gobject-sys", + "libc", + "once_cell", +] + +[[package]] +name = "gio-sys" +version = "0.9.1" +source = "git+https://github.com/gtk-rs/sys#bac293d7ce7547bd238a7f5e3e130c4f66c8df3d" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "pkg-config", +] + +[[package]] +name = "glib" +version = "0.9.1" +source = "git+https://github.com/gtk-rs/glib#cb01dd6886337d29270c682aeb5814c59e123db8" +dependencies = [ + "bitflags", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "once_cell", +] + +[[package]] +name = "glib-macros" +version = "0.9.0" +source = "git+https://github.com/gtk-rs/glib#cb01dd6886337d29270c682aeb5814c59e123db8" +dependencies = [ + "anyhow", + "heck", + "itertools", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "glib-sys" +version = "0.9.1" +source = "git+https://github.com/gtk-rs/sys#bac293d7ce7547bd238a7f5e3e130c4f66c8df3d" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "gobject-sys" +version = "0.9.1" +source = "git+https://github.com/gtk-rs/sys#bac293d7ce7547bd238a7f5e3e130c4f66c8df3d" +dependencies = [ + "glib-sys", + "libc", + "pkg-config", +] + +[[package]] +name = "gtk" +version = "0.8.0" +source = "git+https://github.com/gtk-rs/gtk#f3201e9561f9b4ecaa724bdba4459ad7edc68533" +dependencies = [ + "atk", + "bitflags", + "cairo-rs", + "cairo-sys-rs", + "cc", + "gdk", + "gdk-pixbuf", + "gdk-pixbuf-sys", + "gdk-sys", + "gio", + "gio-sys", + "glib", + "glib-sys", + "gobject-sys", + "gtk-sys", + "libc", + "once_cell", + "pango", + "pango-sys", +] + +[[package]] +name = "gtk-layer-shell-rs" version = "0.1.0" +source = "git+https://github.com/subgraph/gtk-layer-shell-rs#6cca35d8d7957cdfaca5431855877ef2d5bef8e3" +dependencies = [ + "bitflags", + "gdk", + "gio", + "glib", + "glib-sys", + "gtk", + "gtk-layer-shell-sys", + "gtk-sys", + "libc", +] + +[[package]] +name = "gtk-layer-shell-sys" +version = "0.0.1" +source = "git+https://github.com/subgraph/gtk-layer-shell-rs#6cca35d8d7957cdfaca5431855877ef2d5bef8e3" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "gtk-sys", + "libc", + "pkg-config", +] + +[[package]] +name = "gtk-sys" +version = "0.9.2" +source = "git+https://github.com/gtk-rs/sys#bac293d7ce7547bd238a7f5e3e130c4f66c8df3d" +dependencies = [ + "atk-sys", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "pkg-config", +] + +[[package]] +name = "heck" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "itertools" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484" +dependencies = [ + "either", +] + +[[package]] +name = "libc" +version = "0.2.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" + +[[package]] +name = "once_cell" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891f486f630e5c5a4916c7e16c4b24a53e78c860b646e9f8e005e4f16847bfed" + +[[package]] +name = "pango" +version = "0.8.0" +source = "git+https://github.com/gtk-rs/pango#377c7d1e4a9a58749be9cb81df71b84ac65a05e1" +dependencies = [ + "bitflags", + "glib", + "glib-sys", + "gobject-sys", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.9.1" +source = "git+https://github.com/gtk-rs/sys#bac293d7ce7547bd238a7f5e3e130c4f66c8df3d" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "pkg-config", +] + +[[package]] +name = "pin-utils" +version = "0.1.0-alpha.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" + +[[package]] +name = "pkg-config" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" + +[[package]] +name = "proc-macro-error" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53c98547ceaea14eeb26fcadf51dc70d01a2479a7839170eae133721105e4428" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "rustversion", + "syn", +] +[[package]] +name = "proc-macro-error-attr" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2bf5d493cf5d3e296beccfd61794e445e830dfc8070a9c248ad3ee071392c6c" +dependencies = [ + "proc-macro2", + "quote", + "rustversion", + "syn", + "syn-mid", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "proc-macro-nested" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "369a6ed065f249a159e06c45752c780bda2fb53c995718f9e484d08daa9eb42e" + +[[package]] +name = "proc-macro2" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0319972dcae462681daf4da1adeeaa066e3ebd29c69be96c6abb1259d2ee2bcc" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustversion" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a0538bd897e17257b0128d2fd95c2ed6df939374073a36166051a79e2eb7986" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" + +[[package]] +name = "syn" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e4ff033220a41d1a57d8125eab57bf5263783dfdcc18688b1dacc6ce9651ef8" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "syn-mid" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fd3937748a7eccff61ba5b90af1a20dbf610858923a9192ea0ecb0cb77db1d0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-segmentation" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" + +[[package]] +name = "waffy-rs" +version = "0.1.0" +dependencies = [ + "gdk", + "gtk", + "gtk-layer-shell-rs", +] diff --git a/Cargo.toml b/Cargo.toml index dad8f13..0eccd10 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,3 +7,6 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +gtk-layer-shell-rs = { git = "https://github.com/subgraph/gtk-layer-shell-rs" } +gtk = { git = "https://github.com/gtk-rs/gtk.git" } +gdk = { git = "https://github.com/gtk-rs/gdk.git" } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index e7a11a9..e083c47 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,110 @@ +use gtk::*; +use gtk_layer_shell_rs::*; +use gdk::*; + +const GTK_STYLE_PROVIDER_PRIORITY_USER: i32 = 800; + fn main() { - println!("Hello, world!"); + open_config(); + let desktop_entries = find_desktop_entries(); + + gtk::init().expect("Could not init GTK!"); + + let window = Window::new(WindowType::Toplevel); + init_for_window(&window); + set_layer(&window, Layer::Top); + set_anchor(&window, Edge::Top, true); + set_anchor(&window, Edge::Left, true); + set_anchor(&window, Edge::Right, true); + set_anchor(&window, Edge::Bottom, true); + + window.set_resizable(false); + window.set_decorated(false); + + let layout = Box::new(Orientation::Vertical, 0); + window.add(&layout); + + let search_box = Box::new(Orientation::Horizontal, 0); + layout.pack_start(&search_box, false, false, 0); + + let grid_width = 270 * config_columns + (config_columns + 2) * COL_PADDING; + let spacer = Box::new(Orientation::Horizontal, 0); + let width = get_monitor_width() - grid_width / 2; + spacer.set_size_request(width, 1); + + search_box.pack_start(&spacer, false, false, 0); + + let search_label = Label::new(None); + search_label.set_markup(config_prompt); + search_box.pack_start(&search_label, false, false, 0); + + search_label.add_class("textview-label"); + + let search_input = TextView::new(); + search_input.set_name("search"); + search_box.pack_start(&search_input, true, true, 0); + + let app_grid = Grid::new(); + layout.pack_start(&app_grid, true, true, 0); + + for i in 0..config_columns { + app_grid.insert_column(i); + } + + app_grid.set_column_spacing(COL_PADDING); + app_grid.set_row_spacing(COL_PADDING); + app_grid.set_name("apps"); + + app_grid.set_halign(Align::Center); + + update_apps(&desktop_entries); + + let css_provider = CssProvider::new(); + + if config_wal == 1 { + window.add_class("pywal"); + } + + let css = get_css(); + + if let Ok(_) = css_provider.load_from_data(css.as_ref()) { + css_provider.add_provider_for_screen(&window, css, GTK_STYLE_PROVIDER_PRIORITY_USER); + } + + if let Some(display) = window.get_display() { + let arrow = Cursor::new_from_name(&display, "default") + .expect("Could not create 'default' cursor!"); + + let pointer = Cursor::new_from_name(&display, "pointer") + .expect("Could not create 'pointer' cursor!"); + } + + window.set_title("Waffy"); + window.show_all(); + + gtk::main(); +} + +struct DesktopEntry { + +} + +fn get_css() -> String { + +} + +fn update_apps(apps: &Vec) { + +} + +fn get_monitor_width() -> i32 { + +} + +fn find_desktop_entries() -> Vec { + +} + +fn open_config() { + } From 0e5ded14b71e584e7f83703b2c0a3c4c4613a8a9 Mon Sep 17 00:00:00 2001 From: Kasper Seweryn Date: Fri, 10 Jan 2020 18:25:02 +0100 Subject: [PATCH 03/44] Fix all stuff and make window showup Signed-off-by: Kasper Seweryn --- src/main.rs | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/src/main.rs b/src/main.rs index e083c47..1b95f2f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,13 +4,20 @@ use gdk::*; const GTK_STYLE_PROVIDER_PRIORITY_USER: i32 = 800; + fn main() { + const COL_PADDING: u32 = 17; + + let config_wal = 0; + let config_columns = 4; + let config_prompt = "run:"; + open_config(); let desktop_entries = find_desktop_entries(); gtk::init().expect("Could not init GTK!"); - let window = Window::new(WindowType::Toplevel); + let window = gtk::Window::new(gtk::WindowType::Toplevel); init_for_window(&window); set_layer(&window, Layer::Top); set_anchor(&window, Edge::Top, true); @@ -29,7 +36,7 @@ fn main() { let grid_width = 270 * config_columns + (config_columns + 2) * COL_PADDING; let spacer = Box::new(Orientation::Horizontal, 0); - let width = get_monitor_width() - grid_width / 2; + let width = get_monitor_width() - (grid_width as i32) / 2; spacer.set_size_request(width, 1); search_box.pack_start(&spacer, false, false, 0); @@ -38,22 +45,22 @@ fn main() { search_label.set_markup(config_prompt); search_box.pack_start(&search_label, false, false, 0); - search_label.add_class("textview-label"); + add_class(&search_label, "textview-label"); let search_input = TextView::new(); - search_input.set_name("search"); +// search_input.set_name("search"); search_box.pack_start(&search_input, true, true, 0); let app_grid = Grid::new(); layout.pack_start(&app_grid, true, true, 0); for i in 0..config_columns { - app_grid.insert_column(i); + app_grid.insert_column(i as i32); } app_grid.set_column_spacing(COL_PADDING); app_grid.set_row_spacing(COL_PADDING); - app_grid.set_name("apps"); +// app_grid.set_name("apps"); app_grid.set_halign(Align::Center); @@ -62,13 +69,17 @@ fn main() { let css_provider = CssProvider::new(); if config_wal == 1 { - window.add_class("pywal"); + add_class(&window, "pywal"); } let css = get_css(); if let Ok(_) = css_provider.load_from_data(css.as_ref()) { - css_provider.add_provider_for_screen(&window, css, GTK_STYLE_PROVIDER_PRIORITY_USER); + StyleContext::add_provider_for_screen( + &gdk::Screen::get_default().expect("Error initializing css provider"), + &css_provider, + STYLE_PROVIDER_PRIORITY_USER, + ); } if let Some(display) = window.get_display() { @@ -90,7 +101,7 @@ struct DesktopEntry { } fn get_css() -> String { - + return String::from("window { background: alpha(#000, .7) }"); } fn update_apps(apps: &Vec) { @@ -98,13 +109,18 @@ fn update_apps(apps: &Vec) { } fn get_monitor_width() -> i32 { - + return 1920; } fn find_desktop_entries() -> Vec { - + return Vec::::new(); } fn open_config() { } + +fn add_class(widget: &W, class_name: &str) { + let ctx = widget.get_style_context(); + ctx.add_class(class_name); +} From ddae4619be5d8947896cf0879c46dbd59d9918b9 Mon Sep 17 00:00:00 2001 From: Kasper Seweryn Date: Sat, 11 Jan 2020 10:20:14 +0100 Subject: [PATCH 04/44] Implement configuration Signed-off-by: Kasper Seweryn --- Cargo.lock | 443 +++++++++++++++++++++++++++++++++++++++ Cargo.toml | 10 +- res/default_config.hjson | 18 ++ src/main.rs | 34 ++- 4 files changed, 499 insertions(+), 6 deletions(-) create mode 100644 res/default_config.hjson diff --git a/Cargo.lock b/Cargo.lock index d098c7b..54b34ca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,11 +1,32 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "aho-corasick" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" +dependencies = [ + "memchr", +] + [[package]] name = "anyhow" version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7825f6833612eb2414095684fcf6c635becf3ce97fe48cf6421321e93bfbd53c" +[[package]] +name = "arrayref" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" + +[[package]] +name = "arrayvec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" + [[package]] name = "atk" version = "0.8.0" @@ -30,12 +51,66 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "autocfg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" + +[[package]] +name = "backtrace" +version = "0.3.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924c76597f0d9ca25d762c25a4d369d51267536465dc5064bdf0eb073ed477ea" +dependencies = [ + "backtrace-sys", + "cfg-if", + "libc", + "rustc-demangle", +] + +[[package]] +name = "backtrace-sys" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "base64" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" +dependencies = [ + "byteorder", +] + [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "blake2b_simd" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" +dependencies = [ + "arrayref", + "arrayvec", + "constant_time_eq", +] + +[[package]] +name = "byteorder" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" + [[package]] name = "cairo-rs" version = "0.8.0" @@ -65,12 +140,93 @@ version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +dependencies = [ + "bitflags", +] + +[[package]] +name = "constant_time_eq" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "995a44c877f9212528ccc74b21a232f66ad69001e40ede5bcee2ac9ef2657120" + +[[package]] +name = "crossbeam-utils" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" +dependencies = [ + "cfg-if", + "lazy_static", +] + +[[package]] +name = "dirs" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" +dependencies = [ + "cfg-if", + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b" +dependencies = [ + "cfg-if", + "libc", + "redox_users", + "winapi", +] + [[package]] name = "either" version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" +[[package]] +name = "failure" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9" +dependencies = [ + "backtrace", + "failure_derive", +] + +[[package]] +name = "failure_derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + [[package]] name = "futures-channel" version = "0.3.1" @@ -369,12 +525,52 @@ dependencies = [ "either", ] +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libc" version = "0.2.66" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" +[[package]] +name = "linked-hash-map" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d262045c5b87c0861b3f004610afd0e2c851e2908d08b6c870cbb9d5f494ecd" +dependencies = [ + "serde 0.8.23", + "serde_test", +] + +[[package]] +name = "memchr" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" + +[[package]] +name = "num-traits" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" +dependencies = [ + "num-traits 0.2.11", +] + +[[package]] +name = "num-traits" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" +dependencies = [ + "autocfg", +] + [[package]] name = "once_cell" version = "1.2.0" @@ -479,6 +675,129 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + +[[package]] +name = "rand_os" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" +dependencies = [ + "cloudabi", + "fuchsia-cprng", + "libc", + "rand_core 0.4.2", + "rdrand", + "winapi", +] + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "redox_syscall" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" + +[[package]] +name = "redox_users" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ecedbca3bf205f8d8f5c2b44d83cd0690e39ee84b951ed649e9f1841132b66d" +dependencies = [ + "failure", + "rand_os", + "redox_syscall", + "rust-argon2", +] + +[[package]] +name = "regex" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5508c1941e4e7cb19965abef075d35a9a8b5cdf0846f30b4050e9b55dc55e87" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", + "thread_local", +] + +[[package]] +name = "regex-syntax" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e734e891f5b408a29efbf8309e656876276f49ab6a6ac208600b4419bd893d90" + +[[package]] +name = "rust-argon2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca4eaef519b494d1f2848fc602d18816fed808a981aedf4f1f00ceb7c9d32cf" +dependencies = [ + "base64", + "blake2b_simd", + "crossbeam-utils", +] + +[[package]] +name = "rust-embed" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b18893bdbdb0fa5bce588f5d7ab4afbd0678fc879d31535912bf39b7fbc062d6" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50633968284cfc373661345fc6382e62b738079f045738023ebc5e445cf44357" +dependencies = [ + "quote", + "rust-embed-utils", + "syn", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97655158074ccb2d2cfb1ccb4c956ef0f4054e43a2c1e71146d4991e6961e105" +dependencies = [ + "walkdir", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" + [[package]] name = "rustversion" version = "1.0.1" @@ -490,6 +809,63 @@ dependencies = [ "syn", ] +[[package]] +name = "same-file" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585e8ddcedc187886a30fa705c47985c3fa88d06624095856b36ca0b82ff4421" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "serde" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8" + +[[package]] +name = "serde" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-hjson" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a3a4e0ea8a88553209f6cc6cfe8724ecad22e1acf372793c27d995290fe74f8" +dependencies = [ + "lazy_static", + "linked-hash-map", + "num-traits 0.1.43", + "regex", + "serde 0.8.23", +] + +[[package]] +name = "serde_derive" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_test" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "110b3dbdf8607ec493c22d5d947753282f3bae73c0f56d322af1e8c78e4c23d5" +dependencies = [ + "serde 0.8.23", +] + [[package]] name = "slab" version = "0.4.2" @@ -518,6 +894,27 @@ dependencies = [ "syn", ] +[[package]] +name = "synstructure" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "thread_local" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ddf1ad580c7e3d1efff877d972bcc93f995556b9087a5a259630985c88ceab" +dependencies = [ + "lazy_static", +] + [[package]] name = "unicode-segmentation" version = "1.6.0" @@ -534,7 +931,53 @@ checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" name = "waffy-rs" version = "0.1.0" dependencies = [ + "dirs", "gdk", "gtk", "gtk-layer-shell-rs", + "rust-embed", + "serde 1.0.104", + "serde-hjson", ] + +[[package]] +name = "walkdir" +version = "2.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9658c94fa8b940eab2250bd5a457f9c48b748420d71293b165c8cdbe2f55f71e" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml index 0eccd10..777de59 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,9 +4,13 @@ version = "0.1.0" authors = ["Kasper Seweryn "] edition = "2018" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] +dirs = "2.0.2" +serde-hjson = "0.9.1" +rust-embed = "5.2.0" +serde = { version = "1.0.104", features = ["derive"] } + +# Github deps gtk-layer-shell-rs = { git = "https://github.com/subgraph/gtk-layer-shell-rs" } gtk = { git = "https://github.com/gtk-rs/gtk.git" } -gdk = { git = "https://github.com/gtk-rs/gdk.git" } \ No newline at end of file +gdk = { git = "https://github.com/gtk-rs/gdk.git" } diff --git a/res/default_config.hjson b/res/default_config.hjson new file mode 100644 index 0000000..e98061c --- /dev/null +++ b/res/default_config.hjson @@ -0,0 +1,18 @@ +# ___ ___ # +# /'___\ /'___\ # +# __ __ __ __ /\ \__//\ \__/ __ __ # +# /\ \/\ \/\ \ /'__`\ \ \ ,__\ \ ,__\/\ \/\ \ # +# \ \ \_/ \_/ \/\ \L\.\_\ \ \_/\ \ \_/\ \ \_\ \ # +# \ \___x___/'\ \__/.\_\\ \_\ \ \_\ \/`____ \ # +# \/__//__/ \/__/\/_/ \/_/ \/_/ `/___/> \ # +# /\___/ # +# config \/__/ # + +# Number of columns in launcher +columns: 4 + +# Enable pywal colors +enable_pywal: false + +# Search box prompt +search_prompt: "search:" diff --git a/src/main.rs b/src/main.rs index 1b95f2f..d0f6030 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,10 @@ +use std::fs; use gtk::*; use gtk_layer_shell_rs::*; use gdk::*; -const GTK_STYLE_PROVIDER_PRIORITY_USER: i32 = 800; - +use serde::{Deserialize}; +use rust_embed::RustEmbed; fn main() { const COL_PADDING: u32 = 17; @@ -100,6 +101,17 @@ struct DesktopEntry { } +#[derive(RustEmbed)] +#[folder = "res/"] +struct Resource; + +#[derive(Deserialize)] +struct Config { + columns: u8, + search_prompt: String, + enable_pywal: bool, +} + fn get_css() -> String { return String::from("window { background: alpha(#000, .7) }"); } @@ -117,7 +129,23 @@ fn find_desktop_entries() -> Vec { } fn open_config() { - + if let Some(mut config_path) = dirs::config_dir() { + config_path.push("waffy"); + config_path.push("config"); + + if !config_path.exists() { + let file = Resource::get("default_config.hjson").unwrap(); + let content = std::str::from_utf8(file.as_ref()).expect("Cannot read default config"); + let config = serde_hjson::from_str::(&content).expect("Cannot parse default config"); + + fs::write(config_path, content); + Some(config) + } + + let content = fs::read_to_string(config_path).expect("Could not read config"); + let config = serde_hjson::from_str::(&content).expect("Could not parse config"); + Some(config) + } } fn add_class(widget: &W, class_name: &str) { From 868b8b2327204dfb709582ed83f8406edb9c7302 Mon Sep 17 00:00:00 2001 From: insertt Date: Sat, 11 Jan 2020 16:02:55 +0100 Subject: [PATCH 05/44] Fix embedded resources --- src/main.rs | 48 ++++++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/src/main.rs b/src/main.rs index d0f6030..d1b69f1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,12 @@ use std::fs; -use gtk::*; -use gtk_layer_shell_rs::*; + use gdk::*; +use gtk::{ + Align, BoxExt, ContainerExt, CssProvider, + CssProviderExt, Grid, GridExt, GtkWindowExt, Label, LabelExt, Orientation, StyleContext, + StyleContextExt, TextView, WidgetExt, +}; +use gtk_layer_shell_rs::*; use serde::{Deserialize}; use rust_embed::RustEmbed; @@ -29,14 +34,14 @@ fn main() { window.set_resizable(false); window.set_decorated(false); - let layout = Box::new(Orientation::Vertical, 0); + let layout = gtk::Box::new(Orientation::Vertical, 0); window.add(&layout); - let search_box = Box::new(Orientation::Horizontal, 0); + let search_box = gtk::Box::new(Orientation::Horizontal, 0); layout.pack_start(&search_box, false, false, 0); let grid_width = 270 * config_columns + (config_columns + 2) * COL_PADDING; - let spacer = Box::new(Orientation::Horizontal, 0); + let spacer = gtk::Box::new(Orientation::Horizontal, 0); let width = get_monitor_width() - (grid_width as i32) / 2; spacer.set_size_request(width, 1); @@ -49,7 +54,7 @@ fn main() { add_class(&search_label, "textview-label"); let search_input = TextView::new(); -// search_input.set_name("search"); + // search_input.set_name("search"); search_box.pack_start(&search_input, true, true, 0); let app_grid = Grid::new(); @@ -61,7 +66,7 @@ fn main() { app_grid.set_column_spacing(COL_PADDING); app_grid.set_row_spacing(COL_PADDING); -// app_grid.set_name("apps"); + // app_grid.set_name("apps"); app_grid.set_halign(Align::Center); @@ -79,16 +84,16 @@ fn main() { StyleContext::add_provider_for_screen( &gdk::Screen::get_default().expect("Error initializing css provider"), &css_provider, - STYLE_PROVIDER_PRIORITY_USER, + gtk::STYLE_PROVIDER_PRIORITY_USER, ); } if let Some(display) = window.get_display() { - let arrow = Cursor::new_from_name(&display, "default") - .expect("Could not create 'default' cursor!"); + let arrow = + Cursor::new_from_name(&display, "default").expect("Could not create 'default' cursor!"); - let pointer = Cursor::new_from_name(&display, "pointer") - .expect("Could not create 'pointer' cursor!"); + let pointer = + Cursor::new_from_name(&display, "pointer").expect("Could not create 'pointer' cursor!"); } window.set_title("Waffy"); @@ -97,9 +102,7 @@ fn main() { gtk::main(); } -struct DesktopEntry { - -} +struct DesktopEntry {} #[derive(RustEmbed)] #[folder = "res/"] @@ -116,9 +119,7 @@ fn get_css() -> String { return String::from("window { background: alpha(#000, .7) }"); } -fn update_apps(apps: &Vec) { - -} +fn update_apps(apps: &Vec) {} fn get_monitor_width() -> i32 { return 1920; @@ -128,7 +129,7 @@ fn find_desktop_entries() -> Vec { return Vec::::new(); } -fn open_config() { +fn open_config() -> Option { if let Some(mut config_path) = dirs::config_dir() { config_path.push("waffy"); config_path.push("config"); @@ -136,16 +137,19 @@ fn open_config() { if !config_path.exists() { let file = Resource::get("default_config.hjson").unwrap(); let content = std::str::from_utf8(file.as_ref()).expect("Cannot read default config"); - let config = serde_hjson::from_str::(&content).expect("Cannot parse default config"); + let config = + serde_hjson::from_str::(&content).expect("Cannot parse default config"); fs::write(config_path, content); - Some(config) + return Some(config); } let content = fs::read_to_string(config_path).expect("Could not read config"); let config = serde_hjson::from_str::(&content).expect("Could not parse config"); - Some(config) + return Some(config); } + + None } fn add_class(widget: &W, class_name: &str) { From 27ad96a4583116c7a6bff6c5201ac444c6bea9e3 Mon Sep 17 00:00:00 2001 From: Kasper Seweryn Date: Sat, 11 Jan 2020 16:14:18 +0100 Subject: [PATCH 06/44] Move from hjson to json5 Signed-off-by: Kasper Seweryn --- Cargo.lock | 227 +++++++++++++++++++++++---------------- Cargo.toml | 2 +- res/default_config.hjson | 18 ---- res/default_config.json5 | 19 ++++ src/main.rs | 6 +- 5 files changed, 155 insertions(+), 117 deletions(-) delete mode 100644 res/default_config.hjson create mode 100644 res/default_config.json5 diff --git a/Cargo.lock b/Cargo.lock index 54b34ca..e850db2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,14 +1,5 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -[[package]] -name = "aho-corasick" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" -dependencies = [ - "memchr", -] - [[package]] name = "anyhow" version = "1.0.26" @@ -51,12 +42,6 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "autocfg" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" - [[package]] name = "backtrace" version = "0.3.40" @@ -105,6 +90,33 @@ dependencies = [ "constant_time_eq", ] +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding", + "byte-tools", + "byteorder", + "generic-array", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", +] + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + [[package]] name = "byteorder" version = "1.3.2" @@ -171,6 +183,15 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array", +] + [[package]] name = "dirs" version = "2.0.2" @@ -221,6 +242,12 @@ dependencies = [ "synstructure", ] +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + [[package]] name = "fuchsia-cprng" version = "0.1.1" @@ -352,6 +379,15 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "generic-array" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +dependencies = [ + "typenum", +] + [[package]] name = "gio" version = "0.8.0" @@ -525,6 +561,17 @@ dependencies = [ "either", ] +[[package]] +name = "json5" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85fb48cdfbe18a1ef5ce0a0edc30b8b8f61422f7073f709dd09311c2b3d2bba6" +dependencies = [ + "pest", + "pest_derive", + "serde", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -538,38 +585,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" [[package]] -name = "linked-hash-map" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d262045c5b87c0861b3f004610afd0e2c851e2908d08b6c870cbb9d5f494ecd" -dependencies = [ - "serde 0.8.23", - "serde_test", -] - -[[package]] -name = "memchr" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" - -[[package]] -name = "num-traits" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" -dependencies = [ - "num-traits 0.2.11", -] - -[[package]] -name = "num-traits" -version = "0.2.11" +name = "maplit" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" -dependencies = [ - "autocfg", -] +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" [[package]] name = "once_cell" @@ -577,6 +596,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "891f486f630e5c5a4916c7e16c4b24a53e78c860b646e9f8e005e4f16847bfed" +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + [[package]] name = "pango" version = "0.8.0" @@ -602,6 +627,49 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "pest" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e4fb201c5c22a55d8b24fef95f78be52738e5e1361129be1b5e862ecdb6894a" +dependencies = [ + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b9fcf299b5712d06ee128a556c94709aaa04512c4dffb8ead07c5c998447fc0" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_meta" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df43fd99896fd72c485fe47542c7b500e4ac1e8700bf995544d1317a60ded547" +dependencies = [ + "maplit", + "pest", + "sha-1", +] + [[package]] name = "pin-utils" version = "0.1.0-alpha.4" @@ -731,24 +799,6 @@ dependencies = [ "rust-argon2", ] -[[package]] -name = "regex" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5508c1941e4e7cb19965abef075d35a9a8b5cdf0846f30b4050e9b55dc55e87" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", - "thread_local", -] - -[[package]] -name = "regex-syntax" -version = "0.6.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e734e891f5b408a29efbf8309e656876276f49ab6a6ac208600b4419bd893d90" - [[package]] name = "rust-argon2" version = "0.5.1" @@ -818,12 +868,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "serde" -version = "0.8.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8" - [[package]] name = "serde" version = "1.0.104" @@ -833,19 +877,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde-hjson" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a3a4e0ea8a88553209f6cc6cfe8724ecad22e1acf372793c27d995290fe74f8" -dependencies = [ - "lazy_static", - "linked-hash-map", - "num-traits 0.1.43", - "regex", - "serde 0.8.23", -] - [[package]] name = "serde_derive" version = "1.0.104" @@ -858,12 +889,15 @@ dependencies = [ ] [[package]] -name = "serde_test" -version = "0.8.23" +name = "sha-1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "110b3dbdf8607ec493c22d5d947753282f3bae73c0f56d322af1e8c78e4c23d5" +checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" dependencies = [ - "serde 0.8.23", + "block-buffer", + "digest", + "fake-simd", + "opaque-debug", ] [[package]] @@ -907,13 +941,16 @@ dependencies = [ ] [[package]] -name = "thread_local" -version = "1.0.0" +name = "typenum" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ddf1ad580c7e3d1efff877d972bcc93f995556b9087a5a259630985c88ceab" -dependencies = [ - "lazy_static", -] +checksum = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" + +[[package]] +name = "ucd-trie" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f00ed7be0c1ff1e24f46c3d2af4859f7e863672ba3a6e92e7cff702bf9f06c2" [[package]] name = "unicode-segmentation" @@ -935,9 +972,9 @@ dependencies = [ "gdk", "gtk", "gtk-layer-shell-rs", + "json5", "rust-embed", - "serde 1.0.104", - "serde-hjson", + "serde", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 777de59..4bb96d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" [dependencies] dirs = "2.0.2" -serde-hjson = "0.9.1" +json5 = "0.2.5" rust-embed = "5.2.0" serde = { version = "1.0.104", features = ["derive"] } diff --git a/res/default_config.hjson b/res/default_config.hjson deleted file mode 100644 index e98061c..0000000 --- a/res/default_config.hjson +++ /dev/null @@ -1,18 +0,0 @@ -# ___ ___ # -# /'___\ /'___\ # -# __ __ __ __ /\ \__//\ \__/ __ __ # -# /\ \/\ \/\ \ /'__`\ \ \ ,__\ \ ,__\/\ \/\ \ # -# \ \ \_/ \_/ \/\ \L\.\_\ \ \_/\ \ \_/\ \ \_\ \ # -# \ \___x___/'\ \__/.\_\\ \_\ \ \_\ \/`____ \ # -# \/__//__/ \/__/\/_/ \/_/ \/_/ `/___/> \ # -# /\___/ # -# config \/__/ # - -# Number of columns in launcher -columns: 4 - -# Enable pywal colors -enable_pywal: false - -# Search box prompt -search_prompt: "search:" diff --git a/res/default_config.json5 b/res/default_config.json5 new file mode 100644 index 0000000..b2e68fe --- /dev/null +++ b/res/default_config.json5 @@ -0,0 +1,19 @@ +// ___ ___ // +// /'___\ /'___\ // +// __ __ __ __ /\ \__//\ \__/ __ __ // +// /\ \/\ \/\ \ /'__`\ \ \ ,__\ \ ,__\/\ \/\ \ // +// \ \ \_/ \_/ \/\ \L\.\_\ \ \_/\ \ \_/\ \ \_\ \ // +// \ \___x___/'\ \__/.\_\\ \_\ \ \_\ \/`____ \ // +// \/__//__/ \/__/\/_/ \/_/ \/_/ `/___/> \ // +// /\___/ // +// config \/__/ // +{ + // Number of columns in launcher + columns: 4, + + // Enable pywal colors + enable_pywal: false, + + // Search box prompt + search_prompt: 'search:', +} diff --git a/src/main.rs b/src/main.rs index d1b69f1..444095c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -135,17 +135,17 @@ fn open_config() -> Option { config_path.push("config"); if !config_path.exists() { - let file = Resource::get("default_config.hjson").unwrap(); + let file = Resource::get("default_config.json5").unwrap(); let content = std::str::from_utf8(file.as_ref()).expect("Cannot read default config"); let config = - serde_hjson::from_str::(&content).expect("Cannot parse default config"); + json5::from_str::(&content).expect("Cannot parse default config"); fs::write(config_path, content); return Some(config); } let content = fs::read_to_string(config_path).expect("Could not read config"); - let config = serde_hjson::from_str::(&content).expect("Could not parse config"); + let config = json5::from_str::(&content).expect("Could not parse config"); return Some(config); } From bc87e875193bb6dab2e9987c55b3fde2f7a0f3f8 Mon Sep 17 00:00:00 2001 From: Kasper Seweryn Date: Sat, 11 Jan 2020 23:59:31 +0100 Subject: [PATCH 07/44] Create config path if it doesn't exist --- res/default_style.css | 59 +++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 36 ++++++++++++++++++-------- 2 files changed, 84 insertions(+), 11 deletions(-) create mode 100644 res/default_style.css diff --git a/res/default_style.css b/res/default_style.css new file mode 100644 index 0000000..68bb4cf --- /dev/null +++ b/res/default_style.css @@ -0,0 +1,59 @@ + +label { + color: #fff; +} + +.textview-label { + padding-right: 0.5em; +} + +.textview-label, +textview { + font-size: 2em; + padding-top: 47px; + padding-bottom: 47px; +} + +.textview-label, +textview text { + color: #fff; +} + +* { + background-color: transparent; + border: none; +} + +#apps > button { + min-width: 270px; + + border: none; + transition: none; + box-shadow: none; +} + +/* default colors */ +window { + background: alpha(#000, .7); +} + +#apps > button { + background: alpha(#fff, .1); +} + +#apps > button.active { + background: alpha(#fff, .3); +} + +/* pywal colors */ +window.pywal { + background: alpha(@background, .7); +} + +window.pywal #apps > button { + background: @background; +} + +window.pywal #apps > button.active { + background: @color5; +} diff --git a/src/main.rs b/src/main.rs index 444095c..06d340e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,13 +12,21 @@ use serde::{Deserialize}; use rust_embed::RustEmbed; fn main() { + // Create config dir + if let Some(mut config_dir) = dirs::config_dir() { + config_dir.push("waffy"); + if !config_dir.exists() { + fs::create_dir_all(config_dir); + } + } + const COL_PADDING: u32 = 17; let config_wal = 0; let config_columns = 4; let config_prompt = "run:"; - open_config(); + get_config(); let desktop_entries = find_desktop_entries(); gtk::init().expect("Could not init GTK!"); @@ -129,27 +137,33 @@ fn find_desktop_entries() -> Vec { return Vec::::new(); } -fn open_config() -> Option { +fn get_default_config (path_to_save: Option) -> Config { + let file = Resource::get("default_config.json5").unwrap(); + let content = String::from_utf8(file.as_ref().to_vec()).expect("Cannot read default config"); + let config = json5::from_str::(&content).expect("Cannot parse default config"); + + if let Some(path) = path_to_save { + fs::write(path, content); + } + + return config; +} + +fn get_config() -> Config { if let Some(mut config_path) = dirs::config_dir() { config_path.push("waffy"); config_path.push("config"); if !config_path.exists() { - let file = Resource::get("default_config.json5").unwrap(); - let content = std::str::from_utf8(file.as_ref()).expect("Cannot read default config"); - let config = - json5::from_str::(&content).expect("Cannot parse default config"); - - fs::write(config_path, content); - return Some(config); + return get_default_config(Some(config_path)); } let content = fs::read_to_string(config_path).expect("Could not read config"); let config = json5::from_str::(&content).expect("Could not parse config"); - return Some(config); + return config; } - None + get_default_config(None) } fn add_class(widget: &W, class_name: &str) { From bc5947530600635a27abd465fd35f8ca366f49fc Mon Sep 17 00:00:00 2001 From: Kasper Seweryn Date: Sun, 12 Jan 2020 00:14:35 +0100 Subject: [PATCH 08/44] Implement get_css() --- src/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.rs b/src/main.rs index 06d340e..0fb9259 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,6 +11,7 @@ use gtk_layer_shell_rs::*; use serde::{Deserialize}; use rust_embed::RustEmbed; +//mod app; fn main() { // Create config dir if let Some(mut config_dir) = dirs::config_dir() { From ee29d7f3348950e044ceff8f548a9cb2100d37a1 Mon Sep 17 00:00:00 2001 From: insertt Date: Sun, 12 Jan 2020 11:17:34 +0100 Subject: [PATCH 09/44] Port desktop files parser --- src/main.rs | 125 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 118 insertions(+), 7 deletions(-) diff --git a/src/main.rs b/src/main.rs index 444095c..296d71b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,15 +1,17 @@ use std::fs; +use std::fs::File; +use std::io; +use std::path::PathBuf; use gdk::*; use gtk::{ - Align, BoxExt, ContainerExt, CssProvider, - CssProviderExt, Grid, GridExt, GtkWindowExt, Label, LabelExt, Orientation, StyleContext, - StyleContextExt, TextView, WidgetExt, + Align, BoxExt, ContainerExt, CssProvider, CssProviderExt, Grid, GridExt, GtkWindowExt, Label, + LabelExt, Orientation, StyleContext, StyleContextExt, TextView, WidgetExt, }; use gtk_layer_shell_rs::*; -use serde::{Deserialize}; use rust_embed::RustEmbed; +use serde::Deserialize; fn main() { const COL_PADDING: u32 = 17; @@ -102,8 +104,6 @@ fn main() { gtk::main(); } -struct DesktopEntry {} - #[derive(RustEmbed)] #[folder = "res/"] struct Resource; @@ -125,8 +125,119 @@ fn get_monitor_width() -> i32 { return 1920; } +#[derive(Debug)] +struct DesktopEntry { + name: Option, + icon: Option, +} + +impl DesktopEntry { + pub fn empty() -> Self { + Self { + name: None, + icon: None, + } + } + + pub fn set_name>(&mut self, name: S) { + self.name = Some(name.into()); + } + + pub fn set_icon>(&mut self, icon: S) { + self.icon = Some(icon.into()); + } +} + fn find_desktop_entries() -> Vec { - return Vec::::new(); + let mut desktop_entry_dirs: Vec = vec!["/usr/share/applications".into(), "/usr/local/share/applications".into()]; + + if let Some(user_dir) = dirs::home_dir() { + desktop_entry_dirs.push(user_dir.join(".local/share/applications/")); + } + + let mut desktop_entries: Vec = Vec::with_capacity(10); + + for dir in desktop_entry_dirs { + if let Ok(read_dir) = fs::read_dir(dir) { + for entry in read_dir { + if let Ok(entry) = entry { + let path = entry.path(); + let extension = path.extension(); + + match extension { + Some(extension) => { + if extension != "desktop" { + continue; + } + } + None => continue, + } + + if let Ok(desktop_entry) = parse_desktop_file(path) { + if let Some(desktop_entry) = desktop_entry { + desktop_entries.push(desktop_entry); + } + } + } + } + } + } + + desktop_entries +} + +fn parse_desktop_file(path: PathBuf) -> io::Result> { + let content = fs::read_to_string(path)?; + + let mut entry = DesktopEntry::empty(); + let mut entry_section = false; + + for line in content.lines() { + let mut chars = line.chars(); + + if let Some(first_char) = chars.nth(0) { + if first_char == '\0' || first_char == '#' { + continue; + } + } + + if entry_section { + if let Some(first_char) = chars.nth(0) { + if first_char == '[' { + break; + } + } + + let split = line.splitn(2, "=").map(String::from).collect::>(); + + let key = match split.get(0) { + Some(key) => key, + None => continue, + }; + let value = match split.get(1) { + Some(value) => value, + None => continue, + }; + + if key == "NoDisplay" && value == "true" { + return Ok(None); + } + + if key == "Name" { + entry.set_name(value); + continue; + } + + if value == "Icon" { + entry.set_icon(value); + continue; + } + } else if line == "[Desktop Entry]" { + entry_section = true; + } + } + + Ok(Some(entry)) } fn open_config() -> Option { From 4fde1e2378269d951048b703b7b1f5c0d8351861 Mon Sep 17 00:00:00 2001 From: Kasper Seweryn Date: Sun, 12 Jan 2020 14:41:39 +0100 Subject: [PATCH 10/44] Move get_config() to Config::get() --- Cargo.lock | 1 + Cargo.toml | 1 + src/main.rs | 63 +++++++++++++++++++++++++++++++---------------------- 3 files changed, 39 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e850db2..9e2c9fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -973,6 +973,7 @@ dependencies = [ "gtk", "gtk-layer-shell-rs", "json5", + "once_cell", "rust-embed", "serde", ] diff --git a/Cargo.toml b/Cargo.toml index 4bb96d9..2a1e608 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ dirs = "2.0.2" json5 = "0.2.5" rust-embed = "5.2.0" serde = { version = "1.0.104", features = ["derive"] } +once_cell = "1.2.0" # Github deps gtk-layer-shell-rs = { git = "https://github.com/subgraph/gtk-layer-shell-rs" } diff --git a/src/main.rs b/src/main.rs index 09c5fdd..ad3b3c0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ use std::fs; use std::fs::File; use std::io; use std::path::PathBuf; +use once_cell::sync::OnceCell; use gdk::*; use gtk::{ @@ -19,7 +20,7 @@ fn main() { if let Some(mut config_dir) = dirs::config_dir() { config_dir.push("waffy"); if !config_dir.exists() { - fs::create_dir_all(config_dir); + let _ = fs::create_dir_all(config_dir); } } @@ -29,7 +30,7 @@ fn main() { let config_columns = 4; let config_prompt = "run:"; - get_config(); + let config = Config::get(); let desktop_entries = find_desktop_entries(); gtk::init().expect("Could not init GTK!"); @@ -124,14 +125,15 @@ struct Config { enable_pywal: bool, } -fn get_css() -> String { - return String::from("window { background: alpha(#000, .7) }"); -} +fn get_default_css(path_to_save: Option) -> String { + let file = Resource::get("default_style.css").unwrap(); + let content = String::from_utf8(file.as_ref().to_vec()).expect("Cannot read default style"); -fn update_apps(apps: &Vec) {} + if let Some(path) = path_to_save { + let _ = fs::write(path, &content); + } -fn get_monitor_width() -> i32 { - return 1920; + content } #[derive(Debug)] @@ -249,33 +251,42 @@ fn parse_desktop_file(path: PathBuf) -> io::Result> { Ok(Some(entry)) } -fn get_default_config (path_to_save: Option) -> Config { - let file = Resource::get("default_config.json5").unwrap(); - let content = String::from_utf8(file.as_ref().to_vec()).expect("Cannot read default config"); - let config = json5::from_str::(&content).expect("Cannot parse default config"); +static CONFIG: OnceCell = OnceCell::new(); - if let Some(path) = path_to_save { - fs::write(path, content); +impl Config { + fn default (path_to_save: Option) -> Self { + let file = Resource::get("default_config.json5").unwrap(); + let content = String::from_utf8(file.as_ref().to_vec()).expect("Cannot read default config"); + let config = json5::from_str::(&content).expect("Cannot parse default config"); + + if let Some(path) = path_to_save { + let _ = fs::write(path, &content); + } + + config } - return config; -} + fn get_uncached () -> Self { + if let Some(mut config_path) = dirs::config_dir() { + config_path.push("waffy"); + config_path.push("config"); -fn get_config() -> Config { - if let Some(mut config_path) = dirs::config_dir() { - config_path.push("waffy"); - config_path.push("config"); + if !config_path.exists() { + return Self::default(Some(config_path)); + } - if !config_path.exists() { - return get_default_config(Some(config_path)); + let content = fs::read_to_string(config_path).expect("Could not read config"); + let config = json5::from_str::(&content).expect("Could not parse config"); + return config; } - let content = fs::read_to_string(config_path).expect("Could not read config"); - let config = json5::from_str::(&content).expect("Could not parse config"); - return config; + Self::default(None) } - get_default_config(None) + + fn get () -> &'static Self { + CONFIG.get_or_init(Self::get_uncached) + } } fn add_class(widget: &W, class_name: &str) { From b0f63423097a49818259d44a65b88bd24eabfe17 Mon Sep 17 00:00:00 2001 From: Kasper Seweryn Date: Sun, 12 Jan 2020 14:42:13 +0100 Subject: [PATCH 11/44] Add get_css() --- src/main.rs | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/main.rs b/src/main.rs index ad3b3c0..82b3f48 100644 --- a/src/main.rs +++ b/src/main.rs @@ -289,6 +289,44 @@ impl Config { } } +fn get_css() -> String { + let mut content = String::from(""); + if let Some(mut style_path) = dirs::config_dir() { + style_path.push("waffy"); + style_path.push("style.css"); + + if style_path.exists() { + content = fs::read_to_string(style_path).expect("Could not read config"); + } else { + content = get_default_css(Some(style_path)) + } + } + + if content == "" { + content = get_default_css(None); + } + + // TODO: Check config option + if let Some(mut pywal_path) = dirs::cache_dir() { + pywal_path.push("wal"); + pywal_path.push("colors-waybar.css"); + + if pywal_path.exists() { + let mut pywal_content = fs::read_to_string(pywal_path).expect("Could not read config"); + pywal_content.push_str(content.as_str()); + content = pywal_content; + } + } + + content +} + +fn update_apps(apps: &Vec) {} + +fn get_monitor_width() -> i32 { + return 1920; +} + fn add_class(widget: &W, class_name: &str) { let ctx = widget.get_style_context(); ctx.add_class(class_name); From 0316dee064cb4a74d0ad32e821f35be2b4e2556e Mon Sep 17 00:00:00 2001 From: Kasper Seweryn Date: Sun, 12 Jan 2020 14:43:23 +0100 Subject: [PATCH 12/44] Add part of AppGrid --- src/app.rs | 7 +++++++ src/app/grid.rs | 53 +++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 3 ++- 3 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 src/app.rs create mode 100644 src/app/grid.rs diff --git a/src/app.rs b/src/app.rs new file mode 100644 index 0000000..9930656 --- /dev/null +++ b/src/app.rs @@ -0,0 +1,7 @@ +pub mod grid; +use gtk::{Button}; + +#[derive(Copy, Clone)] +pub struct AppButton { + pub element: Button, +} \ No newline at end of file diff --git a/src/app/grid.rs b/src/app/grid.rs new file mode 100644 index 0000000..7e225d6 --- /dev/null +++ b/src/app/grid.rs @@ -0,0 +1,53 @@ +use super::AppButton; +use gtk::{Widget, ScrolledWindow, Viewport, Grid, GridExt, ContainerExt, WidgetExt}; + +pub struct AppGrid { + filter: fn(&Vec) -> &Vec, + buttons: Vec, + grid: Grid, + pub element: ScrolledWindow, +} + +impl AppGrid { + pub fn new (buttons: &Vec) -> Self { + + let window = ScrolledWindow::new(None, None); + let viewport = Viewport::new(None, None); + let grid = Grid::new(); + + // Initialize columns + let columns = 4; // TODO: Get from config + for i in 0..(columns - 1) { + grid.insert_column(i); + } + + window.add(&viewport); + viewport.add(&grid); + + Self { + filter: |buttons| { buttons }, + buttons: buttons.to_vec(), + grid, + element: window, + } + } + + pub fn add (&mut self, button: &AppButton) { + self.buttons.push(*button); + } + + pub fn show_all (&self) { + let buttons = (self.filter)(&self.buttons); + let columns = 4; // TODO: Get from config + + self.grid.foreach(|child| { + self.grid.remove(child); + }); + + for (i, button) in buttons.iter().enumerate() { + self.grid.attach(button.element, i as i32 % columns, i as i32 / columns, 1, 1); + } + + self.grid.show_all(); + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 82b3f48..cca667e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,7 +14,8 @@ use gtk_layer_shell_rs::*; use rust_embed::RustEmbed; use serde::Deserialize; -//mod app; +mod app; + fn main() { // Create config dir if let Some(mut config_dir) = dirs::config_dir() { From 553324362ad273534cb526ff51a2fc9553632adc Mon Sep 17 00:00:00 2001 From: Kasper Seweryn Date: Sun, 12 Jan 2020 17:30:07 +0100 Subject: [PATCH 13/44] Implement Grid --- Cargo.lock | 7 +++++ Cargo.toml | 1 + src/app.rs | 7 ----- src/app/grid.rs | 53 ------------------------------------ src/grid.rs | 72 +++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 2 +- 6 files changed, 81 insertions(+), 61 deletions(-) delete mode 100644 src/app.rs delete mode 100644 src/app/grid.rs create mode 100644 src/grid.rs diff --git a/Cargo.lock b/Cargo.lock index 9e2c9fb..5a56594 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -906,6 +906,12 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" +[[package]] +name = "sublime_fuzzy" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdac3d983d073c19487ba1f5e16eda43e9c6e50aa895d87110d0febe389b66b9" + [[package]] name = "syn" version = "1.0.13" @@ -976,6 +982,7 @@ dependencies = [ "once_cell", "rust-embed", "serde", + "sublime_fuzzy", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 2a1e608..9b588a0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ json5 = "0.2.5" rust-embed = "5.2.0" serde = { version = "1.0.104", features = ["derive"] } once_cell = "1.2.0" +sublime_fuzzy = "0.6.0" # Github deps gtk-layer-shell-rs = { git = "https://github.com/subgraph/gtk-layer-shell-rs" } diff --git a/src/app.rs b/src/app.rs deleted file mode 100644 index 9930656..0000000 --- a/src/app.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub mod grid; -use gtk::{Button}; - -#[derive(Copy, Clone)] -pub struct AppButton { - pub element: Button, -} \ No newline at end of file diff --git a/src/app/grid.rs b/src/app/grid.rs deleted file mode 100644 index 7e225d6..0000000 --- a/src/app/grid.rs +++ /dev/null @@ -1,53 +0,0 @@ -use super::AppButton; -use gtk::{Widget, ScrolledWindow, Viewport, Grid, GridExt, ContainerExt, WidgetExt}; - -pub struct AppGrid { - filter: fn(&Vec) -> &Vec, - buttons: Vec, - grid: Grid, - pub element: ScrolledWindow, -} - -impl AppGrid { - pub fn new (buttons: &Vec) -> Self { - - let window = ScrolledWindow::new(None, None); - let viewport = Viewport::new(None, None); - let grid = Grid::new(); - - // Initialize columns - let columns = 4; // TODO: Get from config - for i in 0..(columns - 1) { - grid.insert_column(i); - } - - window.add(&viewport); - viewport.add(&grid); - - Self { - filter: |buttons| { buttons }, - buttons: buttons.to_vec(), - grid, - element: window, - } - } - - pub fn add (&mut self, button: &AppButton) { - self.buttons.push(*button); - } - - pub fn show_all (&self) { - let buttons = (self.filter)(&self.buttons); - let columns = 4; // TODO: Get from config - - self.grid.foreach(|child| { - self.grid.remove(child); - }); - - for (i, button) in buttons.iter().enumerate() { - self.grid.attach(button.element, i as i32 % columns, i as i32 / columns, 1, 1); - } - - self.grid.show_all(); - } -} \ No newline at end of file diff --git a/src/grid.rs b/src/grid.rs new file mode 100644 index 0000000..cecbeed --- /dev/null +++ b/src/grid.rs @@ -0,0 +1,72 @@ +use gtk::{Grid as GtkGrid, ScrolledWindow as GtkWindow, Viewport as GtkViewport, Widget as GtkWidget, GridExt, ViewportExt, ScrolledWindowExt, ContainerExt, WidgetExt, Adjustment}; +use sublime_fuzzy::{best_match as fuzzy_match, format_simple as fuzzy_format}; + +use super::Config; + +pub trait GridButton { + fn default_icon(&self) -> String; + fn label(&self) -> String; + fn display_label(&self) -> String; + fn icon(&self) -> Option; + fn set_display_label(&self, label: String) -> (); + fn gtk_widget(&self) -> &GtkWidget; +} + +pub struct Grid { + items: Vec, + filter_string: String, + flags: u8, + click_callback: Box, + window: GtkWindow, + grid: GtkGrid, +} + +impl Grid { + pub fn new (items: Vec, flags: u8, click_callback: Box) -> Self { + let adjustment = None::<>k::Adjustment>; + let window = GtkWindow::new(adjustment, adjustment); + let viewport = GtkViewport::new(adjustment, adjustment); + let grid = GtkGrid::new(); + + let config = Config::get(); + + // Initialize columns + for i in 0..(config.columns - 1) { + grid.insert_column(i as i32); + } + + window.add(&viewport); + viewport.add(&grid); + + Self { items, filter_string: String::from(""), flags, click_callback, window, grid } + } + + pub fn filter (&mut self, needle: String) { + self.filter_string = needle; + self.update(); + } + + pub fn update (&self) { + self.grid.foreach(|child| self.grid.remove(child)); + + let config = Config::get(); + let columns = config.columns as i32; + + let mut filtered: Vec<&T> = Vec::new(); + for item in self.items.iter() { + let label = item.label(); + if let Some(res) = fuzzy_match(self.filter_string.as_str(), label.as_str()) { + item.set_display_label(fuzzy_format(&res, label.as_str(), "", "")); + filtered.push(item); + } + } + + for (i, item) in filtered.into_iter().enumerate() { + let col = i as i32 % columns; + let row = i as i32 / columns; + self.grid.attach(item.gtk_widget(), col, row, 1, 1); + } + + self.grid.show_all(); + } +} diff --git a/src/main.rs b/src/main.rs index cca667e..8c79b23 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,7 +14,7 @@ use gtk_layer_shell_rs::*; use rust_embed::RustEmbed; use serde::Deserialize; -mod app; +mod grid; fn main() { // Create config dir From 8a2d0f0710b931ea9485f0dfa3ffafd9bde76186 Mon Sep 17 00:00:00 2001 From: Kasper Seweryn Date: Sun, 12 Jan 2020 19:32:54 +0100 Subject: [PATCH 14/44] Add items to grid Close #15 Close #13 --- Cargo.lock | 3 ++ Cargo.toml | 3 ++ src/grid.rs | 60 ++++++++++++++++++++++++++++-------- src/main.rs | 89 +++++++++++++++++++++++++++++++++++++++++++---------- 4 files changed, 125 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5a56594..5018ee0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -976,10 +976,13 @@ version = "0.1.0" dependencies = [ "dirs", "gdk", + "gdk-pixbuf", + "gio", "gtk", "gtk-layer-shell-rs", "json5", "once_cell", + "pango", "rust-embed", "serde", "sublime_fuzzy", diff --git a/Cargo.toml b/Cargo.toml index 9b588a0..77ca57f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,3 +16,6 @@ sublime_fuzzy = "0.6.0" gtk-layer-shell-rs = { git = "https://github.com/subgraph/gtk-layer-shell-rs" } gtk = { git = "https://github.com/gtk-rs/gtk.git" } gdk = { git = "https://github.com/gtk-rs/gdk.git" } +gdk-pixbuf = { git = "https://github.com/gtk-rs/gdk-pixbuf.git" } +gio = { git = "https://github.com/gtk-rs/gio.git" } +pango = { git = "https://github.com/gtk-rs/pango.git" } diff --git a/src/grid.rs b/src/grid.rs index cecbeed..0b698f6 100644 --- a/src/grid.rs +++ b/src/grid.rs @@ -1,28 +1,32 @@ -use gtk::{Grid as GtkGrid, ScrolledWindow as GtkWindow, Viewport as GtkViewport, Widget as GtkWidget, GridExt, ViewportExt, ScrolledWindowExt, ContainerExt, WidgetExt, Adjustment}; +use gtk::{ + Grid as GtkGrid, ScrolledWindow as GtkWindow, Viewport as GtkViewport, Widget as GtkWidget, + GridExt, ViewportExt, ScrolledWindowExt, ContainerExt, WidgetExt, LabelExt, +}; use sublime_fuzzy::{best_match as fuzzy_match, format_simple as fuzzy_format}; use super::Config; +pub const SHOW_ICON: u32 = 0b01; +pub const SHOW_LABEL: u32 = 0b10; + pub trait GridButton { - fn default_icon(&self) -> String; - fn label(&self) -> String; - fn display_label(&self) -> String; - fn icon(&self) -> Option; - fn set_display_label(&self, label: String) -> (); - fn gtk_widget(&self) -> &GtkWidget; + fn label(&self) -> &String; + fn display_label(&self) -> gtk::Label; + fn icon(&self) -> gtk::Image; + fn set_display_label(&mut self, label: String); } pub struct Grid { items: Vec, filter_string: String, - flags: u8, + flags: u32, click_callback: Box, - window: GtkWindow, + pub window: GtkWindow, grid: GtkGrid, } impl Grid { - pub fn new (items: Vec, flags: u8, click_callback: Box) -> Self { + pub fn new (items: Vec, flags: u32, click_callback: Box) -> Self { let adjustment = None::<>k::Adjustment>; let window = GtkWindow::new(adjustment, adjustment); let viewport = GtkViewport::new(adjustment, adjustment); @@ -35,6 +39,10 @@ impl Grid { grid.insert_column(i as i32); } + grid.set_column_spacing(17); + grid.set_row_spacing(17); + grid.set_halign(gtk::Align::Center); + window.add(&viewport); viewport.add(&grid); @@ -46,7 +54,7 @@ impl Grid { self.update(); } - pub fn update (&self) { + pub fn update (&mut self) { self.grid.foreach(|child| self.grid.remove(child)); let config = Config::get(); @@ -54,9 +62,14 @@ impl Grid { let mut filtered: Vec<&T> = Vec::new(); for item in self.items.iter() { + if self.filter_string == "" { + filtered.push(item); + continue; + } + let label = item.label(); if let Some(res) = fuzzy_match(self.filter_string.as_str(), label.as_str()) { - item.set_display_label(fuzzy_format(&res, label.as_str(), "", "")); +// item.set_display_label(fuzzy_format(&res, label.as_str(), "", "")); filtered.push(item); } } @@ -64,7 +77,28 @@ impl Grid { for (i, item) in filtered.into_iter().enumerate() { let col = i as i32 % columns; let row = i as i32 / columns; - self.grid.attach(item.gtk_widget(), col, row, 1, 1); + + let widget = gtk::Button::new(); + let content = gtk::Grid::new(); + + content.set_column_spacing(17); + + if self.flags & SHOW_ICON > 0 { + content.insert_column(0); + content.attach(&item.icon(), 0, 0, 1, 1); + } + + if self.flags & SHOW_LABEL > 0 { + let label = item.display_label(); + content.insert_column(1); + content.attach(&label, 1, 0, 1, 1); + + label.set_max_width_chars(16); + label.set_ellipsize(pango::EllipsizeMode::End); + } + + widget.add(&content); + self.grid.attach(&widget, col, row, 1, 1); } self.grid.show_all(); diff --git a/src/main.rs b/src/main.rs index 8c79b23..fa4f114 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,13 @@ use std::fs; -use std::fs::File; use std::io; use std::path::PathBuf; use once_cell::sync::OnceCell; use gdk::*; use gtk::{ - Align, BoxExt, ContainerExt, CssProvider, CssProviderExt, Grid, GridExt, GtkWindowExt, Label, - LabelExt, Orientation, StyleContext, StyleContextExt, TextView, WidgetExt, + Align, BoxExt, ContainerExt, CssProvider, CssProviderExt, GridExt, GtkWindowExt, Label, + LabelExt, Orientation, StyleContext, StyleContextExt, TextView, WidgetExt, Widget, + IconTheme, Image, ImageExt, IconThemeExt }; use gtk_layer_shell_rs::*; @@ -15,6 +15,7 @@ use rust_embed::RustEmbed; use serde::Deserialize; mod grid; +use grid::GridButton; fn main() { // Create config dir @@ -70,20 +71,23 @@ fn main() { // search_input.set_name("search"); search_box.pack_start(&search_input, true, true, 0); - let app_grid = Grid::new(); - layout.pack_start(&app_grid, true, true, 0); + let mut app_grid = grid::Grid::::new(desktop_entries, grid::SHOW_ICON | grid::SHOW_LABEL, Box::new(|entry| { + println!("{:?}", entry.display_name); + })); + + layout.pack_start(&app_grid.window, true, true, 0); for i in 0..config_columns { - app_grid.insert_column(i as i32); +// app_grid.insert_column(i as i32); } - app_grid.set_column_spacing(COL_PADDING); - app_grid.set_row_spacing(COL_PADDING); +// app_grid.set_column_spacing(COL_PADDING); +// app_grid.set_row_spacing(COL_PADDING); // app_grid.set_name("apps"); - app_grid.set_halign(Align::Center); +// app_grid.set_halign(Align::Center); - update_apps(&desktop_entries); +// update_apps(&desktop_entries); let css_provider = CssProvider::new(); @@ -109,8 +113,9 @@ fn main() { Cursor::new_from_name(&display, "pointer").expect("Could not create 'pointer' cursor!"); } - window.set_title("Waffy"); + window.set_title("waffy"); window.show_all(); + app_grid.update(); gtk::main(); } @@ -139,24 +144,73 @@ fn get_default_css(path_to_save: Option) -> String { #[derive(Debug)] struct DesktopEntry { - name: Option, - icon: Option, + name: String, + display_name: String, + icon_path: Option, } impl DesktopEntry { pub fn empty() -> Self { Self { - name: None, - icon: None, + name: String::from(""), + display_name: String::from(""), + icon_path: None, } } pub fn set_name>(&mut self, name: S) { - self.name = Some(name.into()); + self.name = name.into(); + self.display_name = self.name.clone(); } pub fn set_icon>(&mut self, icon: S) { - self.icon = Some(icon.into()); + self.icon_path = Some(icon.into()); + } +} + +impl GridButton for DesktopEntry { + fn label(&self) -> &String { + &self.name + } + + fn display_label(&self) -> Label { + Label::new(Some(&self.display_name)) + } + + fn icon(&self) -> Image { + if self.icon_path == None { + // gtk::IconSize::Dialog is 48x48 + return Image::new_from_icon_name(Some("application-x-executable"), gtk::IconSize::Dialog); + } + + let icon = self.icon_path.as_ref().unwrap(); + if PathBuf::from(&icon).exists() { + let image = Image::new_from_file(&icon); + + // Scale down to 48x48 + if let Some(pixbuf) = image.get_pixbuf() { + if let Some(pixbuf) = pixbuf.scale_simple(48, 48, gdk_pixbuf::InterpType::Tiles) { + return Image::new_from_pixbuf(Some(&pixbuf)); + } + } + } + + if let Some(theme) = IconTheme::get_default() { + let info = theme.load_icon(&icon, 48, gtk::IconLookupFlags::FORCE_SIZE); + + if let Ok(info) = info { + if let Some(pixbuf) = info { + return Image::new_from_pixbuf(Some(&pixbuf)); + } + } + } + + // gtk::IconSize::Dialog is 48x48 + Image::new_from_icon_name(Some("application-x-executable"), gtk::IconSize::Dialog) + } + + fn set_display_label(&mut self, label: String) { + self.display_name = label; } } @@ -242,6 +296,7 @@ fn parse_desktop_file(path: PathBuf) -> io::Result> { if value == "Icon" { entry.set_icon(value); + println!("{}", value); continue; } } else if line == "[Desktop Entry]" { From 46cea2dc6ecff435d231ebfb0fb59ea328339ee1 Mon Sep 17 00:00:00 2001 From: Kasper Seweryn Date: Sun, 12 Jan 2020 19:40:24 +0100 Subject: [PATCH 15/44] Fix icons --- src/main.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index fa4f114..19e3fcc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -294,9 +294,8 @@ fn parse_desktop_file(path: PathBuf) -> io::Result> { continue; } - if value == "Icon" { + if key == "Icon" { entry.set_icon(value); - println!("{}", value); continue; } } else if line == "[Desktop Entry]" { From 23e24a397d049fb694cdbb3e9715611e5c1cd422 Mon Sep 17 00:00:00 2001 From: Kasper Seweryn Date: Sun, 12 Jan 2020 20:52:29 +0100 Subject: [PATCH 16/44] Split main.rs into more chunks --- src/config.rs | 63 +++++++++ src/desktop_entry.rs | 174 ++++++++++++++++++++++++ src/main.rs | 314 +++---------------------------------------- src/resource.rs | 13 ++ src/style.rs | 52 +++++++ 5 files changed, 318 insertions(+), 298 deletions(-) create mode 100644 src/config.rs create mode 100644 src/desktop_entry.rs create mode 100644 src/resource.rs create mode 100644 src/style.rs diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..9a557ac --- /dev/null +++ b/src/config.rs @@ -0,0 +1,63 @@ +use once_cell::sync::OnceCell; +use serde::Deserialize; +use std::path::PathBuf; +use std::fs; + +use super::resource::Resource; + +static CONFIG: OnceCell = OnceCell::new(); + +#[derive(Deserialize)] +pub struct Config { + pub columns: u8, + pub search_prompt: String, + pub enable_pywal: bool, +} + +impl Config { + pub fn create_dir () { + if let Some(mut config_dir) = dirs::config_dir() { + config_dir.push("waffy"); + if !config_dir.exists() { + let _ = fs::create_dir_all(config_dir); + } + } + } + + pub fn default (path_to_save: Option) -> Self { +// let file = Resource::get("default_config.json5").unwrap(); +// let content = String::from_utf8(file.as_ref().to_vec()) + let content = Resource::from_file("default_config.json5") + .expect("Cannot read default config"); + + let config = json5::from_str::(&content).expect("Cannot parse default config"); + if let Some(path) = path_to_save { + let _ = fs::write(path, &content); + } + + config + } + + pub fn get_uncached () -> Self { + if let Some(mut config_path) = dirs::config_dir() { + config_path.push("waffy"); + config_path.push("config"); + + if !config_path.exists() { + return Self::default(Some(config_path)); + } + + let content = fs::read_to_string(config_path).expect("Could not read config"); + let config = json5::from_str::(&content).expect("Could not parse config"); + return config; + } + + Self::default(None) + } + + + pub fn get () -> &'static Self { + CONFIG.get_or_init(Self::get_uncached) + } +} + diff --git a/src/desktop_entry.rs b/src/desktop_entry.rs new file mode 100644 index 0000000..6fca37a --- /dev/null +++ b/src/desktop_entry.rs @@ -0,0 +1,174 @@ +use std::path::PathBuf; +use std::{fs, io}; +use gtk::{Label, Image, IconTheme, ImageExt, IconThemeExt}; + +use super::grid::GridButton; + +pub struct DesktopEntry { + pub name: String, + pub display_name: String, + pub icon_path: Option, +} + +impl DesktopEntry { + pub fn empty () -> Self { + Self { + name: String::from(""), + display_name: String::from(""), + icon_path: None, + } + } + + pub fn from_path (path: PathBuf) -> io::Result> { + let content = fs::read_to_string(path)?; + + let mut entry = Self::empty(); + let mut entry_section = false; + + for line in content.lines() { + let mut chars = line.chars(); + + if let Some(first_char) = chars.nth(0) { + if first_char == '\0' || first_char == '#' { + continue; + } + } + + if entry_section { + if let Some(first_char) = chars.nth(0) { + if first_char == '[' { + break; + } + } + + let split = line.splitn(2, "=").map(String::from).collect::>(); + + let key = match split.get(0) { + Some(key) => key, + None => continue, + }; + + let value = match split.get(1) { + Some(value) => value, + None => continue, + }; + + if (key == "NoDisplay" || key == "Hidden") && value == "true" { + return Ok(None); + } + + if key == "Name" { + entry.set_name(value); + continue; + } + + if key == "Icon" { + entry.set_icon(value); + continue; + } + } else if line == "[Desktop Entry]" { + entry_section = true; + } + } + + Ok(Some(entry)) + } + + pub fn get_dirs () -> Vec { + let mut dirs: Vec = vec!["/usr/share/applications".into(), "/usr/local/share/applications".into()]; + + if let Some(user_dir) = dirs::home_dir() { + dirs.push(user_dir.join(".local/share/applications/")); + } + + dirs + } + + pub fn get_all () -> Vec { + let mut desktop_entries: Vec = Vec::with_capacity(10); + + for dir in Self::get_dirs() { + if let Ok(read_dir) = fs::read_dir(dir) { + for entry in read_dir { + if let Ok(entry) = entry { + let path = entry.path(); + let extension = path.extension(); + + match extension { + Some(extension) => { + if extension != "desktop" { + continue; + } + } + None => continue, + } + + if let Ok(desktop_entry) = Self::from_path(path) { + if let Some(desktop_entry) = desktop_entry { + desktop_entries.push(desktop_entry); + } + } + } + } + } + } + + desktop_entries + } + + pub fn set_name> (&mut self, name: S) { + self.name = name.into(); + self.display_name = self.name.clone(); + } + + pub fn set_icon> (&mut self, icon: S) { + self.icon_path = Some(icon.into()); + } +} + +impl GridButton for DesktopEntry { + fn label(&self) -> &String { + &self.name + } + + fn display_label(&self) -> Label { + Label::new(Some(&self.display_name)) + } + + fn icon(&self) -> Image { + if self.icon_path == None { + // gtk::IconSize::Dialog is 48x48 + return Image::new_from_icon_name(Some("application-x-executable"), gtk::IconSize::Dialog); + } + + let icon = self.icon_path.as_ref().unwrap(); + if PathBuf::from(&icon).exists() { + let image = Image::new_from_file(&icon); + + // Scale down to 48x48 + if let Some(pixbuf) = image.get_pixbuf() { + if let Some(pixbuf) = pixbuf.scale_simple(48, 48, gdk_pixbuf::InterpType::Tiles) { + return Image::new_from_pixbuf(Some(&pixbuf)); + } + } + } + + if let Some(theme) = IconTheme::get_default() { + let info = theme.load_icon(&icon, 48, gtk::IconLookupFlags::FORCE_SIZE); + + if let Ok(info) = info { + if let Some(pixbuf) = info { + return Image::new_from_pixbuf(Some(&pixbuf)); + } + } + } + + // gtk::IconSize::Dialog is 48x48 + Image::new_from_icon_name(Some("application-x-executable"), gtk::IconSize::Dialog) + } + + fn set_display_label(&mut self, label: String) { + self.display_name = label; + } +} + diff --git a/src/main.rs b/src/main.rs index 19e3fcc..e0c6c6f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,39 +1,27 @@ -use std::fs; -use std::io; -use std::path::PathBuf; -use once_cell::sync::OnceCell; - use gdk::*; use gtk::{ Align, BoxExt, ContainerExt, CssProvider, CssProviderExt, GridExt, GtkWindowExt, Label, LabelExt, Orientation, StyleContext, StyleContextExt, TextView, WidgetExt, Widget, - IconTheme, Image, ImageExt, IconThemeExt + IconTheme, Image, ImageExt, IconThemeExt, }; use gtk_layer_shell_rs::*; -use rust_embed::RustEmbed; -use serde::Deserialize; - mod grid; -use grid::GridButton; +mod style; +mod config; +mod resource; -fn main() { - // Create config dir - if let Some(mut config_dir) = dirs::config_dir() { - config_dir.push("waffy"); - if !config_dir.exists() { - let _ = fs::create_dir_all(config_dir); - } - } +mod desktop_entry; +use desktop_entry::DesktopEntry; + +use config::Config; - const COL_PADDING: u32 = 17; - let config_wal = 0; - let config_columns = 4; - let config_prompt = "run:"; +fn main() { + Config::create_dir(); let config = Config::get(); - let desktop_entries = find_desktop_entries(); + let desktop_entries = DesktopEntry::get_all(); gtk::init().expect("Could not init GTK!"); @@ -54,15 +42,15 @@ fn main() { let search_box = gtk::Box::new(Orientation::Horizontal, 0); layout.pack_start(&search_box, false, false, 0); - let grid_width = 270 * config_columns + (config_columns + 2) * COL_PADDING; + let grid_width = 270 * 4 + (4 + - 1) * 17; let spacer = gtk::Box::new(Orientation::Horizontal, 0); - let width = get_monitor_width() - (grid_width as i32) / 2; + let width = get_monitor_width() / 2 - (grid_width as i32) / 2; spacer.set_size_request(width, 1); search_box.pack_start(&spacer, false, false, 0); let search_label = Label::new(None); - search_label.set_markup(config_prompt); + search_label.set_markup(config.search_prompt.as_str()); search_box.pack_start(&search_label, false, false, 0); add_class(&search_label, "textview-label"); @@ -77,25 +65,13 @@ fn main() { layout.pack_start(&app_grid.window, true, true, 0); - for i in 0..config_columns { -// app_grid.insert_column(i as i32); - } - -// app_grid.set_column_spacing(COL_PADDING); -// app_grid.set_row_spacing(COL_PADDING); - // app_grid.set_name("apps"); - -// app_grid.set_halign(Align::Center); - -// update_apps(&desktop_entries); - let css_provider = CssProvider::new(); - if config_wal == 1 { + if config.enable_pywal == true { add_class(&window, "pywal"); } - let css = get_css(); + let css = style::get_css(); if let Ok(_) = css_provider.load_from_data(css.as_ref()) { StyleContext::add_provider_for_screen( @@ -120,264 +96,6 @@ fn main() { gtk::main(); } -#[derive(RustEmbed)] -#[folder = "res/"] -struct Resource; - -#[derive(Deserialize)] -struct Config { - columns: u8, - search_prompt: String, - enable_pywal: bool, -} - -fn get_default_css(path_to_save: Option) -> String { - let file = Resource::get("default_style.css").unwrap(); - let content = String::from_utf8(file.as_ref().to_vec()).expect("Cannot read default style"); - - if let Some(path) = path_to_save { - let _ = fs::write(path, &content); - } - - content -} - -#[derive(Debug)] -struct DesktopEntry { - name: String, - display_name: String, - icon_path: Option, -} - -impl DesktopEntry { - pub fn empty() -> Self { - Self { - name: String::from(""), - display_name: String::from(""), - icon_path: None, - } - } - - pub fn set_name>(&mut self, name: S) { - self.name = name.into(); - self.display_name = self.name.clone(); - } - - pub fn set_icon>(&mut self, icon: S) { - self.icon_path = Some(icon.into()); - } -} - -impl GridButton for DesktopEntry { - fn label(&self) -> &String { - &self.name - } - - fn display_label(&self) -> Label { - Label::new(Some(&self.display_name)) - } - - fn icon(&self) -> Image { - if self.icon_path == None { - // gtk::IconSize::Dialog is 48x48 - return Image::new_from_icon_name(Some("application-x-executable"), gtk::IconSize::Dialog); - } - - let icon = self.icon_path.as_ref().unwrap(); - if PathBuf::from(&icon).exists() { - let image = Image::new_from_file(&icon); - - // Scale down to 48x48 - if let Some(pixbuf) = image.get_pixbuf() { - if let Some(pixbuf) = pixbuf.scale_simple(48, 48, gdk_pixbuf::InterpType::Tiles) { - return Image::new_from_pixbuf(Some(&pixbuf)); - } - } - } - - if let Some(theme) = IconTheme::get_default() { - let info = theme.load_icon(&icon, 48, gtk::IconLookupFlags::FORCE_SIZE); - - if let Ok(info) = info { - if let Some(pixbuf) = info { - return Image::new_from_pixbuf(Some(&pixbuf)); - } - } - } - - // gtk::IconSize::Dialog is 48x48 - Image::new_from_icon_name(Some("application-x-executable"), gtk::IconSize::Dialog) - } - - fn set_display_label(&mut self, label: String) { - self.display_name = label; - } -} - -fn find_desktop_entries() -> Vec { - let mut desktop_entry_dirs: Vec = vec!["/usr/share/applications".into(), "/usr/local/share/applications".into()]; - - if let Some(user_dir) = dirs::home_dir() { - desktop_entry_dirs.push(user_dir.join(".local/share/applications/")); - } - - let mut desktop_entries: Vec = Vec::with_capacity(10); - - for dir in desktop_entry_dirs { - if let Ok(read_dir) = fs::read_dir(dir) { - for entry in read_dir { - if let Ok(entry) = entry { - let path = entry.path(); - let extension = path.extension(); - - match extension { - Some(extension) => { - if extension != "desktop" { - continue; - } - } - None => continue, - } - - if let Ok(desktop_entry) = parse_desktop_file(path) { - if let Some(desktop_entry) = desktop_entry { - desktop_entries.push(desktop_entry); - } - } - } - } - } - } - - desktop_entries -} - -fn parse_desktop_file(path: PathBuf) -> io::Result> { - let content = fs::read_to_string(path)?; - - let mut entry = DesktopEntry::empty(); - let mut entry_section = false; - - for line in content.lines() { - let mut chars = line.chars(); - - if let Some(first_char) = chars.nth(0) { - if first_char == '\0' || first_char == '#' { - continue; - } - } - - if entry_section { - if let Some(first_char) = chars.nth(0) { - if first_char == '[' { - break; - } - } - - let split = line.splitn(2, "=").map(String::from).collect::>(); - - let key = match split.get(0) { - Some(key) => key, - None => continue, - }; - let value = match split.get(1) { - Some(value) => value, - None => continue, - }; - - if key == "NoDisplay" && value == "true" { - return Ok(None); - } - - if key == "Name" { - entry.set_name(value); - continue; - } - - if key == "Icon" { - entry.set_icon(value); - continue; - } - } else if line == "[Desktop Entry]" { - entry_section = true; - } - } - - Ok(Some(entry)) -} - -static CONFIG: OnceCell = OnceCell::new(); - -impl Config { - fn default (path_to_save: Option) -> Self { - let file = Resource::get("default_config.json5").unwrap(); - let content = String::from_utf8(file.as_ref().to_vec()).expect("Cannot read default config"); - let config = json5::from_str::(&content).expect("Cannot parse default config"); - - if let Some(path) = path_to_save { - let _ = fs::write(path, &content); - } - - config - } - - fn get_uncached () -> Self { - if let Some(mut config_path) = dirs::config_dir() { - config_path.push("waffy"); - config_path.push("config"); - - if !config_path.exists() { - return Self::default(Some(config_path)); - } - - let content = fs::read_to_string(config_path).expect("Could not read config"); - let config = json5::from_str::(&content).expect("Could not parse config"); - return config; - } - - Self::default(None) - } - - - fn get () -> &'static Self { - CONFIG.get_or_init(Self::get_uncached) - } -} - -fn get_css() -> String { - let mut content = String::from(""); - if let Some(mut style_path) = dirs::config_dir() { - style_path.push("waffy"); - style_path.push("style.css"); - - if style_path.exists() { - content = fs::read_to_string(style_path).expect("Could not read config"); - } else { - content = get_default_css(Some(style_path)) - } - } - - if content == "" { - content = get_default_css(None); - } - - // TODO: Check config option - if let Some(mut pywal_path) = dirs::cache_dir() { - pywal_path.push("wal"); - pywal_path.push("colors-waybar.css"); - - if pywal_path.exists() { - let mut pywal_content = fs::read_to_string(pywal_path).expect("Could not read config"); - pywal_content.push_str(content.as_str()); - content = pywal_content; - } - } - - content -} - -fn update_apps(apps: &Vec) {} - fn get_monitor_width() -> i32 { return 1920; } diff --git a/src/resource.rs b/src/resource.rs new file mode 100644 index 0000000..7112f9b --- /dev/null +++ b/src/resource.rs @@ -0,0 +1,13 @@ +use rust_embed::RustEmbed; +use std::string::FromUtf8Error; + +#[derive(RustEmbed)] +#[folder = "res/"] +pub struct Resource; + +impl Resource { + pub fn from_file (filename: &str) -> Result { + let file = Self::get(filename).unwrap(); + String::from_utf8(file.as_ref().to_vec()) + } +} diff --git a/src/style.rs b/src/style.rs new file mode 100644 index 0000000..e0c3d71 --- /dev/null +++ b/src/style.rs @@ -0,0 +1,52 @@ +use std::path::PathBuf; +use std::fs; + +use super::resource::Resource; + +pub fn get_default_css(path_to_save: Option) -> String { +// let file = Resource::get("default_style.css").unwrap(); +// let content = String::from_utf8(file.as_ref().to_vec()) + + let content = Resource::from_file("default_style.css") + .expect("Cannot read default style"); + + + if let Some(path) = path_to_save { + let _ = fs::write(path, &content); + } + + content +} + +pub fn get_css() -> String { + let mut content = String::from(""); + if let Some(mut style_path) = dirs::config_dir() { + style_path.push("waffy"); + style_path.push("style.css"); + + if style_path.exists() { + content = fs::read_to_string(style_path).expect("Could not read config"); + } else { + content = get_default_css(Some(style_path)) + } + } + + if content == "" { + content = get_default_css(None); + } + + // TODO: Check config option + if let Some(mut pywal_path) = dirs::cache_dir() { + pywal_path.push("wal"); + pywal_path.push("colors-waybar.css"); + + if pywal_path.exists() { + let mut pywal_content = fs::read_to_string(pywal_path).expect("Could not read config"); + pywal_content.push_str(content.as_str()); + content = pywal_content; + } + } + + content +} + From 617a83f1c7c5743d7a72c27c3c011f3d9bfcb57d Mon Sep 17 00:00:00 2001 From: Kasper Seweryn Date: Sun, 12 Jan 2020 21:07:38 +0100 Subject: [PATCH 17/44] Fix desktop entry parser not breaking on `[` --- src/desktop_entry.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/desktop_entry.rs b/src/desktop_entry.rs index 6fca37a..06e3f3c 100644 --- a/src/desktop_entry.rs +++ b/src/desktop_entry.rs @@ -19,7 +19,7 @@ impl DesktopEntry { } } - pub fn from_path (path: PathBuf) -> io::Result> { + pub fn from_file(path: PathBuf) -> io::Result> { let content = fs::read_to_string(path)?; let mut entry = Self::empty(); @@ -27,16 +27,17 @@ impl DesktopEntry { for line in content.lines() { let mut chars = line.chars(); + let first_char = chars.nth(0); - if let Some(first_char) = chars.nth(0) { - if first_char == '\0' || first_char == '#' { + if let Some(chr) = first_char { + if chr == '\0' || chr == '#' { continue; } } if entry_section { - if let Some(first_char) = chars.nth(0) { - if first_char == '[' { + if let Some(chr) = first_char { + if chr == '[' { break; } } @@ -103,7 +104,7 @@ impl DesktopEntry { None => continue, } - if let Ok(desktop_entry) = Self::from_path(path) { + if let Ok(desktop_entry) = Self::from_file(path) { if let Some(desktop_entry) = desktop_entry { desktop_entries.push(desktop_entry); } From 0e026ffb734103ef9ea7b5afbece3d279829ca86 Mon Sep 17 00:00:00 2001 From: Kasper Seweryn Date: Sun, 12 Jan 2020 21:30:27 +0100 Subject: [PATCH 18/44] Connect click event --- src/grid.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/grid.rs b/src/grid.rs index 0b698f6..acdf106 100644 --- a/src/grid.rs +++ b/src/grid.rs @@ -1,6 +1,6 @@ use gtk::{ Grid as GtkGrid, ScrolledWindow as GtkWindow, Viewport as GtkViewport, Widget as GtkWidget, - GridExt, ViewportExt, ScrolledWindowExt, ContainerExt, WidgetExt, LabelExt, + GridExt, ViewportExt, ScrolledWindowExt, ContainerExt, WidgetExt, LabelExt, ButtonExt, }; use sublime_fuzzy::{best_match as fuzzy_match, format_simple as fuzzy_format}; @@ -97,6 +97,10 @@ impl Grid { label.set_ellipsize(pango::EllipsizeMode::End); } + widget.connect_clicked(|_| { + (self.click_callback)(&item); + }); + widget.add(&content); self.grid.attach(&widget, col, row, 1, 1); } From 97218608b9f7abce3c9cad1b483324ff5678a08d Mon Sep 17 00:00:00 2001 From: Kasper Seweryn Date: Sun, 12 Jan 2020 23:59:45 +0100 Subject: [PATCH 19/44] Fix all warnings --- src/grid.rs | 13 ++++++++----- src/main.rs | 5 ++--- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/grid.rs b/src/grid.rs index acdf106..0239ae8 100644 --- a/src/grid.rs +++ b/src/grid.rs @@ -1,8 +1,11 @@ use gtk::{ - Grid as GtkGrid, ScrolledWindow as GtkWindow, Viewport as GtkViewport, Widget as GtkWidget, - GridExt, ViewportExt, ScrolledWindowExt, ContainerExt, WidgetExt, LabelExt, ButtonExt, + Grid as GtkGrid, ScrolledWindow as GtkWindow, Viewport as GtkViewport, + GridExt, ContainerExt, WidgetExt, LabelExt, ButtonExt, +}; +use sublime_fuzzy::{ + best_match as fuzzy_match, + // format_simple as fuzzy_format }; -use sublime_fuzzy::{best_match as fuzzy_match, format_simple as fuzzy_format}; use super::Config; @@ -20,13 +23,13 @@ pub struct Grid { items: Vec, filter_string: String, flags: u32, - click_callback: Box, + click_callback: Box, pub window: GtkWindow, grid: GtkGrid, } impl Grid { - pub fn new (items: Vec, flags: u32, click_callback: Box) -> Self { + pub fn new (items: Vec, flags: u32, click_callback: Box) -> Self { let adjustment = None::<>k::Adjustment>; let window = GtkWindow::new(adjustment, adjustment); let viewport = GtkViewport::new(adjustment, adjustment); diff --git a/src/main.rs b/src/main.rs index e0c6c6f..380957c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,7 @@ use gdk::*; use gtk::{ - Align, BoxExt, ContainerExt, CssProvider, CssProviderExt, GridExt, GtkWindowExt, Label, - LabelExt, Orientation, StyleContext, StyleContextExt, TextView, WidgetExt, Widget, - IconTheme, Image, ImageExt, IconThemeExt, + BoxExt, ContainerExt, CssProvider, CssProviderExt, GtkWindowExt, Label, + LabelExt, Orientation, StyleContext, StyleContextExt, TextView, WidgetExt, }; use gtk_layer_shell_rs::*; From 54e160e8dce890e08f0b028874edc90c76eb92f8 Mon Sep 17 00:00:00 2001 From: Kasper Seweryn Date: Mon, 13 Jan 2020 11:03:44 +0100 Subject: [PATCH 20/44] Add another trail to connect events --- src/grid.rs | 85 +++++++++++++++++++++++++++-------------------------- src/main.rs | 3 +- 2 files changed, 46 insertions(+), 42 deletions(-) diff --git a/src/grid.rs b/src/grid.rs index 0239ae8..f0f8457 100644 --- a/src/grid.rs +++ b/src/grid.rs @@ -19,17 +19,15 @@ pub trait GridButton { fn set_display_label(&mut self, label: String); } -pub struct Grid { - items: Vec, +pub struct Grid { + items: Vec, filter_string: String, - flags: u32, - click_callback: Box, pub window: GtkWindow, grid: GtkGrid, } -impl Grid { - pub fn new (items: Vec, flags: u32, click_callback: Box) -> Self { +impl Grid { + pub fn new (items: Vec, flags: u32, click_callback: Box) -> Self { let adjustment = None::<>k::Adjustment>; let window = GtkWindow::new(adjustment, adjustment); let viewport = GtkViewport::new(adjustment, adjustment); @@ -49,49 +47,25 @@ impl Grid { window.add(&viewport); viewport.add(&grid); - Self { items, filter_string: String::from(""), flags, click_callback, window, grid } - } - - pub fn filter (&mut self, needle: String) { - self.filter_string = needle; - self.update(); - } - - pub fn update (&mut self) { - self.grid.foreach(|child| self.grid.remove(child)); - - let config = Config::get(); - let columns = config.columns as i32; - - let mut filtered: Vec<&T> = Vec::new(); - for item in self.items.iter() { - if self.filter_string == "" { - filtered.push(item); - continue; - } + let mut buttons: Vec = Vec::new(); - let label = item.label(); - if let Some(res) = fuzzy_match(self.filter_string.as_str(), label.as_str()) { -// item.set_display_label(fuzzy_format(&res, label.as_str(), "", "")); - filtered.push(item); - } - } - - for (i, item) in filtered.into_iter().enumerate() { - let col = i as i32 % columns; - let row = i as i32 / columns; + let mut i = 0; + for item in items { + let col = i as i32 % config.columns as i32; + let row = i as i32 / config.columns as i32; + i += 1; let widget = gtk::Button::new(); let content = gtk::Grid::new(); content.set_column_spacing(17); - if self.flags & SHOW_ICON > 0 { + if flags & SHOW_ICON > 0 { content.insert_column(0); content.attach(&item.icon(), 0, 0, 1, 1); } - if self.flags & SHOW_LABEL > 0 { + if flags & SHOW_LABEL > 0 { let label = item.display_label(); content.insert_column(1); content.attach(&label, 1, 0, 1, 1); @@ -100,14 +74,43 @@ impl Grid { label.set_ellipsize(pango::EllipsizeMode::End); } - widget.connect_clicked(|_| { - (self.click_callback)(&item); + widget.connect_clicked(|widget| { + (click_callback)(item); }); widget.add(&content); - self.grid.attach(&widget, col, row, 1, 1); + grid.attach(&widget, col, row, 1, 1); + buttons.push(widget); } + Self { + items: buttons, + filter_string: String::from(""), + window, grid, + } + } + + pub fn filter (&mut self, needle: String) { + self.filter_string = needle; + self.update(); + } + + pub fn update (&mut self) { + self.grid.foreach(|child| self.grid.remove(child)); +// +// for item in self.items.iter() { +// if self.filter_string == "" { +// filtered.push(item); +// continue; +// } +// +// let label = item.label(); +// if let Some(res) = fuzzy_match(self.filter_string.as_str(), label.as_str()) { +//// item.set_display_label(fuzzy_format(&res, label.as_str(), "", "")); +// filtered.push(item); +// } +// } +// self.grid.show_all(); } } diff --git a/src/main.rs b/src/main.rs index 380957c..0642ff7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -62,6 +62,7 @@ fn main() { println!("{:?}", entry.display_name); })); + layout.pack_start(&app_grid.window, true, true, 0); let css_provider = CssProvider::new(); @@ -90,7 +91,7 @@ fn main() { window.set_title("waffy"); window.show_all(); - app_grid.update(); +// app_grid.update(); gtk::main(); } From 67ab3d3b0577995a55ef2b586d764f29b180a051 Mon Sep 17 00:00:00 2001 From: Kasper Seweryn Date: Mon, 13 Jan 2020 11:03:44 +0100 Subject: [PATCH 21/44] Add another trail to connect events Fixes #11 --- src/grid.rs | 85 +++++++++++++++++++++++++++-------------------------- src/main.rs | 3 +- 2 files changed, 46 insertions(+), 42 deletions(-) diff --git a/src/grid.rs b/src/grid.rs index 0239ae8..f0f8457 100644 --- a/src/grid.rs +++ b/src/grid.rs @@ -19,17 +19,15 @@ pub trait GridButton { fn set_display_label(&mut self, label: String); } -pub struct Grid { - items: Vec, +pub struct Grid { + items: Vec, filter_string: String, - flags: u32, - click_callback: Box, pub window: GtkWindow, grid: GtkGrid, } -impl Grid { - pub fn new (items: Vec, flags: u32, click_callback: Box) -> Self { +impl Grid { + pub fn new (items: Vec, flags: u32, click_callback: Box) -> Self { let adjustment = None::<>k::Adjustment>; let window = GtkWindow::new(adjustment, adjustment); let viewport = GtkViewport::new(adjustment, adjustment); @@ -49,49 +47,25 @@ impl Grid { window.add(&viewport); viewport.add(&grid); - Self { items, filter_string: String::from(""), flags, click_callback, window, grid } - } - - pub fn filter (&mut self, needle: String) { - self.filter_string = needle; - self.update(); - } - - pub fn update (&mut self) { - self.grid.foreach(|child| self.grid.remove(child)); - - let config = Config::get(); - let columns = config.columns as i32; - - let mut filtered: Vec<&T> = Vec::new(); - for item in self.items.iter() { - if self.filter_string == "" { - filtered.push(item); - continue; - } + let mut buttons: Vec = Vec::new(); - let label = item.label(); - if let Some(res) = fuzzy_match(self.filter_string.as_str(), label.as_str()) { -// item.set_display_label(fuzzy_format(&res, label.as_str(), "", "")); - filtered.push(item); - } - } - - for (i, item) in filtered.into_iter().enumerate() { - let col = i as i32 % columns; - let row = i as i32 / columns; + let mut i = 0; + for item in items { + let col = i as i32 % config.columns as i32; + let row = i as i32 / config.columns as i32; + i += 1; let widget = gtk::Button::new(); let content = gtk::Grid::new(); content.set_column_spacing(17); - if self.flags & SHOW_ICON > 0 { + if flags & SHOW_ICON > 0 { content.insert_column(0); content.attach(&item.icon(), 0, 0, 1, 1); } - if self.flags & SHOW_LABEL > 0 { + if flags & SHOW_LABEL > 0 { let label = item.display_label(); content.insert_column(1); content.attach(&label, 1, 0, 1, 1); @@ -100,14 +74,43 @@ impl Grid { label.set_ellipsize(pango::EllipsizeMode::End); } - widget.connect_clicked(|_| { - (self.click_callback)(&item); + widget.connect_clicked(|widget| { + (click_callback)(item); }); widget.add(&content); - self.grid.attach(&widget, col, row, 1, 1); + grid.attach(&widget, col, row, 1, 1); + buttons.push(widget); } + Self { + items: buttons, + filter_string: String::from(""), + window, grid, + } + } + + pub fn filter (&mut self, needle: String) { + self.filter_string = needle; + self.update(); + } + + pub fn update (&mut self) { + self.grid.foreach(|child| self.grid.remove(child)); +// +// for item in self.items.iter() { +// if self.filter_string == "" { +// filtered.push(item); +// continue; +// } +// +// let label = item.label(); +// if let Some(res) = fuzzy_match(self.filter_string.as_str(), label.as_str()) { +//// item.set_display_label(fuzzy_format(&res, label.as_str(), "", "")); +// filtered.push(item); +// } +// } +// self.grid.show_all(); } } diff --git a/src/main.rs b/src/main.rs index 380957c..0642ff7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -62,6 +62,7 @@ fn main() { println!("{:?}", entry.display_name); })); + layout.pack_start(&app_grid.window, true, true, 0); let css_provider = CssProvider::new(); @@ -90,7 +91,7 @@ fn main() { window.set_title("waffy"); window.show_all(); - app_grid.update(); +// app_grid.update(); gtk::main(); } From 76cb3d6646ae167d7c070acc7995f72d41fea481 Mon Sep 17 00:00:00 2001 From: insertt Date: Wed, 15 Jan 2020 22:39:28 +0100 Subject: [PATCH 22/44] Implement button callbacks --- src/config.rs | 20 ++++++-------- src/desktop_entry.rs | 32 ++++++++++++++-------- src/grid.rs | 65 ++++++++++++++++++++++++-------------------- src/main.rs | 31 ++++++++++++++------- src/resource.rs | 2 +- src/style.rs | 11 +++----- 6 files changed, 91 insertions(+), 70 deletions(-) diff --git a/src/config.rs b/src/config.rs index 9a557ac..9d5a7c5 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,7 +1,7 @@ use once_cell::sync::OnceCell; use serde::Deserialize; -use std::path::PathBuf; use std::fs; +use std::path::PathBuf; use super::resource::Resource; @@ -15,7 +15,7 @@ pub struct Config { } impl Config { - pub fn create_dir () { + pub fn create_dir() { if let Some(mut config_dir) = dirs::config_dir() { config_dir.push("waffy"); if !config_dir.exists() { @@ -24,11 +24,11 @@ impl Config { } } - pub fn default (path_to_save: Option) -> Self { -// let file = Resource::get("default_config.json5").unwrap(); -// let content = String::from_utf8(file.as_ref().to_vec()) - let content = Resource::from_file("default_config.json5") - .expect("Cannot read default config"); + pub fn default(path_to_save: Option) -> Self { + // let file = Resource::get("default_config.json5").unwrap(); + // let content = String::from_utf8(file.as_ref().to_vec()) + let content = + Resource::from_file("default_config.json5").expect("Cannot read default config"); let config = json5::from_str::(&content).expect("Cannot parse default config"); if let Some(path) = path_to_save { @@ -38,7 +38,7 @@ impl Config { config } - pub fn get_uncached () -> Self { + pub fn get_uncached() -> Self { if let Some(mut config_path) = dirs::config_dir() { config_path.push("waffy"); config_path.push("config"); @@ -55,9 +55,7 @@ impl Config { Self::default(None) } - - pub fn get () -> &'static Self { + pub fn get() -> &'static Self { CONFIG.get_or_init(Self::get_uncached) } } - diff --git a/src/desktop_entry.rs b/src/desktop_entry.rs index 06e3f3c..e42791d 100644 --- a/src/desktop_entry.rs +++ b/src/desktop_entry.rs @@ -1,6 +1,9 @@ +use std::cell::RefCell; +use std::rc::Rc; + +use gtk::{IconTheme, IconThemeExt, Image, ImageExt, Label}; use std::path::PathBuf; use std::{fs, io}; -use gtk::{Label, Image, IconTheme, ImageExt, IconThemeExt}; use super::grid::GridButton; @@ -11,7 +14,7 @@ pub struct DesktopEntry { } impl DesktopEntry { - pub fn empty () -> Self { + pub fn empty() -> Self { Self { name: String::from(""), display_name: String::from(""), @@ -75,8 +78,11 @@ impl DesktopEntry { Ok(Some(entry)) } - pub fn get_dirs () -> Vec { - let mut dirs: Vec = vec!["/usr/share/applications".into(), "/usr/local/share/applications".into()]; + pub fn get_dirs() -> Vec { + let mut dirs: Vec = vec![ + "/usr/share/applications".into(), + "/usr/local/share/applications".into(), + ]; if let Some(user_dir) = dirs::home_dir() { dirs.push(user_dir.join(".local/share/applications/")); @@ -85,8 +91,8 @@ impl DesktopEntry { dirs } - pub fn get_all () -> Vec { - let mut desktop_entries: Vec = Vec::with_capacity(10); + pub fn get_all() -> Vec>> { + let mut desktop_entries: Vec>> = Vec::with_capacity(10); for dir in Self::get_dirs() { if let Ok(read_dir) = fs::read_dir(dir) { @@ -106,7 +112,7 @@ impl DesktopEntry { if let Ok(desktop_entry) = Self::from_file(path) { if let Some(desktop_entry) = desktop_entry { - desktop_entries.push(desktop_entry); + desktop_entries.push(Rc::new(RefCell::new(desktop_entry))); } } } @@ -117,12 +123,12 @@ impl DesktopEntry { desktop_entries } - pub fn set_name> (&mut self, name: S) { + pub fn set_name>(&mut self, name: S) { self.name = name.into(); self.display_name = self.name.clone(); } - pub fn set_icon> (&mut self, icon: S) { + pub fn set_icon>(&mut self, icon: S) { self.icon_path = Some(icon.into()); } } @@ -139,7 +145,10 @@ impl GridButton for DesktopEntry { fn icon(&self) -> Image { if self.icon_path == None { // gtk::IconSize::Dialog is 48x48 - return Image::new_from_icon_name(Some("application-x-executable"), gtk::IconSize::Dialog); + return Image::new_from_icon_name( + Some("application-x-executable"), + gtk::IconSize::Dialog, + ); } let icon = self.icon_path.as_ref().unwrap(); @@ -158,7 +167,7 @@ impl GridButton for DesktopEntry { let info = theme.load_icon(&icon, 48, gtk::IconLookupFlags::FORCE_SIZE); if let Ok(info) = info { - if let Some(pixbuf) = info { + if let Some(pixbuf) = info { return Image::new_from_pixbuf(Some(&pixbuf)); } } @@ -172,4 +181,3 @@ impl GridButton for DesktopEntry { self.display_name = label; } } - diff --git a/src/grid.rs b/src/grid.rs index f0f8457..0c3b50b 100644 --- a/src/grid.rs +++ b/src/grid.rs @@ -1,15 +1,15 @@ +use std::cell::RefCell; +use std::rc::Rc; + use gtk::{ - Grid as GtkGrid, ScrolledWindow as GtkWindow, Viewport as GtkViewport, - GridExt, ContainerExt, WidgetExt, LabelExt, ButtonExt, -}; -use sublime_fuzzy::{ - best_match as fuzzy_match, - // format_simple as fuzzy_format + ButtonExt, ContainerExt, Grid as GtkGrid, GridExt, LabelExt, ScrolledWindow as GtkWindow, + Viewport as GtkViewport, WidgetExt, }; +use sublime_fuzzy::best_match as fuzzy_match; use super::Config; -pub const SHOW_ICON: u32 = 0b01; +pub const SHOW_ICON: u32 = 0b01; pub const SHOW_LABEL: u32 = 0b10; pub trait GridButton { @@ -27,7 +27,11 @@ pub struct Grid { } impl Grid { - pub fn new (items: Vec, flags: u32, click_callback: Box) -> Self { + pub fn new( + items: Vec>>, + flags: u32, + click_callback: Rc>)>, + ) -> Self { let adjustment = None::<>k::Adjustment>; let window = GtkWindow::new(adjustment, adjustment); let viewport = GtkViewport::new(adjustment, adjustment); @@ -62,11 +66,11 @@ impl Grid { if flags & SHOW_ICON > 0 { content.insert_column(0); - content.attach(&item.icon(), 0, 0, 1, 1); + content.attach(&item.borrow().icon(), 0, 0, 1, 1); } if flags & SHOW_LABEL > 0 { - let label = item.display_label(); + let label = item.borrow().display_label(); content.insert_column(1); content.attach(&label, 1, 0, 1, 1); @@ -74,8 +78,10 @@ impl Grid { label.set_ellipsize(pango::EllipsizeMode::End); } - widget.connect_clicked(|widget| { - (click_callback)(item); + let callback = click_callback.clone(); + + widget.connect_clicked(move |_| { + (callback)(item.clone()); }); widget.add(&content); @@ -86,31 +92,32 @@ impl Grid { Self { items: buttons, filter_string: String::from(""), - window, grid, + window, + grid, } } - pub fn filter (&mut self, needle: String) { + pub fn filter(&mut self, needle: String) { self.filter_string = needle; self.update(); } - pub fn update (&mut self) { + pub fn update(&mut self) { self.grid.foreach(|child| self.grid.remove(child)); -// -// for item in self.items.iter() { -// if self.filter_string == "" { -// filtered.push(item); -// continue; -// } -// -// let label = item.label(); -// if let Some(res) = fuzzy_match(self.filter_string.as_str(), label.as_str()) { -//// item.set_display_label(fuzzy_format(&res, label.as_str(), "", "")); -// filtered.push(item); -// } -// } -// + // + // for item in self.items.iter() { + // if self.filter_string == "" { + // filtered.push(item); + // continue; + // } + // + // let label = item.label(); + // if let Some(res) = fuzzy_match(self.filter_string.as_str(), label.as_str()) { + //// item.set_display_label(fuzzy_format(&res, label.as_str(), "", "")); + // filtered.push(item); + // } + // } + // self.grid.show_all(); } } diff --git a/src/main.rs b/src/main.rs index 0642ff7..33394a6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,21 +1,24 @@ +use crate::grid::GridButton; +use std::cell::RefCell; +use std::rc::Rc; + use gdk::*; use gtk::{ - BoxExt, ContainerExt, CssProvider, CssProviderExt, GtkWindowExt, Label, - LabelExt, Orientation, StyleContext, StyleContextExt, TextView, WidgetExt, + BoxExt, ContainerExt, CssProvider, CssProviderExt, GtkWindowExt, Label, LabelExt, Orientation, + StyleContext, StyleContextExt, TextView, WidgetExt, }; use gtk_layer_shell_rs::*; -mod grid; -mod style; mod config; +mod grid; mod resource; +mod style; mod desktop_entry; use desktop_entry::DesktopEntry; use config::Config; - fn main() { Config::create_dir(); @@ -41,7 +44,7 @@ fn main() { let search_box = gtk::Box::new(Orientation::Horizontal, 0); layout.pack_start(&search_box, false, false, 0); - let grid_width = 270 * 4 + (4 + - 1) * 17; + let grid_width = 270 * 4 + (4 + -1) * 17; let spacer = gtk::Box::new(Orientation::Horizontal, 0); let width = get_monitor_width() / 2 - (grid_width as i32) / 2; spacer.set_size_request(width, 1); @@ -58,10 +61,18 @@ fn main() { // search_input.set_name("search"); search_box.pack_start(&search_input, true, true, 0); - let mut app_grid = grid::Grid::::new(desktop_entries, grid::SHOW_ICON | grid::SHOW_LABEL, Box::new(|entry| { - println!("{:?}", entry.display_name); - })); + let buttons = desktop_entries + .into_iter() + .map(|entry| entry as Rc>) + .collect::>(); + let mut app_grid = grid::Grid::new( + buttons, + grid::SHOW_ICON | grid::SHOW_LABEL, + Rc::new(|entry| { + println!("{:?}", entry.borrow().label()); + }), + ); layout.pack_start(&app_grid.window, true, true, 0); @@ -91,7 +102,7 @@ fn main() { window.set_title("waffy"); window.show_all(); -// app_grid.update(); + // app_grid.update(); gtk::main(); } diff --git a/src/resource.rs b/src/resource.rs index 7112f9b..72d669a 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -6,7 +6,7 @@ use std::string::FromUtf8Error; pub struct Resource; impl Resource { - pub fn from_file (filename: &str) -> Result { + pub fn from_file(filename: &str) -> Result { let file = Self::get(filename).unwrap(); String::from_utf8(file.as_ref().to_vec()) } diff --git a/src/style.rs b/src/style.rs index e0c3d71..297f85a 100644 --- a/src/style.rs +++ b/src/style.rs @@ -1,15 +1,13 @@ -use std::path::PathBuf; use std::fs; +use std::path::PathBuf; use super::resource::Resource; pub fn get_default_css(path_to_save: Option) -> String { -// let file = Resource::get("default_style.css").unwrap(); -// let content = String::from_utf8(file.as_ref().to_vec()) - - let content = Resource::from_file("default_style.css") - .expect("Cannot read default style"); + // let file = Resource::get("default_style.css").unwrap(); + // let content = String::from_utf8(file.as_ref().to_vec()) + let content = Resource::from_file("default_style.css").expect("Cannot read default style"); if let Some(path) = path_to_save { let _ = fs::write(path, &content); @@ -49,4 +47,3 @@ pub fn get_css() -> String { content } - From 3342e85697896772e7fa637fe0f27f2b464d2bf0 Mon Sep 17 00:00:00 2001 From: insertt Date: Wed, 15 Jan 2020 23:09:08 +0100 Subject: [PATCH 23/44] Implement simple key press handler --- src/main.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index 33394a6..2dc619d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,8 +4,8 @@ use std::rc::Rc; use gdk::*; use gtk::{ - BoxExt, ContainerExt, CssProvider, CssProviderExt, GtkWindowExt, Label, LabelExt, Orientation, - StyleContext, StyleContextExt, TextView, WidgetExt, + BoxExt, ContainerExt, CssProvider, CssProviderExt, GtkWindowExt, Inhibit, Label, LabelExt, + Orientation, StyleContext, StyleContextExt, TextView, WidgetExt, }; use gtk_layer_shell_rs::*; @@ -35,6 +35,18 @@ fn main() { set_anchor(&window, Edge::Right, true); set_anchor(&window, Edge::Bottom, true); + window.connect_key_release_event(|window, key| { + use gdk::enums::key; + + match key.get_keyval() { + key::Escape => { + window.close(); + std::process::exit(0) + } + _ => Inhibit(true), + } + }); + window.set_resizable(false); window.set_decorated(false); From 67301135c787d9f5029a6c35c87590ce48047aca Mon Sep 17 00:00:00 2001 From: Kasper Seweryn Date: Thu, 16 Jan 2020 02:18:38 +0100 Subject: [PATCH 24/44] Add license and readme back --- LICENSE | 21 +++++++++++++++++++++ README.md | 22 ++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 LICENSE create mode 100644 README.md diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d587413 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Kasper Seweryn + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..2f6e54f --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +# waffy +Wayland compatible, touch friendly application launcher + +![waffy-screenshot](https://i.imgur.com/iyEQVqo.png) + +## Features +- wlroots dedicated launcher +- Touch support +- Read all application locations +- Respect hidden apps +- Fuzzy find apps + +## Requirements +- [gtk-layer-shell](https://github.com/wmww/gtk-layer-shell) - A library to write GTK applications that use Layer Shell + +## Building +### Build with git and cmake +```shell script +git clone https://github.com/wvffle/waffy.git +cd waffy +cargo build --release +``` From b5c19e4e84851eb7dd1233bf8bb0c18282238b0a Mon Sep 17 00:00:00 2001 From: Kasper Seweryn Date: Thu, 16 Jan 2020 02:32:15 +0100 Subject: [PATCH 25/44] Enable keyboard interactivity by default (for debug purposes) --- src/main.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main.rs b/src/main.rs index 2dc619d..3fc88f2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -34,6 +34,8 @@ fn main() { set_anchor(&window, Edge::Left, true); set_anchor(&window, Edge::Right, true); set_anchor(&window, Edge::Bottom, true); + set_keyboard_interactivity(&window, true); + window.connect_key_release_event(|window, key| { use gdk::enums::key; From 92114a45976a48577489a8da2a2e8af2f9d551a7 Mon Sep 17 00:00:00 2001 From: Kasper Seweryn Date: Thu, 16 Jan 2020 02:32:55 +0100 Subject: [PATCH 26/44] Add enter/leave events to buttons (needs fix) --- src/grid.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/grid.rs b/src/grid.rs index 0c3b50b..f252d24 100644 --- a/src/grid.rs +++ b/src/grid.rs @@ -3,7 +3,7 @@ use std::rc::Rc; use gtk::{ ButtonExt, ContainerExt, Grid as GtkGrid, GridExt, LabelExt, ScrolledWindow as GtkWindow, - Viewport as GtkViewport, WidgetExt, + Viewport as GtkViewport, WidgetExt, StyleContextExt }; use sublime_fuzzy::best_match as fuzzy_match; @@ -84,6 +84,18 @@ impl Grid { (callback)(item.clone()); }); + widget.connect_enter_notify_event(move |widget, _| { + let ctx = widget.get_style_context(); + ctx.add_class("active"); + gtk::Inhibit(true) + }); + + widget.connect_leave_notify_event(move |widget, _| { + let ctx = widget.get_style_context(); + ctx.remove_class("active"); + gtk::Inhibit(true) + }); + widget.add(&content); grid.attach(&widget, col, row, 1, 1); buttons.push(widget); From ba809d4dd824e141cc713cbb953ae12b1f1868b1 Mon Sep 17 00:00:00 2001 From: Kasper Seweryn Date: Thu, 16 Jan 2020 02:48:15 +0100 Subject: [PATCH 27/44] Add proper keyboard interactivity switch --- src/main.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index 3fc88f2..275277e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -34,21 +34,32 @@ fn main() { set_anchor(&window, Edge::Left, true); set_anchor(&window, Edge::Right, true); set_anchor(&window, Edge::Bottom, true); - set_keyboard_interactivity(&window, true); - window.connect_key_release_event(|window, key| { use gdk::enums::key; match key.get_keyval() { key::Escape => { - window.close(); + window.destroy(); std::process::exit(0) } _ => Inhibit(true), } }); + window.connect_enter_notify_event(|window, event| { + set_keyboard_interactivity(window, true); + Inhibit(false) + }); + + window.connect_leave_notify_event(|window, event| { + if event.get_detail() == gdk::NotifyType::NonlinearVirtual { + set_keyboard_interactivity(window, false); + } + + Inhibit(false) + }); + window.set_resizable(false); window.set_decorated(false); From 8f6b0555331d1b19baea46eb76eaf839256ce69e Mon Sep 17 00:00:00 2001 From: insertt Date: Thu, 16 Jan 2020 10:04:07 +0100 Subject: [PATCH 28/44] Code cleanup --- src/desktop_entry.rs | 4 ++-- src/grid.rs | 4 +++- src/main.rs | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/desktop_entry.rs b/src/desktop_entry.rs index e42791d..7e184f6 100644 --- a/src/desktop_entry.rs +++ b/src/desktop_entry.rs @@ -30,7 +30,7 @@ impl DesktopEntry { for line in content.lines() { let mut chars = line.chars(); - let first_char = chars.nth(0); + let first_char = chars.next(); if let Some(chr) = first_char { if chr == '\0' || chr == '#' { @@ -45,7 +45,7 @@ impl DesktopEntry { } } - let split = line.splitn(2, "=").map(String::from).collect::>(); + let split = line.splitn(2, '=').map(String::from).collect::>(); let key = match split.get(0) { Some(key) => key, diff --git a/src/grid.rs b/src/grid.rs index f252d24..5b80045 100644 --- a/src/grid.rs +++ b/src/grid.rs @@ -12,6 +12,8 @@ use super::Config; pub const SHOW_ICON: u32 = 0b01; pub const SHOW_LABEL: u32 = 0b10; +type GridButtonCallback = dyn Fn(Rc>); + pub trait GridButton { fn label(&self) -> &String; fn display_label(&self) -> gtk::Label; @@ -30,7 +32,7 @@ impl Grid { pub fn new( items: Vec>>, flags: u32, - click_callback: Rc>)>, + click_callback: Rc, ) -> Self { let adjustment = None::<>k::Adjustment>; let window = GtkWindow::new(adjustment, adjustment); diff --git a/src/main.rs b/src/main.rs index 3fc88f2..8c8f85e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -92,7 +92,7 @@ fn main() { let css_provider = CssProvider::new(); - if config.enable_pywal == true { + if config.enable_pywal { add_class(&window, "pywal"); } From 63784cf0dd5f19b44df42152bf569cc92b9706e2 Mon Sep 17 00:00:00 2001 From: Kasper Seweryn Date: Sat, 18 Jan 2020 16:10:38 +0100 Subject: [PATCH 29/44] Add broken `grid.update()` Signed-off-by: Kasper Seweryn --- src/grid.rs | 104 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 82 insertions(+), 22 deletions(-) diff --git a/src/grid.rs b/src/grid.rs index 5b80045..8fde552 100644 --- a/src/grid.rs +++ b/src/grid.rs @@ -1,18 +1,24 @@ -use std::cell::RefCell; +use std::cell::{RefCell, Ref}; use std::rc::Rc; use gtk::{ ButtonExt, ContainerExt, Grid as GtkGrid, GridExt, LabelExt, ScrolledWindow as GtkWindow, Viewport as GtkViewport, WidgetExt, StyleContextExt }; -use sublime_fuzzy::best_match as fuzzy_match; +use sublime_fuzzy::{ + best_match as fuzzy_match, + format_simple as fuzzy_format, +}; use super::Config; +use std::collections::HashMap; +use std::collections::vec_deque::VecDeque; pub const SHOW_ICON: u32 = 0b01; pub const SHOW_LABEL: u32 = 0b10; -type GridButtonCallback = dyn Fn(Rc>); +type GridButtonRc = Rc>; +type GridButtonCallback = dyn Fn(GridButtonRc); pub trait GridButton { fn label(&self) -> &String; @@ -22,7 +28,8 @@ pub trait GridButton { } pub struct Grid { - items: Vec, + buttons: Vec, + items: Vec, filter_string: String, pub window: GtkWindow, grid: GtkGrid, @@ -30,7 +37,7 @@ pub struct Grid { impl Grid { pub fn new( - items: Vec>>, + items: Vec, flags: u32, click_callback: Rc, ) -> Self { @@ -103,8 +110,14 @@ impl Grid { buttons.push(widget); } + let mut local_items = Vec::new(); + for item in items { + local_items.push(item.clone()); + } + Self { - items: buttons, + buttons, + items: local_items, filter_string: String::from(""), window, grid, @@ -117,21 +130,68 @@ impl Grid { } pub fn update(&mut self) { - self.grid.foreach(|child| self.grid.remove(child)); - // - // for item in self.items.iter() { - // if self.filter_string == "" { - // filtered.push(item); - // continue; - // } - // - // let label = item.label(); - // if let Some(res) = fuzzy_match(self.filter_string.as_str(), label.as_str()) { - //// item.set_display_label(fuzzy_format(&res, label.as_str(), "", "")); - // filtered.push(item); - // } - // } - // - self.grid.show_all(); + let mut sorted = Vec::with_capacity(self.items.len()); + + for (i, mut item) in self.items.iter().enumerate() { + let mut item = item.borrow_mut(); + if self.filter_string == "" { + sorted.push((item, true, i)); + continue; + } + + let label = item.label(); + if let Some(res) = fuzzy_match(self.filter_string.as_str(), label.as_str()) { + item.set_display_label(fuzzy_format(&res, label.as_str(), "", "")); + sorted.push((item, true, i)); + continue; + } + + sorted.push((item, false, i)); + } + + sorted.sort_by_key(|a| a.0.label()); + sorted.sort_by_key(|a| a.1); + + let mut items = HashMap::new(); + for (i, item) in sorted.into_iter().enumerate() { + items.insert(item.0.label(), (item.0, item.1, i)); + } + + let columns = Config::get().columns as i32; + + let mut visited = HashMap::new(); + let mut queue = VecDeque::new(); + for (i, (item, _, _)) in sorted.into_iter().enumerate() { + if *visited.get(&i).unwrap_or(&false) { + continue; + } + + queue.push_back((i, None)); + while !queue.is_empty() { + let (i, button) = queue.pop_front().unwrap(); + visited.insert(i, true); + + let (item, show, next_idx) = sorted.get(i).unwrap(); + + let col = i as i32 % columns; + let row = i as i32 / columns; + let button = button.unwrap_or(&self.grid.get_child_at(col, row).unwrap()); + + if !visited.get(next_idx).unwrap_or(&false) { + let col = *next_idx as i32 % columns; + let row = *next_idx as i32 / columns; + queue.push_back((next_idx.clone(), Some(&self.grid.get_child_at(col, row).unwrap()))); + } + + let col = *next_idx as i32 % columns; + let row = *next_idx as i32 / columns; + self.grid.attach(button, col, row, 1, 1); + + if *show { button.show(); } + else { button.hide(); } + } + } + + self.grid.show(); } } From af5a6f8487ca21d6b91e167b1a1bbc667ef210f1 Mon Sep 17 00:00:00 2001 From: Kasper Seweryn Date: Sun, 19 Jan 2020 15:04:53 +0100 Subject: [PATCH 30/44] Fix button hover effect --- src/grid.rs | 10 ++++------ src/main.rs | 3 +++ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/grid.rs b/src/grid.rs index 8fde552..23283bf 100644 --- a/src/grid.rs +++ b/src/grid.rs @@ -32,7 +32,7 @@ pub struct Grid { items: Vec, filter_string: String, pub window: GtkWindow, - grid: GtkGrid, + pub(crate) grid: GtkGrid, } impl Grid { @@ -63,7 +63,10 @@ impl Grid { let mut buttons: Vec = Vec::new(); let mut i = 0; + let mut local_items = Vec::new(); for item in items { + local_items.push(item.clone()); + let col = i as i32 % config.columns as i32; let row = i as i32 / config.columns as i32; i += 1; @@ -110,11 +113,6 @@ impl Grid { buttons.push(widget); } - let mut local_items = Vec::new(); - for item in items { - local_items.push(item.clone()); - } - Self { buttons, items: local_items, diff --git a/src/main.rs b/src/main.rs index f98b3be..c74cf78 100644 --- a/src/main.rs +++ b/src/main.rs @@ -99,6 +99,9 @@ fn main() { }), ); + let grid = &app_grid.grid; + grid.set_widget_name("apps"); + layout.pack_start(&app_grid.window, true, true, 0); let css_provider = CssProvider::new(); From c3e8ea3effeda3e4a648684365dd36bba890d093 Mon Sep 17 00:00:00 2001 From: Kasper Seweryn Date: Thu, 23 Jan 2020 23:06:08 +0100 Subject: [PATCH 31/44] Change `pub(crate)` to `pub` Signed-off-by: Kasper Seweryn --- src/grid.rs | 128 ++++++++++++++++++++++++++-------------------------- 1 file changed, 64 insertions(+), 64 deletions(-) diff --git a/src/grid.rs b/src/grid.rs index 23283bf..e25a619 100644 --- a/src/grid.rs +++ b/src/grid.rs @@ -32,7 +32,7 @@ pub struct Grid { items: Vec, filter_string: String, pub window: GtkWindow, - pub(crate) grid: GtkGrid, + pub grid: GtkGrid, } impl Grid { @@ -128,68 +128,68 @@ impl Grid { } pub fn update(&mut self) { - let mut sorted = Vec::with_capacity(self.items.len()); - - for (i, mut item) in self.items.iter().enumerate() { - let mut item = item.borrow_mut(); - if self.filter_string == "" { - sorted.push((item, true, i)); - continue; - } - - let label = item.label(); - if let Some(res) = fuzzy_match(self.filter_string.as_str(), label.as_str()) { - item.set_display_label(fuzzy_format(&res, label.as_str(), "", "")); - sorted.push((item, true, i)); - continue; - } - - sorted.push((item, false, i)); - } - - sorted.sort_by_key(|a| a.0.label()); - sorted.sort_by_key(|a| a.1); - - let mut items = HashMap::new(); - for (i, item) in sorted.into_iter().enumerate() { - items.insert(item.0.label(), (item.0, item.1, i)); - } - - let columns = Config::get().columns as i32; - - let mut visited = HashMap::new(); - let mut queue = VecDeque::new(); - for (i, (item, _, _)) in sorted.into_iter().enumerate() { - if *visited.get(&i).unwrap_or(&false) { - continue; - } - - queue.push_back((i, None)); - while !queue.is_empty() { - let (i, button) = queue.pop_front().unwrap(); - visited.insert(i, true); - - let (item, show, next_idx) = sorted.get(i).unwrap(); - - let col = i as i32 % columns; - let row = i as i32 / columns; - let button = button.unwrap_or(&self.grid.get_child_at(col, row).unwrap()); - - if !visited.get(next_idx).unwrap_or(&false) { - let col = *next_idx as i32 % columns; - let row = *next_idx as i32 / columns; - queue.push_back((next_idx.clone(), Some(&self.grid.get_child_at(col, row).unwrap()))); - } - - let col = *next_idx as i32 % columns; - let row = *next_idx as i32 / columns; - self.grid.attach(button, col, row, 1, 1); - - if *show { button.show(); } - else { button.hide(); } - } - } - - self.grid.show(); +// let mut sorted = Vec::with_capacity(self.items.len()); +// +// for (i, mut item) in self.items.iter().enumerate() { +// let mut item = item.borrow_mut(); +// if self.filter_string == "" { +// sorted.push((item, true, i)); +// continue; +// } +// +// let label = item.label(); +// if let Some(res) = fuzzy_match(self.filter_string.as_str(), label.as_str()) { +// item.set_display_label(fuzzy_format(&res, label.as_str(), "", "")); +// sorted.push((item, true, i)); +// continue; +// } +// +// sorted.push((item, false, i)); +// } +// +// sorted.sort_by_key(|a| a.0.label()); +// sorted.sort_by_key(|a| a.1); +// +// let mut items = HashMap::new(); +// for (i, item) in sorted.into_iter().enumerate() { +// items.insert(item.0.label(), (item.0, item.1, i)); +// } +// +// let columns = Config::get().columns as i32; +// +// let mut visited = HashMap::new(); +// let mut queue = VecDeque::new(); +// for (i, (item, _, _)) in sorted.into_iter().enumerate() { +// if *visited.get(&i).unwrap_or(&false) { +// continue; +// } +// +// queue.push_back((i, None)); +// while !queue.is_empty() { +// let (i, button) = queue.pop_front().unwrap(); +// visited.insert(i, true); +// +// let (item, show, next_idx) = sorted.get(i).unwrap(); +// +// let col = i as i32 % columns; +// let row = i as i32 / columns; +// let button = button.unwrap_or(&self.grid.get_child_at(col, row).unwrap()); +// +// if !visited.get(next_idx).unwrap_or(&false) { +// let col = *next_idx as i32 % columns; +// let row = *next_idx as i32 / columns; +// queue.push_back((next_idx.clone(), Some(&self.grid.get_child_at(col, row).unwrap()))); +// } +// +// let col = *next_idx as i32 % columns; +// let row = *next_idx as i32 / columns; +// self.grid.attach(button, col, row, 1, 1); +// +// if *show { button.show(); } +// else { button.hide(); } +// } +// } +// +// self.grid.show(); } } From a1682a8d7d4baff63c8f3f66c69ef3a3f87ced58 Mon Sep 17 00:00:00 2001 From: insertt Date: Mon, 24 Feb 2020 00:27:43 +0100 Subject: [PATCH 32/44] Compilable implementation of grid update --- src/grid.rs | 137 ++++++++++++++++++++++++++-------------------------- src/main.rs | 45 ++++++++++------- 2 files changed, 97 insertions(+), 85 deletions(-) diff --git a/src/grid.rs b/src/grid.rs index e25a619..7ae0f6f 100644 --- a/src/grid.rs +++ b/src/grid.rs @@ -1,9 +1,11 @@ -use std::cell::{RefCell, Ref}; +use std::cell::{Ref, RefCell}; +use std::collections::HashMap; +use std::collections::vec_deque::VecDeque; use std::rc::Rc; use gtk::{ ButtonExt, ContainerExt, Grid as GtkGrid, GridExt, LabelExt, ScrolledWindow as GtkWindow, - Viewport as GtkViewport, WidgetExt, StyleContextExt + StyleContextExt, Viewport as GtkViewport, WidgetExt, }; use sublime_fuzzy::{ best_match as fuzzy_match, @@ -11,8 +13,6 @@ use sublime_fuzzy::{ }; use super::Config; -use std::collections::HashMap; -use std::collections::vec_deque::VecDeque; pub const SHOW_ICON: u32 = 0b01; pub const SHOW_LABEL: u32 = 0b10; @@ -127,69 +127,70 @@ impl Grid { self.update(); } - pub fn update(&mut self) { -// let mut sorted = Vec::with_capacity(self.items.len()); -// -// for (i, mut item) in self.items.iter().enumerate() { -// let mut item = item.borrow_mut(); -// if self.filter_string == "" { -// sorted.push((item, true, i)); -// continue; -// } -// -// let label = item.label(); -// if let Some(res) = fuzzy_match(self.filter_string.as_str(), label.as_str()) { -// item.set_display_label(fuzzy_format(&res, label.as_str(), "", "")); -// sorted.push((item, true, i)); -// continue; -// } -// -// sorted.push((item, false, i)); -// } -// -// sorted.sort_by_key(|a| a.0.label()); -// sorted.sort_by_key(|a| a.1); -// -// let mut items = HashMap::new(); -// for (i, item) in sorted.into_iter().enumerate() { -// items.insert(item.0.label(), (item.0, item.1, i)); -// } -// -// let columns = Config::get().columns as i32; -// -// let mut visited = HashMap::new(); -// let mut queue = VecDeque::new(); -// for (i, (item, _, _)) in sorted.into_iter().enumerate() { -// if *visited.get(&i).unwrap_or(&false) { -// continue; -// } -// -// queue.push_back((i, None)); -// while !queue.is_empty() { -// let (i, button) = queue.pop_front().unwrap(); -// visited.insert(i, true); -// -// let (item, show, next_idx) = sorted.get(i).unwrap(); -// -// let col = i as i32 % columns; -// let row = i as i32 / columns; -// let button = button.unwrap_or(&self.grid.get_child_at(col, row).unwrap()); -// -// if !visited.get(next_idx).unwrap_or(&false) { -// let col = *next_idx as i32 % columns; -// let row = *next_idx as i32 / columns; -// queue.push_back((next_idx.clone(), Some(&self.grid.get_child_at(col, row).unwrap()))); -// } -// -// let col = *next_idx as i32 % columns; -// let row = *next_idx as i32 / columns; -// self.grid.attach(button, col, row, 1, 1); -// -// if *show { button.show(); } -// else { button.hide(); } -// } -// } -// -// self.grid.show(); + pub fn update(&self) { + let mut sorted_entries = HashMap::with_capacity(self.items.len()); + + for (i, mut item) in self.items.iter().enumerate() { + let mut item = item.borrow_mut(); + let label = item.label().clone(); + + if self.filter_string == "" { + sorted_entries.insert(label, (item, true, i)); + continue; + } + + if let Some(res) = fuzzy_match(self.filter_string.as_str(), label.as_str()) { + item.set_display_label(fuzzy_format(&res, label.as_str(), "", "")); + sorted_entries.insert(label, (item, true, i)); + continue; + } + + sorted_entries.insert(label, (item, false, i)); + } + + // sorted.sort_by_key(|a| a.0.label().clone()); + // sorted.sort_by_key(|a| a.1); + // + // let mut items = HashMap::new(); + // for (i, item) in sorted.into_iter().enumerate() { + // items.insert(item.0.label().clone(), (item.0, item.1, i)); + // } + + let columns = Config::get().columns as i32; + + let mut visited = HashMap::new(); + let mut queue = VecDeque::new(); + + for (i, (key, value)) in sorted_entries.into_iter().enumerate() { + if visited.get(&i).copied().unwrap_or(false) { + continue; + } + + queue.push_back((i, None)); + while !queue.is_empty() { + let (i, button) = queue.pop_front().unwrap(); + visited.insert(i, true); + + let (_, show, next_idx) = value; + + let col = i as i32 % columns; + let row = i as i32 / columns; + let button = &button.unwrap_or(self.grid.get_child_at(col, row).unwrap()); + + if !visited.get(&next_idx).unwrap_or(&false) { + let col = next_idx as i32 % columns; + let row = next_idx as i32 / columns; + queue.push_back((next_idx.clone(), Some(self.grid.get_child_at(col, row).unwrap()))); + } + + let col = next_idx as i32 % columns; + let row = next_idx as i32 / columns; + self.grid.attach(button, col, row, 1, 1); + + if show { button.show(); } else { button.hide(); } + } + } + + self.grid.show(); } } diff --git a/src/main.rs b/src/main.rs index c74cf78..6b7f5f1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,23 +1,21 @@ -use crate::grid::GridButton; use std::cell::RefCell; use std::rc::Rc; use gdk::*; -use gtk::{ - BoxExt, ContainerExt, CssProvider, CssProviderExt, GtkWindowExt, Inhibit, Label, LabelExt, - Orientation, StyleContext, StyleContextExt, TextView, WidgetExt, -}; +use gtk::{BoxExt, ContainerExt, CssProvider, CssProviderExt, GtkWindowExt, Inhibit, Label, LabelExt, Orientation, StyleContext, StyleContextExt, TextBuffer, TextBufferExt, TextTagTable, TextView, WidgetExt}; use gtk_layer_shell_rs::*; +use config::Config; +use desktop_entry::DesktopEntry; + +use crate::grid::GridButton; + mod config; mod grid; mod resource; mod style; mod desktop_entry; -use desktop_entry::DesktopEntry; - -use config::Config; fn main() { Config::create_dir(); @@ -82,27 +80,41 @@ fn main() { add_class(&search_label, "textview-label"); - let search_input = TextView::new(); - // search_input.set_name("search"); - search_box.pack_start(&search_input, true, true, 0); - let buttons = desktop_entries .into_iter() .map(|entry| entry as Rc>) .collect::>(); - let mut app_grid = grid::Grid::new( + let mut app_grid = Rc::new(RefCell::new(grid::Grid::new( buttons, grid::SHOW_ICON | grid::SHOW_LABEL, Rc::new(|entry| { println!("{:?}", entry.borrow().label()); }), - ); + ))); + + let buffer = TextBuffer::new(None::<&TextTagTable>); + let cloned_app_grid = app_grid.clone(); + + buffer.connect_changed(move |buf| { + let search_text = buf.get_text(&buf.get_start_iter(), &buf.get_end_iter(), false) + .unwrap_or("".into()); + + if let Ok(mut grid) = cloned_app_grid.try_borrow_mut() { + println!("search text: {}", search_text); + + grid.filter(search_text.to_string()); + } + }); + + let search_input = TextView::new_with_buffer(&buffer); + search_box.pack_start(&search_input, true, true, 0); - let grid = &app_grid.grid; + let grid = &app_grid.borrow().grid; grid.set_widget_name("apps"); - layout.pack_start(&app_grid.window, true, true, 0); + let gtk_window = &app_grid.borrow().window; + layout.pack_start(gtk_window, true, true, 0); let css_provider = CssProvider::new(); @@ -130,7 +142,6 @@ fn main() { window.set_title("waffy"); window.show_all(); - // app_grid.update(); gtk::main(); } From 810d5794fd34cce133b37c7b9d9cbed9f3e84abc Mon Sep 17 00:00:00 2001 From: Kasper Seweryn Date: Mon, 24 Feb 2020 00:35:04 +0000 Subject: [PATCH 33/44] Create FUNDING.yml --- .github/FUNDING.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..113d33f --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: ['https://paypal.me/wvffle'] From af567ca528242a1dae84e158c6721131199382ec Mon Sep 17 00:00:00 2001 From: Kasper Seweryn Date: Sat, 28 Mar 2020 22:48:36 +0100 Subject: [PATCH 34/44] Change cargo name to `waffy` Signed-off-by: Kasper Seweryn --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 77ca57f..5e818fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "waffy-rs" +name = "waffy" version = "0.1.0" authors = ["Kasper Seweryn "] edition = "2018" From 912d38b08ebed80b6fd0def7df2241c54e0eccfc Mon Sep 17 00:00:00 2001 From: Kasper Seweryn Date: Sat, 28 Mar 2020 23:19:25 +0100 Subject: [PATCH 35/44] Fix updating grid Signed-off-by: Kasper Seweryn --- Cargo.lock | 2 +- src/grid.rs | 21 +++++++++------------ src/main.rs | 12 +++++++----- 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5018ee0..38fb9e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -971,7 +971,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" [[package]] -name = "waffy-rs" +name = "waffy" version = "0.1.0" dependencies = [ "dirs", diff --git a/src/grid.rs b/src/grid.rs index 7ae0f6f..45adbd4 100644 --- a/src/grid.rs +++ b/src/grid.rs @@ -1,4 +1,4 @@ -use std::cell::{Ref, RefCell}; +use std::cell::RefCell; use std::collections::HashMap; use std::collections::vec_deque::VecDeque; use std::rc::Rc; @@ -28,9 +28,8 @@ pub trait GridButton { } pub struct Grid { - buttons: Vec, +// buttons: Vec, items: Vec, - filter_string: String, pub window: GtkWindow, pub grid: GtkGrid, } @@ -114,32 +113,30 @@ impl Grid { } Self { - buttons, +// buttons, items: local_items, - filter_string: String::from(""), window, grid, } } - pub fn filter(&mut self, needle: String) { - self.filter_string = needle; - self.update(); + pub fn filter(&self, needle: String) { + self.update(needle); } - pub fn update(&self) { + pub fn update(&self, needle: String) { let mut sorted_entries = HashMap::with_capacity(self.items.len()); - for (i, mut item) in self.items.iter().enumerate() { + for (i, item) in self.items.iter().enumerate() { let mut item = item.borrow_mut(); let label = item.label().clone(); - if self.filter_string == "" { + if needle == "" { sorted_entries.insert(label, (item, true, i)); continue; } - if let Some(res) = fuzzy_match(self.filter_string.as_str(), label.as_str()) { + if let Some(res) = fuzzy_match(needle.as_str(), label.as_str()) { item.set_display_label(fuzzy_format(&res, label.as_str(), "", "")); sorted_entries.insert(label, (item, true, i)); continue; diff --git a/src/main.rs b/src/main.rs index 6b7f5f1..e0d77fc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -41,11 +41,13 @@ fn main() { window.destroy(); std::process::exit(0) } - _ => Inhibit(true), + _ => { + Inhibit(true) + }, } }); - window.connect_enter_notify_event(|window, event| { + window.connect_enter_notify_event(|window, _event| { set_keyboard_interactivity(window, true); Inhibit(false) }); @@ -85,7 +87,7 @@ fn main() { .map(|entry| entry as Rc>) .collect::>(); - let mut app_grid = Rc::new(RefCell::new(grid::Grid::new( + let app_grid = Rc::new(RefCell::new(grid::Grid::new( buttons, grid::SHOW_ICON | grid::SHOW_LABEL, Rc::new(|entry| { @@ -100,13 +102,13 @@ fn main() { let search_text = buf.get_text(&buf.get_start_iter(), &buf.get_end_iter(), false) .unwrap_or("".into()); - if let Ok(mut grid) = cloned_app_grid.try_borrow_mut() { + if let Ok(grid) = cloned_app_grid.try_borrow() { println!("search text: {}", search_text); - grid.filter(search_text.to_string()); } }); + let search_input = TextView::new_with_buffer(&buffer); search_box.pack_start(&search_input, true, true, 0); From 9338eb11229587c1d31edeb87060b985b0899bd4 Mon Sep 17 00:00:00 2001 From: Kasper Seweryn Date: Sun, 29 Mar 2020 01:43:15 +0100 Subject: [PATCH 36/44] Fix filtering Signed-off-by: Kasper Seweryn --- Cargo.lock | 1 + Cargo.toml | 1 + src/grid.rs | 90 ++++++++++++++++++++++++----------------------------- 3 files changed, 42 insertions(+), 50 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 38fb9e9..bf37bf9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -978,6 +978,7 @@ dependencies = [ "gdk", "gdk-pixbuf", "gio", + "glib", "gtk", "gtk-layer-shell-rs", "json5", diff --git a/Cargo.toml b/Cargo.toml index 5e818fa..408eb0f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,4 +18,5 @@ gtk = { git = "https://github.com/gtk-rs/gtk.git" } gdk = { git = "https://github.com/gtk-rs/gdk.git" } gdk-pixbuf = { git = "https://github.com/gtk-rs/gdk-pixbuf.git" } gio = { git = "https://github.com/gtk-rs/gio.git" } +glib = { git = "https://github.com/gtk-rs/glib.git" } pango = { git = "https://github.com/gtk-rs/pango.git" } diff --git a/src/grid.rs b/src/grid.rs index 45adbd4..538ddc1 100644 --- a/src/grid.rs +++ b/src/grid.rs @@ -1,12 +1,9 @@ use std::cell::RefCell; -use std::collections::HashMap; -use std::collections::vec_deque::VecDeque; use std::rc::Rc; -use gtk::{ - ButtonExt, ContainerExt, Grid as GtkGrid, GridExt, LabelExt, ScrolledWindow as GtkWindow, - StyleContextExt, Viewport as GtkViewport, WidgetExt, -}; +use gtk::{ButtonExt, ContainerExt, Grid as GtkGrid, GridExt, LabelExt, ScrolledWindow as GtkWindow, StyleContextExt, Viewport as GtkViewport, WidgetExt, Label}; +use glib::object::Cast; + use sublime_fuzzy::{ best_match as fuzzy_match, format_simple as fuzzy_format, @@ -28,10 +25,11 @@ pub trait GridButton { } pub struct Grid { -// buttons: Vec, + buttons: Vec, items: Vec, pub window: GtkWindow, pub grid: GtkGrid, + flags: u32, } impl Grid { @@ -113,10 +111,11 @@ impl Grid { } Self { -// buttons, + buttons, items: local_items, window, grid, + flags } } @@ -125,67 +124,58 @@ impl Grid { } pub fn update(&self, needle: String) { - let mut sorted_entries = HashMap::with_capacity(self.items.len()); + let mut sorted_entries = Vec::with_capacity(self.items.len()); + let columns = Config::get().columns as i32; + self.grid.foreach(|widget| self.grid.remove(widget)); for (i, item) in self.items.iter().enumerate() { let mut item = item.borrow_mut(); let label = item.label().clone(); + let button = self.buttons.get(i).unwrap(); if needle == "" { - sorted_entries.insert(label, (item, true, i)); + sorted_entries.push(true); + button.set_label(&label); continue; } if let Some(res) = fuzzy_match(needle.as_str(), label.as_str()) { - item.set_display_label(fuzzy_format(&res, label.as_str(), "", "")); - sorted_entries.insert(label, (item, true, i)); +// if self.flags & SHOW_LABEL > 0 { +// let mut label_idx = 0; +// +// if self.flags & SHOW_ICON > 0 { +// label_idx = 1; +// } +// +// if let Some(widget) = button.get_children().get(label_idx) { +// if let Some(gtklabel) = button.get_children().get(label_idx) { +// let gtklabel: Label = widget.downcast().unwrap(); +// gtklabel.set_markup(fuzzy_format(&res, label.as_str(), "", "").as_str()); +// } +// } +// } + + sorted_entries.push(true); continue; } - sorted_entries.insert(label, (item, false, i)); + sorted_entries.push(false); } - // sorted.sort_by_key(|a| a.0.label().clone()); - // sorted.sort_by_key(|a| a.1); - // - // let mut items = HashMap::new(); - // for (i, item) in sorted.into_iter().enumerate() { - // items.insert(item.0.label().clone(), (item.0, item.1, i)); - // } - - let columns = Config::get().columns as i32; - - let mut visited = HashMap::new(); - let mut queue = VecDeque::new(); - - for (i, (key, value)) in sorted_entries.into_iter().enumerate() { - if visited.get(&i).copied().unwrap_or(false) { - continue; - } - - queue.push_back((i, None)); - while !queue.is_empty() { - let (i, button) = queue.pop_front().unwrap(); - visited.insert(i, true); - - let (_, show, next_idx) = value; + let mut idx = 0; + for (i, visible) in sorted_entries.into_iter().enumerate() { + let mut col = i as i32 % columns; + let mut row = i as i32 / columns; + let button = self.buttons.get(i).unwrap(); - let col = i as i32 % columns; - let row = i as i32 / columns; - let button = &button.unwrap_or(self.grid.get_child_at(col, row).unwrap()); + if visible { + col = idx as i32 % columns; + row = idx as i32 / columns; - if !visited.get(&next_idx).unwrap_or(&false) { - let col = next_idx as i32 % columns; - let row = next_idx as i32 / columns; - queue.push_back((next_idx.clone(), Some(self.grid.get_child_at(col, row).unwrap()))); - } - - let col = next_idx as i32 % columns; - let row = next_idx as i32 / columns; self.grid.attach(button, col, row, 1, 1); - - if show { button.show(); } else { button.hide(); } + idx += 1; } + } self.grid.show(); From 2dc5dc810a221b821cc07ed26bbe1428ca252aec Mon Sep 17 00:00:00 2001 From: Kasper Seweryn Date: Sun, 29 Mar 2020 03:06:50 +0200 Subject: [PATCH 37/44] Add code responsible for showing matched chars Signed-off-by: Kasper Seweryn --- res/default_style.css | 8 ++++++++ src/grid.rs | 29 +++++++++++++++-------------- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/res/default_style.css b/res/default_style.css index 68bb4cf..791849a 100644 --- a/res/default_style.css +++ b/res/default_style.css @@ -45,6 +45,10 @@ window { background: alpha(#fff, .3); } +.button-highlight { + color: #00aaff; +} + /* pywal colors */ window.pywal { background: alpha(@background, .7); @@ -57,3 +61,7 @@ window.pywal #apps > button { window.pywal #apps > button.active { background: @color5; } + +.button-highlight { + color: @color3; +} diff --git a/src/grid.rs b/src/grid.rs index 538ddc1..9ebfd40 100644 --- a/src/grid.rs +++ b/src/grid.rs @@ -140,20 +140,21 @@ impl Grid { } if let Some(res) = fuzzy_match(needle.as_str(), label.as_str()) { -// if self.flags & SHOW_LABEL > 0 { -// let mut label_idx = 0; -// -// if self.flags & SHOW_ICON > 0 { -// label_idx = 1; -// } -// -// if let Some(widget) = button.get_children().get(label_idx) { -// if let Some(gtklabel) = button.get_children().get(label_idx) { -// let gtklabel: Label = widget.downcast().unwrap(); -// gtklabel.set_markup(fuzzy_format(&res, label.as_str(), "", "").as_str()); -// } -// } -// } + let container = button.get_children().get(0).as_ref().unwrap(); + let grid: gtk::Grid = container.downcast().unwrap(); + for child in grid.get_children() { + match child.downcast::