Great things coming up

This commit is contained in:
Javier Feliz 2025-08-27 23:01:30 -04:00
commit 33ab4308d1
14 changed files with 804 additions and 0 deletions

16
.envrc Normal file
View File

@ -0,0 +1,16 @@
# Use the Nix shell
use nix
# Optional: Layout for different build types
layout() {
case $1 in
cpp)
# Add any C++ specific environment setup here
export CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE:-Debug}
export CMAKE_EXPORT_COMPILE_COMMANDS=ON
;;
esac
}
# Activate the cpp layout
layout cpp

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
build
.vscode
.zig-cache
zig-out

74
CLAUDE.md Normal file
View File

@ -0,0 +1,74 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
WayCast is a Raycast-like application launcher for Linux, built with C++20 and Qt6. It scans XDG desktop entries to provide a searchable interface for launching applications on Wayland/X11.
## Build System
The project supports three build systems:
### Primary: CMake with Ninja (Recommended)
```bash
# Configure and build
make configure # or: cmake -S . -B build -G Ninja
make bld # or: cmake --build build
make run # or: ./build/waycast
# Combined: make br (build + run)
# Full rebuild: make all (configure + build + run)
```
### Alternative: Zig Build
```bash
zig build run
```
### Development Environment: Nix Shell
```bash
nix-shell # Sets up Qt6, CMake, Ninja, Clang, and required dependencies
```
## Architecture
### Core Components
- **main.cpp**: Entry point, currently configured for CLI testing of desktop entry parsing
- **dmenu.hpp/namespace**: Desktop entry parsing using GIO/GLib to read XDG application data
- **files.hpp/namespace**: File system utilities for scanning directories and reading files
- **ui/Main.qml**: Qt Quick interface (currently minimal, Qt GUI code commented out in main)
### Key Classes
- `dmenu::DesktopEntry`: Parses .desktop files using GDesktopAppInfo, extracts app metadata (name, icon, executable, display flags)
- `files::findFilesWithExtension()`: Recursively scans directories for files with specific extensions
- `DEVec`: Type alias for `std::unique_ptr<std::vector<DesktopEntry>>`
### Current State
The application is in active development:
- Main Qt GUI loop is commented out in main.cpp:56-64
- Currently runs as CLI tool that prints discovered application IDs
- Desktop entry scanning logic is functional
- Qt QML interface exists but is not connected
## Dependencies
- **Qt6**: Core, Gui, Qml, Quick, QuickControls2
- **GIO/GLib**: For XDG desktop entry parsing
- **C++20**: Uses std::format, filesystem, and modern C++ features
- **CMake 3.21+**: Build system
- **Ninja**: Preferred generator
## Development Notes
- Qt resources are bundled via CMakeLists.txt (qt_add_qml_module)
- Uses C++20 modules compilation flags for GCC/Clang
- Nix shell provides complete development environment with Qt6 Wayland support
- Built-in RPATH configuration for Linux runtime library discovery
## Project Conventions
- We're not using header files. Prioritize .hpp

67
CMakeLists.txt Normal file
View File

@ -0,0 +1,67 @@
cmake_minimum_required(VERSION 3.21)
project(waycast LANGUAGES CXX C)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# Use Ninja if available (optional)
if(NOT CMAKE_GENERATOR)
set(CMAKE_GENERATOR "Ninja" CACHE INTERNAL "" FORCE)
endif()
find_package(PkgConfig REQUIRED)
find_package(Qt6 REQUIRED COMPONENTS
Core
Gui
Qml
Quick
QuickControls2
)
find_package(LayerShellQt REQUIRED)
pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
qt_standard_project_setup()
qt_add_executable(waycast)
include_directories("${CMAKE_SOURCE_DIR}/lib")
include_directories("${CMAKE_SOURCE_DIR}/lib")
file(GLOB_RECURSE SRC_FILES CONFIGURE_DEPENDS "${CMAKE_SOURCE_DIR}/src/*.cpp")
target_sources(waycast PRIVATE ${SRC_FILES} src/main.cpp)
target_include_directories(waycast PRIVATE ${CMAKE_SOURCE_DIR}/lib)
# target_include_directories(waycast PRIVATE ${CMAKE_SOURCE_DIR}/lib ${CMAKE_CURRENT_BINARY_DIR})
# Bundle QML into the app (no runtime QML path headaches)
qt_add_qml_module(waycast
URI WayCast
VERSION 1.0
QML_FILES
ui/Main.qml
)
target_link_libraries(waycast PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Qml
Qt6::Quick
PkgConfig::GIO
LayerShellQt::Interface
)
# On Linux, ensure plugins/libs are found at runtime if needed
# (usually fine when running inside nix shell)
if(UNIX AND NOT APPLE)
set_target_properties(waycast PROPERTIES
INSTALL_RPATH "$ORIGIN"
BUILD_WITH_INSTALL_RPATH TRUE
)
endif()
# Add -fmodules-ts where needed
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
target_compile_options(waycast PRIVATE -fmodules-ts)
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
target_compile_options(waycast PRIVATE -fmodules-ts)
endif()

