feat: initial project structure

This commit is contained in:
Daniil Polyakov 2026-04-01 22:55:34 +03:00
parent 65a3a59cf7
commit e005abeee2
16 changed files with 549 additions and 0 deletions

89
CMakeLists.txt Normal file
View File

@ -0,0 +1,89 @@
cmake_minimum_required(VERSION 3.16)
project(LEZExplorerUIPlugin VERSION 1.0.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
# Find Qt packages
find_package(Qt6 REQUIRED COMPONENTS Core Widgets)
# Try to find the component-interfaces package first
find_package(component-interfaces QUIET)
# If not found, use the local interfaces folder
if(NOT component-interfaces_FOUND)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/interfaces)
add_library(component-interfaces INTERFACE)
target_include_directories(component-interfaces INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/interfaces)
endif()
# Source files
set(SOURCES
src/ExplorerPlugin.cpp
src/ExplorerPlugin.h
src/ExplorerWidget.cpp
src/ExplorerWidget.h
)
# Create the plugin library
add_library(lez_explorer_ui SHARED ${SOURCES})
# Set output name without lib prefix
set_target_properties(lez_explorer_ui PROPERTIES
PREFIX ""
OUTPUT_NAME "lez_explorer_ui"
)
# Include directories
target_include_directories(lez_explorer_ui PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_BINARY_DIR}
)
# Link against libraries
target_link_libraries(lez_explorer_ui PRIVATE
Qt6::Core
Qt6::Widgets
component-interfaces
)
# Set common properties for both platforms
set_target_properties(lez_explorer_ui PROPERTIES
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/modules"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/modules"
BUILD_WITH_INSTALL_RPATH TRUE
SKIP_BUILD_RPATH FALSE
)
if(APPLE)
set_target_properties(lez_explorer_ui PROPERTIES
INSTALL_RPATH "@loader_path"
INSTALL_NAME_DIR "@rpath"
BUILD_WITH_INSTALL_NAME_DIR TRUE
)
add_custom_command(TARGET lez_explorer_ui POST_BUILD
COMMAND install_name_tool -id "@rpath/lez_explorer_ui.dylib" $<TARGET_FILE:lez_explorer_ui>
COMMENT "Updating library paths for macOS"
)
else()
set_target_properties(lez_explorer_ui PROPERTIES
INSTALL_RPATH "$ORIGIN"
INSTALL_RPATH_USE_LINK_PATH FALSE
)
endif()
install(TARGETS lez_explorer_ui
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/logos/modules
RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR}/logos/modules
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/logos/modules
)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/metadata.json
DESTINATION ${CMAKE_INSTALL_DATADIR}/lez-explorer-ui
)
message(STATUS "LEZ Explorer UI Plugin configured successfully")

44
app/CMakeLists.txt Normal file
View File

@ -0,0 +1,44 @@
cmake_minimum_required(VERSION 3.16)
project(LEZExplorerUIApp LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
# Find Qt packages
find_package(Qt6 REQUIRED COMPONENTS Core Widgets)
# Set output directories
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
# Create the executable
add_executable(lez-explorer-ui-app
main.cpp
mainwindow.cpp
mainwindow.h
)
# Link libraries
target_link_libraries(lez-explorer-ui-app PRIVATE
Qt6::Core
Qt6::Widgets
)
# Set RPATH settings for the executable
if(APPLE)
set_target_properties(lez-explorer-ui-app PROPERTIES
INSTALL_RPATH "@executable_path/../lib"
BUILD_WITH_INSTALL_RPATH TRUE
)
elseif(UNIX)
set_target_properties(lez-explorer-ui-app PROPERTIES
INSTALL_RPATH "$ORIGIN/../lib"
BUILD_WITH_INSTALL_RPATH TRUE
)
endif()
# Install rules
install(TARGETS lez-explorer-ui-app
RUNTIME DESTINATION bin
)

13
app/main.cpp Normal file
View File

@ -0,0 +1,13 @@
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
MainWindow window;
window.show();
return app.exec();
}

60
app/mainwindow.cpp Normal file
View File

@ -0,0 +1,60 @@
#include "mainwindow.h"
#include <QtWidgets>
MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent)
{
setupUi();
}
MainWindow::~MainWindow() = default;
void MainWindow::setupUi()
{
// Determine the appropriate plugin extension based on the platform
QString pluginExtension;
#if defined(Q_OS_WIN)
pluginExtension = ".dll";
#elif defined(Q_OS_MAC)
pluginExtension = ".dylib";
#else
pluginExtension = ".so";
#endif
QString pluginPath = QCoreApplication::applicationDirPath() + "/../lez_explorer_ui" + pluginExtension;
QPluginLoader loader(pluginPath);
QWidget* explorerWidget = nullptr;
if (loader.load()) {
QObject* plugin = loader.instance();
if (plugin) {
QMetaObject::invokeMethod(plugin, "createWidget",
Qt::DirectConnection,
Q_RETURN_ARG(QWidget*, explorerWidget));
}
}
if (explorerWidget) {
setCentralWidget(explorerWidget);
} else {
qWarning() << "Failed to load LEZ Explorer UI plugin from:" << pluginPath;
qWarning() << "Error:" << loader.errorString();
auto* fallbackWidget = new QWidget(this);
auto* layout = new QVBoxLayout(fallbackWidget);
auto* messageLabel = new QLabel("LEZ Explorer UI module not loaded", fallbackWidget);
QFont font = messageLabel->font();
font.setPointSize(14);
messageLabel->setFont(font);
messageLabel->setAlignment(Qt::AlignCenter);
layout->addWidget(messageLabel);
setCentralWidget(fallbackWidget);
}
setWindowTitle("LEZ Explorer");
resize(1024, 768);
}