11
Makefile Normal file
View File

@ -0,0 +1,11 @@
configure:
rm -rf build
cmake -S . -B build -G Ninja
bld:
cmake --build build
run:
./build/waycast
br: bld run
all: configure bld run

3
README.md Normal file
View File

@ -0,0 +1,3 @@
# WayCast
Raycast for linux. We deserve this.

57
build.zig Normal file
View File

@ -0,0 +1,57 @@
const std = @import("std");
pub fn build(b: *std.Build) void {
// const target = b.standardTargetOptions(.{});
// const optimize = b.standardOptimizeOption(.{});
// Step 1: Generate Qt resources automatically
const rcc_cmd = b.addSystemCommand(&.{ "rcc", "ui/resources.qrc", "-o", "qrc_resources.cpp" });
// Create the executable
const mod = b.createModule(.{
.target = b.graph.host,
});
mod.addCSourceFiles(.{
.files = &.{
"main.cpp",
"qrc_resources.cpp",
},
.flags = &.{
"-std=c++20",
"-Wall",
"-Wextra",
},
});
const exe = b.addExecutable(.{
.name = "waycast",
.root_module = mod,
});
exe.step.dependOn(&rcc_cmd.step);
// Link C++ standard library
exe.linkLibCpp();
// Link Qt6
exe.linkSystemLibrary("Qt6Core");
exe.linkSystemLibrary("Qt6Quick");
exe.linkSystemLibrary("Qt6Gui");
exe.linkSystemLibrary("Qt6Qml");
// Install the executable
b.installArtifact(exe);
// Create a run step
const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());
// Allow passing arguments to the program
if (b.args) |args| {
run_cmd.addArgs(args);
}
// Create the run step
const run_step = b.step("run", "Run the application");
run_step.dependOn(&run_cmd.step);
}

106
lib/dmenu.hpp Normal file
View File

@ -0,0 +1,106 @@
#pragma once
#include "files.hpp"
#include <glib.h>
#include <gio/gio.h>
#include <gio/gappinfo.h>
#include <gio-unix-2.0/gio/gdesktopappinfo.h>
#include <string>
#include <vector>
#include <iostream>
namespace dmenu
{
class DesktopEntry
{
public:
std::string id;
std::string name;
std::string icon_path;
std::string exec;
bool display = false;
DesktopEntry(std::string path)
{
GDesktopAppInfo *info = g_desktop_app_info_new_from_filename(path.c_str());
if (!info) {
std::cerr << "Failed to create desktop app info for: " << path << std::endl;
return;
}
GAppInfo *app = G_APP_INFO(info);
if (!app) {
std::cerr << "Failed to get app info for: " << path << std::endl;
g_object_unref(info);
return;
}
const char *app_id = g_app_info_get_id(app);
if (app_id) id = app_id;
const char *app_name = g_app_info_get_name(app);
if (app_name) name = app_name;
GIcon *icon = g_app_info_get_icon(app);
if (icon) {
char* icon_str = g_icon_to_string(icon);
if (icon_str) {
icon_path = icon_str;
g_free(icon_str);
}
}
const char *ex = g_app_info_get_executable(app);
if (ex)
exec = ex;
display = g_desktop_app_info_get_boolean(info, "NoDisplay") ? false : true;
g_object_unref(info);
}
std::string iconPath()
{
return icon_path;
}
};
std::vector<std::string> split(std::string s, char delimiter)
{
std::vector<std::string> items;
std::string line;
std::stringstream ss(s);
while (std::getline(ss, line, delimiter))
{
items.push_back(line);
}
return items;
}
using DEVec = std::unique_ptr<std::vector<DesktopEntry>>;
DEVec get_dmenu_app_data()
{
DEVec out = std::make_unique<std::vector<DesktopEntry>>();
const char* env_dirs = std::getenv("XDG_DATA_DIRS");
if (!env_dirs) {
std::cerr << "XDG_DATA_DIRS environment variable not set" << std::endl;
return out;
}
std::string dataDirs = env_dirs;
std::vector<std::string> paths = split(dataDirs, ':');
for (std::string &p : paths)
p.append("/applications");
for (std::string &p : paths)
{
std::vector<std::filesystem::path> desktopFiles = files::findFilesWithExtension(p, ".desktop");
for (const auto &dfile : desktopFiles)
{
out->emplace_back(dfile.string());
}
}
return out;
}
}

53
lib/files.hpp Normal file
View File

@ -0,0 +1,53 @@
#pragma once
#include <filesystem>
#include <string>
#include <vector>
#include <unordered_set>
#include <system_error>
#include <iostream>
#include <sstream>
#include <fstream>
namespace files
{
namespace fs = std::filesystem;
std::vector<fs::path> findFilesWithExtension(const std::string path, const std::string ext)
{
std::vector<fs::path> out;
std::unordered_set<std::string> seen; // canonicalized paths to dedupe
std::error_code ec;
fs::path p(path);
if (!fs::exists(p, ec) || !fs::is_directory(p, ec))
return out;
for (const auto &entry : fs::directory_iterator(p, ec))
{
if (ec)
{
ec.clear();
continue;
}
const auto &filePath = entry.path();
if (filePath.extension() == ext && fs::is_regular_file(filePath, ec))
{
out.push_back(filePath);
}
}
return out;
}
std::string readFile(const std::string &filename)
{
std::ifstream in(filename);
if (!in)
throw std::runtime_error("Could not open file");
std::ostringstream ss;
ss << in.rdbuf(); // dump entire buffer into string
return ss.str();
}
}

35
shell.nix Normal file
View File

@ -0,0 +1,35 @@
{
pkgs ? import <nixpkgs> { },
}:
let
qt = pkgs.qt6Packages;
in
pkgs.mkShell {
buildInputs = [
pkgs.cmake
pkgs.ninja
pkgs.clang_18
pkgs.lldb
pkgs.ccacheWrapper
pkgs.glib
pkgs.glib.dev
qt.full
qt.qtbase
qt.qtdeclarative # <- provides QML/Quick + Quick Controls 2 (QtQuick.Controls)
qt.qtwayland # <- provides the Wayland platform plugin
pkgs.kdePackages.layer-shell-qt # <- provides layer shell support
pkgs.kdePackages.layer-shell-qt.dev # <- provides headers
pkgs.pkg-config
# qt.qttools
# qt.qtshadertools
];
shellHook = ''
export QT_PLUGIN_PATH='${qt.full}'
export QML2_IMPORT_PATH='${qt.full}:${pkgs.kdePackages.layer-shell-qt}/lib/qt-6/qml'
export QT_QPA_PLATFORM=wayland
echo "--------------------------------------------"
echo "QT_PLUGIN_PATH: $QT_PLUGIN_PATH"
echo "QML2_IMPORT_PATH: $QML2_IMPORT_PATH"
'';
}

72
src/main.cpp Normal file
View File