14
app/mainwindow.h Normal file
View File

@ -0,0 +1,14 @@
#pragma once
#include <QMainWindow>
class MainWindow : public QMainWindow {
Q_OBJECT
public:
explicit MainWindow(QWidget* parent = nullptr);
~MainWindow() override;
private:
void setupUi();
};

27
flake.lock generated Normal file
View File

@ -0,0 +1,27 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1751274312,
"narHash": "sha256-/bVBlRpECLVzjV19t5KMdMFWSwKLtb5RyXdjz3LJT+g=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "50ab793786d9de88ee30ec4e4c24fb4236fc2674",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-24.11",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

53
flake.nix Normal file
View File

@ -0,0 +1,53 @@
{
description = "LEZ Explorer UI - A Qt block explorer for the Logos Execution Zone";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
};
outputs = { self, nixpkgs }:
let
systems = [ "aarch64-darwin" "x86_64-darwin" "aarch64-linux" "x86_64-linux" ];
forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f {
pkgs = import nixpkgs { inherit system; };
});
in
{
packages = forAllSystems ({ pkgs }:
let
common = import ./nix/default.nix { inherit pkgs; };
src = ./.;
lib = import ./nix/lib.nix { inherit pkgs common src; };
app = import ./nix/app.nix {
inherit pkgs common src;
lezExplorerUI = lib;
};
in
{
lez-explorer-ui-lib = lib;
app = app;
lib = lib;
default = lib;
}
);
devShells = forAllSystems ({ pkgs }: {
default = pkgs.mkShell {
nativeBuildInputs = [
pkgs.cmake
pkgs.ninja
pkgs.pkg-config
];
buildInputs = [
pkgs.qt6.qtbase
];
shellHook = ''
echo "LEZ Explorer UI development environment"
'';
};
});
};
}

17
interfaces/IComponent.h Normal file
View File

@ -0,0 +1,17 @@
#pragma once
#include <QObject>
#include <QWidget>
#include <QtPlugin>
class LogosAPI;
class IComponent {
public:
virtual ~IComponent() = default;
virtual QWidget* createWidget(LogosAPI* logosAPI = nullptr) = 0;
virtual void destroyWidget(QWidget* widget) = 0;
};
#define IComponent_iid "com.logos.component.IComponent"
Q_DECLARE_INTERFACE(IComponent, IComponent_iid)

10
metadata.json Normal file
View File

@ -0,0 +1,10 @@
{
"name": "lez_explorer_ui",
"version": "1.0.0",
"description": "LEZ Explorer UI - A block explorer for the Logos Execution Zone",
"type": "ui",
"main": "lez_explorer_ui",
"dependencies": [],
"category": "explorer",
"capabilities": ["ui_components"]
}

64
nix/app.nix Normal file
View File

@ -0,0 +1,64 @@
# Builds the lez-explorer-ui-app standalone application
{ pkgs, common, src, lezExplorerUI }:
pkgs.stdenv.mkDerivation rec {
pname = "lez-explorer-ui-app";
version = common.version;
inherit src;
inherit (common) buildInputs meta;
nativeBuildInputs = common.nativeBuildInputs ++ pkgs.lib.optionals pkgs.stdenv.isLinux [ pkgs.patchelf ];
# This is a GUI application, enable Qt wrapping
dontWrapQtApps = false;
qtLibPath = pkgs.lib.makeLibraryPath ([
pkgs.qt6.qtbase
] ++ pkgs.lib.optionals pkgs.stdenv.isLinux [
pkgs.libglvnd
]);
qtPluginPath = "${pkgs.qt6.qtbase}/lib/qt-6/plugins";
qtWrapperArgs = [
"--prefix" "LD_LIBRARY_PATH" ":" qtLibPath
"--prefix" "QT_PLUGIN_PATH" ":" qtPluginPath
];
configurePhase = ''
runHook preConfigure
cmake -S app -B build \
-GNinja \
-DCMAKE_BUILD_TYPE=Release
runHook postConfigure
'';
buildPhase = ''
runHook preBuild
cmake --build build
runHook postBuild
'';
installPhase = ''
runHook preInstall
mkdir -p $out/bin $out/lib
# Install the app binary
cp build/bin/lez-explorer-ui-app $out/bin/
# Determine platform-specific plugin extension
OS_EXT="so"
case "$(uname -s)" in
Darwin) OS_EXT="dylib";;
esac
# Copy the explorer UI plugin to root directory (loaded relative to binary)
if [ -f "${lezExplorerUI}/lib/lez_explorer_ui.$OS_EXT" ]; then
cp -L "${lezExplorerUI}/lib/lez_explorer_ui.$OS_EXT" "$out/"
fi
runHook postInstall
'';
}