@ -0,0 +1,72 @@
#include "dmenu.hpp"
#include "files.hpp"
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QIcon>
#include <QWindow>
#include <LayerShellQt/window.h>
#include <cstdlib>
#include <string>
#include <iostream>
#include <format>
#include <vector>
#include <sstream>
#include <filesystem>
std::vector<std::string> split(std::string s, char delimiter)
{
std::vector<std::string> items;
std::string line;
std::stringstream ss(s);
while (std::getline(ss, line, delimiter))
{
items.push_back(line);
}
return items;
}
int main(int argc, char *argv[])
{
// dmenu::DEVec apps = dmenu::get_dmenu_app_data();
// for (auto &app : *apps.get())
// {
// std::cout << std::format("---\nName: {}\nID: {}\nIcon: {}\nExec: {}\nDisp: {}\n---\n", app.id, app.name, app.iconPath(), app.exec, app.display ? "yes" : "no");
// }
QGuiApplication app(argc, argv);
QCoreApplication::setApplicationName("waycast");
// Enable system theme support
app.setDesktopSettingsAware(true);
QQmlApplicationEngine engine;
// Set up layer shell before creating any windows
QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed, &app, []()
{ QCoreApplication::exit(-1); });
engine.loadFromModule("WayCast", "Main");
// Get the root objects and configure layer shell
auto rootObjects = engine.rootObjects();
if (!rootObjects.isEmpty())
{
QWindow *window = qobject_cast<QWindow *>(rootObjects.first());
if (window)
{
LayerShellQt::Window *layerWindow = LayerShellQt::Window::get(window);
if (layerWindow)
{
layerWindow->setLayer(LayerShellQt::Window::LayerTop);
layerWindow->setAnchors({});
layerWindow->setKeyboardInteractivity(LayerShellQt::Window::KeyboardInteractivityOnDemand);
// Now show the window after layer shell is configured
window->show();
}
}
}
return app.exec();
}

178
src/qrc_resources.cpp Normal file
View File

@ -0,0 +1,178 @@
/****************************************************************************
** Resource object code
**
** Created by: The Resource Compiler for Qt version 5.15.17
**
** WARNING! All changes made in this file will be lost!
*****************************************************************************/
static const unsigned char qt_resource_data[] = {
// /home/javi/projects/waycast/ui/main.qml
0x0,0x0,0x5,0x8e,
0x69,
0x6d,0x70,0x6f,0x72,0x74,0x20,0x51,0x74,0x51,0x75,0x69,0x63,0x6b,0x20,0x32,0x2e,
0x31,0x35,0xa,0x69,0x6d,0x70,0x6f,0x72,0x74,0x20,0x51,0x74,0x51,0x75,0x69,0x63,
0x6b,0x2e,0x57,0x69,0x6e,0x64,0x6f,0x77,0x20,0x32,0x2e,0x31,0x35,0xa,0x69,0x6d,
0x70,0x6f,0x72,0x74,0x20,0x51,0x74,0x51,0x75,0x69,0x63,0x6b,0x2e,0x43,0x6f,0x6e,
0x74,0x72,0x6f,0x6c,0x73,0x20,0x32,0x2e,0x31,0x35,0xa,0xa,0x41,0x70,0x70,0x6c,
0x69,0x63,0x61,0x74,0x69,0x6f,0x6e,0x57,0x69,0x6e,0x64,0x6f,0x77,0x20,0x7b,0xa,
0x20,0x20,0x20,0x20,0x69,0x64,0x3a,0x20,0x77,0x69,0x6e,0x64,0x6f,0x77,0xa,0x20,
0x20,0x20,0x20,0x77,0x69,0x64,0x74,0x68,0x3a,0x20,0x36,0x34,0x30,0xa,0x20,0x20,
0x20,0x20,0x68,0x65,0x69,0x67,0x68,0x74,0x3a,0x20,0x34,0x38,0x30,0xa,0x20,0x20,
0x20,0x20,0x76,0x69,0x73,0x69,0x62,0x6c,0x65,0x3a,0x20,0x74,0x72,0x75,0x65,0xa,
0x20,0x20,0x20,0x20,0x74,0x69,0x74,0x6c,0x65,0x3a,0x20,0x71,0x73,0x54,0x72,0x28,
0x22,0x51,0x74,0x20,0x51,0x75,0x69,0x63,0x6b,0x20,0x48,0x65,0x6c,0x6c,0x6f,0x20,
0x57,0x6f,0x72,0x6c,0x64,0x22,0x29,0xa,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,
0x20,0x52,0x65,0x63,0x74,0x61,0x6e,0x67,0x6c,0x65,0x20,0x7b,0xa,0x20,0x20,0x20,
0x20,0x20,0x20,0x20,0x20,0x61,0x6e,0x63,0x68,0x6f,0x72,0x73,0x2e,0x66,0x69,0x6c,
0x6c,0x3a,0x20,0x70,0x61,0x72,0x65,0x6e,0x74,0xa,0x20,0x20,0x20,0x20,0x20,0x20,
0x20,0x20,0x67,0x72,0x61,0x64,0x69,0x65,0x6e,0x74,0x3a,0x20,0x47,0x72,0x61,0x64,
0x69,0x65,0x6e,0x74,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
0x20,0x20,0x20,0x47,0x72,0x61,0x64,0x69,0x65,0x6e,0x74,0x53,0x74,0x6f,0x70,0x20,
0x7b,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3a,0x20,0x30,0x2e,0x30,0x3b,
0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x20,0x22,0x23,0x34,0x41,0x39,0x30,0x45,0x32,
0x22,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
0x47,0x72,0x61,0x64,0x69,0x65,0x6e,0x74,0x53,0x74,0x6f,0x70,0x20,0x7b,0x20,0x70,
0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3a,0x20,0x31,0x2e,0x30,0x3b,0x20,0x63,0x6f,
0x6c,0x6f,0x72,0x3a,0x20,0x22,0x23,0x33,0x35,0x37,0x41,0x42,0x44,0x22,0x20,0x7d,
0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,
0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x43,0x6f,0x6c,0x75,
0x6d,0x6e,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
0x20,0x61,0x6e,0x63,0x68,0x6f,0x72,0x73,0x2e,0x63,0x65,0x6e,0x74,0x65,0x72,0x49,
0x6e,0x3a,0x20,0x70,0x61,0x72,0x65,0x6e,0x74,0xa,0x20,0x20,0x20,0x20,0x20,0x20,
0x20,0x20,0x20,0x20,0x20,0x20,0x73,0x70,0x61,0x63,0x69,0x6e,0x67,0x3a,0x20,0x32,
0x30,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0xa,0x20,
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x54,0x65,0x78,0x74,0x20,
0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
0x20,0x20,0x69,0x64,0x3a,0x20,0x68,0x65,0x6c,0x6c,0x6f,0x54,0x65,0x78,0x74,0xa,
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
0x74,0x65,0x78,0x74,0x3a,0x20,0x22,0x48,0x65,0x6c,0x6c,0x6f,0x2c,0x20,0x51,0x74,
0x20,0x51,0x75,0x69,0x63,0x6b,0x21,0x22,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x6e,0x74,0x2e,0x70,0x69,
0x78,0x65,0x6c,0x53,0x69,0x7a,0x65,0x3a,0x20,0x33,0x32,0xa,0x20,0x20,0x20,0x20,
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6f,0x6e,0x74,
0x2e,0x62,0x6f,0x6c,0x64,0x3a,0x20,0x74,0x72,0x75,0x65,0xa,0x20,0x20,0x20,0x20,
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6c,0x6f,
0x72,0x3a,0x20,0x22,0x77,0x68,0x69,0x74,0x65,0x22,0xa,0x20,0x20,0x20,0x20,0x20,
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x61,0x6e,0x63,0x68,0x6f,
0x72,0x73,0x2e,0x68,0x6f,0x72,0x69,0x7a,0x6f,0x6e,0x74,0x61,0x6c,0x43,0x65,0x6e,
0x74,0x65,0x72,0x3a,0x20,0x70,0x61,0x72,0x65,0x6e,0x74,0x2e,0x68,0x6f,0x72,0x69,
0x7a,0x6f,0x6e,0x74,0x61,0x6c,0x43,0x65,0x6e,0x74,0x65,0x72,0xa,0x20,0x20,0x20,
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
0x20,0x20,0x20,0x20,0x42,0x75,0x74,0x74,0x6f,0x6e,0x20,0x7b,0xa,0x20,0x20,0x20,
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x74,0x65,0x78,
0x74,0x3a,0x20,0x22,0x43,0x6c,0x69,0x63,0x6b,0x20,0x6d,0x65,0x21,0x22,0xa,0x20,
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x61,
0x6e,0x63,0x68,0x6f,0x72,0x73,0x2e,0x68,0x6f,0x72,0x69,0x7a,0x6f,0x6e,0x74,0x61,
0x6c,0x43,0x65,0x6e,0x74,0x65,0x72,0x3a,0x20,0x70,0x61,0x72,0x65,0x6e,0x74,0x2e,
0x68,0x6f,0x72,0x69,0x7a,0x6f,0x6e,0x74,0x61,0x6c,0x43,0x65,0x6e,0x74,0x65,0x72,
0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
0x20,0x20,0x6f,0x6e,0x43,0x6c,0x69,0x63,0x6b,0x65,0x64,0x3a,0x20,0x7b,0xa,0x20,
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
0x20,0x20,0x20,0x68,0x65,0x6c,0x6c,0x6f,0x54,0x65,0x78,0x74,0x2e,0x74,0x65,0x78,
0x74,0x20,0x3d,0x20,0x22,0x42,0x75,0x74,0x74,0x6f,0x6e,0x20,0x63,0x6c,0x69,0x63,
0x6b,0x65,0x64,0x21,0x22,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x68,0x65,0x6c,0x6c,0x6f,0x54,
0x65,0x78,0x74,0x2e,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x22,0x23,0x46,0x46,
0x44,0x37,0x30,0x30,0x22,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x42,
0x75,0x74,0x74,0x6f,0x6e,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x74,0x65,0x78,0x74,0x3a,0x20,0x22,0x52,
0x65,0x73,0x65,0x74,0x22,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
0x20,0x20,0x20,0x20,0x20,0x20,0x61,0x6e,0x63,0x68,0x6f,0x72,0x73,0x2e,0x68,0x6f,
0x72,0x69,0x7a,0x6f,0x6e,0x74,0x61,0x6c,0x43,0x65,0x6e,0x74,0x65,0x72,0x3a,0x20,
0x70,0x61,0x72,0x65,0x6e,0x74,0x2e,0x68,0x6f,0x72,0x69,0x7a,0x6f,0x6e,0x74,0x61,
0x6c,0x43,0x65,0x6e,0x74,0x65,0x72,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x6e,0x43,0x6c,0x69,0x63,0x6b,
0x65,0x64,0x3a,0x20,0x7b,0xa,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x68,0x65,0x6c,0x6c,0x6f,0x54,
0x65,0x78,0x74,0x2e,0x74,0x65,0x78,0x74,0x20,0x3d,0x20,0x22,0x48,0x65,0x6c,0x6c,
0x6f,0x2c,0x20,0x51,0x74,0x20,0x51,0x75,0x69,0x63,0x6b,0x21,0x22,0xa,0x20,0x20,
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
0x20,0x20,0x68,0x65,0x6c,0x6c,0x6f,0x54,0x65,0x78,0x74,0x2e,0x63,0x6f,0x6c,0x6f,
0x72,0x20,0x3d,0x20,0x22,0x77,0x68,0x69,0x74,0x65,0x22,0xa,0x20,0x20,0x20,0x20,
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,
0x20,0x20,0x20,0x20,0x7d,0xa,0x20,0x20,0x20,0x20,0x7d,0xa,0x7d,
};
static const unsigned char qt_resource_name[] = {
// ui
0x0,0x2,
0x0,0x0,0x7,0xb9,
0x0,0x75,
0x0,0x69,
// main.qml
0x0,0x8,
0x8,0x1,0x5a,0x5c,
0x0,0x6d,
0x0,0x61,0x0,0x69,0x0,0x6e,0x0,0x2e,0x0,0x71,0x0,0x6d,0x0,0x6c,
};
static const unsigned char qt_resource_struct[] = {
// :
0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
0x0,0x0,0x0,0x49,0x77,0x38,0x70,0x0,
// :/ui
0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2,
0x0,0x0,0x0,0x49,0x77,0x38,0x70,0x0,
// :/ui/main.qml
0x0,0x0,0x0,0xa,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,
0x0,0x0,0x0,0x49,0x77,0x38,0x70,0x0,
};
#ifdef QT_NAMESPACE
# define QT_RCC_PREPEND_NAMESPACE(name) ::QT_NAMESPACE::name
# define QT_RCC_MANGLE_NAMESPACE0(x) x
# define QT_RCC_MANGLE_NAMESPACE1(a, b) a##_##b
# define QT_RCC_MANGLE_NAMESPACE2(a, b) QT_RCC_MANGLE_NAMESPACE1(a,b)
# define QT_RCC_MANGLE_NAMESPACE(name) QT_RCC_MANGLE_NAMESPACE2( \
QT_RCC_MANGLE_NAMESPACE0(name), QT_RCC_MANGLE_NAMESPACE0(QT_NAMESPACE))
#else
# define QT_RCC_PREPEND_NAMESPACE(name) name
# define QT_RCC_MANGLE_NAMESPACE(name) name
#endif
#ifdef QT_NAMESPACE
namespace QT_NAMESPACE {
#endif
bool qRegisterResourceData(int, const unsigned char *, const unsigned char *, const unsigned char *);
bool qUnregisterResourceData(int, const unsigned char *, const unsigned char *, const unsigned char *);
#ifdef QT_NAMESPACE
}
#endif
int QT_RCC_MANGLE_NAMESPACE(qInitResources)();
int QT_RCC_MANGLE_NAMESPACE(qInitResources)()
{
int version = 3;
QT_RCC_PREPEND_NAMESPACE(qRegisterResourceData)
(version, qt_resource_struct, qt_resource_name, qt_resource_data);
return 1;
}
int QT_RCC_MANGLE_NAMESPACE(qCleanupResources)();
int QT_RCC_MANGLE_NAMESPACE(qCleanupResources)()
{
int version = 3;
QT_RCC_PREPEND_NAMESPACE(qUnregisterResourceData)
(version, qt_resource_struct, qt_resource_name, qt_resource_data);
return 1;
}
namespace {
struct initializer {
initializer() { QT_RCC_MANGLE_NAMESPACE(qInitResources)(); }
~initializer() { QT_RCC_MANGLE_NAMESPACE(qCleanupResources)(); }
} dummy;
}