27
nix/default.nix Normal file
View File

@ -0,0 +1,27 @@
# Common build configuration shared across all packages
{ pkgs }:
{
pname = "lez-explorer-ui";
version = "1.0.0";
nativeBuildInputs = [
pkgs.cmake
pkgs.ninja
pkgs.pkg-config
pkgs.qt6.wrapQtAppsHook
];
buildInputs = [
pkgs.qt6.qtbase
];
cmakeFlags = [
"-GNinja"
];
meta = with pkgs.lib; {
description = "LEZ Explorer UI - A Qt block explorer for the Logos Execution Zone";
platforms = platforms.unix;
};
}

45
nix/lib.nix Normal file
View File

@ -0,0 +1,45 @@
# Builds the lez-explorer-ui library (Qt plugin)
{ pkgs, common, src }:
pkgs.stdenv.mkDerivation {
pname = "${common.pname}-lib";
version = common.version;
inherit src;
inherit (common) buildInputs cmakeFlags meta;
nativeBuildInputs = common.nativeBuildInputs;
# Library (Qt plugin), not an app — no Qt wrapper
dontWrapQtApps = true;
configurePhase = ''
runHook preConfigure
cmake -S . -B build \
-GNinja \
-DCMAKE_BUILD_TYPE=Release
runHook postConfigure
'';
buildPhase = ''
runHook preBuild
cmake --build build
runHook postBuild
'';
installPhase = ''
runHook preInstall
mkdir -p $out/lib
if [ -f build/modules/lez_explorer_ui.dylib ]; then
cp build/modules/lez_explorer_ui.dylib $out/lib/
elif [ -f build/modules/lez_explorer_ui.so ]; then
cp build/modules/lez_explorer_ui.so $out/lib/
else
echo "Error: No library file found in build/modules/"
ls -la build/modules/ 2>/dev/null || true
exit 1
fi
runHook postInstall
'';
}

17
src/ExplorerPlugin.cpp Normal file
View File

@ -0,0 +1,17 @@
#include "ExplorerPlugin.h"
#include "ExplorerWidget.h"
ExplorerPlugin::ExplorerPlugin(QObject* parent)
: QObject(parent)
{
}
QWidget* ExplorerPlugin::createWidget(LogosAPI* /*logosAPI*/)
{
return new ExplorerWidget();
}
void ExplorerPlugin::destroyWidget(QWidget* widget)
{
delete widget;
}

18
src/ExplorerPlugin.h Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#include "IComponent.h"
#include <QObject>
#include <QtPlugin>
class ExplorerPlugin : public QObject, public IComponent {
Q_OBJECT
Q_INTERFACES(IComponent)
Q_PLUGIN_METADATA(IID IComponent_iid FILE "metadata.json")
public:
explicit ExplorerPlugin(QObject* parent = nullptr);
Q_INVOKABLE QWidget* createWidget(LogosAPI* logosAPI = nullptr) override;
void destroyWidget(QWidget* widget) override;
};

38
src/ExplorerWidget.cpp Normal file
View File

@ -0,0 +1,38 @@
#include "ExplorerWidget.h"
#include <QPainter>
#include <QVBoxLayout>
#include <QLabel>
ExplorerWidget::ExplorerWidget(QWidget* parent)
: QWidget(parent)
{
auto* layout = new QVBoxLayout(this);
auto* label = new QLabel("LEZ Explorer", this);
label->setAlignment(Qt::AlignCenter);
QFont font = label->font();
font.setPointSize(24);
font.setBold(true);
label->setFont(font);
label->setStyleSheet("color: white;");
layout->addWidget(label);
}
void ExplorerWidget::paintEvent(QPaintEvent* /*event*/)
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
// Draw a blue rectangle filling the widget
painter.setBrush(QColor(30, 60, 120));
painter.setPen(Qt::NoPen);
painter.drawRect(rect());
// Draw a lighter inner rectangle
int margin = 40;
QRect inner = rect().adjusted(margin, margin, -margin, -margin);
painter.setBrush(QColor(40, 80, 160));
painter.drawRoundedRect(inner, 16, 16);
}

13
src/ExplorerWidget.h Normal file
View File

@ -0,0 +1,13 @@
#pragma once
#include <QWidget>
class ExplorerWidget : public QWidget {
Q_OBJECT
public:
explicit ExplorerWidget(QWidget* parent = nullptr);
protected:
void paintEvent(QPaintEvent* event) override;
};