123
ui/Main.qml Normal file
View File

@ -0,0 +1,123 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Window
import QtQuick.Controls.Material
import QtQuick.Controls.Universal
ApplicationWindow {
id: win
visible: false
width: 600
height: 400
flags: Qt.FramelessWindowHint
property int timeoutInterval: 5000
Shortcut {
sequence: "Escape"
onActivated: Qt.quit()
}
Component.onCompleted: {
forceActiveFocus()
}
Rectangle {
anchors.fill: parent
radius: 8
border.width: 1
border.color: palette.mid
color: palette.window
Column {
anchors.fill: parent
anchors.margins: 10
spacing: 5
TextField {
id: searchField
width: parent.width
placeholderText: "Type to search applications..."
selectByMouse: true
focus: true
Keys.onDownPressed: listView.incrementCurrentIndex()
Keys.onUpPressed: listView.decrementCurrentIndex()
Keys.onReturnPressed: {
if (listView.currentItem) {
console.log("Selected:", listModel.get(listView.currentIndex).name)
}
}
}
ScrollView {
width: parent.width
height: parent.height - searchField.height - parent.spacing
clip: true
ListView {
id: listView
model: listModel
currentIndex: 0
highlightFollowsCurrentItem: true
highlight: Rectangle {
color: palette.highlight
radius: 4
}
delegate: ItemDelegate {
width: listView.width
height: 40
Rectangle {
anchors.fill: parent
color: parent.hovered ? palette.alternateBase : "transparent"
radius: 4
}
Row {
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
anchors.margins: 10
spacing: 10
Rectangle {
width: 24
height: 24
color: palette.button
radius: 4
Text {
anchors.centerIn: parent
text: "📱"
font.pixelSize: 16
}
}
Text {
anchors.verticalCenter: parent.verticalCenter
text: model.name
color: palette.text
font.pixelSize: 14
}
}
onClicked: {
listView.currentIndex = index
console.log("Clicked:", model.name)
}
}
}
}
}
}
ListModel {
id: listModel
ListElement { name: "Firefox"; exec: "firefox" }
ListElement { name: "Terminal"; exec: "gnome-terminal" }
ListElement { name: "File Manager"; exec: "nautilus" }
ListElement { name: "Text Editor"; exec: "gedit" }
ListElement { name: "Calculator"; exec: "gnome-calculator" }
}
}

5
ui/resources.qrc Normal file
View File

@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/ui">
<file>main.qml</file>
</qresource>
</RCC>