Skip to content
This repository was archived by the owner on May 2, 2022. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .claude/settings.local.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"permissions": {
"allow": [
"Bash(mkdir:*)"
],
"deny": [],
"ask": []
}
}
98 changes: 77 additions & 21 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,31 +1,87 @@
cmake_minimum_required(VERSION 3.1)
project(tray VERSION 0.2 DESCRIPTION "A cross-platform C++ system tray library")
cmake_minimum_required(VERSION 3.14)

file(GLOB src
"tray/src/*.cpp"
"tray/src/*/*.cpp"
"tray/src/*/*/*.cpp"
)
# ──────────────────────────────────────────────────────────────────────────────
project(tray VERSION 0.2 DESCRIPTION "A cross-platform C++ system-tray library"
LANGUAGES CXX)

# ─── source files ─────────────────────────────────────────────────────────────
# Everything under tray/src/ (one recursive glob keeps it readable)
file(GLOB_RECURSE src CONFIGURE_DEPENDS "tray/src/*.cpp")
# macOS uses Objective-C++ (.mm files)
if(APPLE)
file(GLOB_RECURSE src_mm CONFIGURE_DEPENDS "tray/src/*.mm")
list(APPEND src ${src_mm})
endif()

add_library(tray STATIC ${src})

if (UNIX)
# ─── public headers ───────────────────────────────────────────────────────────
target_include_directories(tray
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/tray/include>
$<INSTALL_INTERFACE:include>)

# ─── platform-specific dependencies ───────────────────────────────────────────
if(UNIX AND NOT APPLE)
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK3 REQUIRED gtk+-3.0)
pkg_check_modules(APPINDICATOR REQUIRED appindicator3-0.1)

target_link_libraries(tray INTERFACE ${GTK3_LIBRARIES} ${APPINDICATOR_LIBRARIES})
target_compile_options(tray PRIVATE -Wall -Wextra -Werror -pedantic -Wno-unused-lambda-capture)
target_include_directories(tray SYSTEM PUBLIC ${GTK3_INCLUDE_DIRS} ${APPINDICATOR_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR})
endif()
# Ask pkg-config to give us IMPORTED targets that already carry their flags
pkg_check_modules(GTK3 REQUIRED IMPORTED_TARGET gtk+-3.0)
pkg_check_modules(APPINDICATOR REQUIRED IMPORTED_TARGET ayatana-appindicator3-0.1)
pkg_check_modules(CAIRO REQUIRED IMPORTED_TARGET cairo)

# Link – using the IMPORTED targets automatically propagates include paths,
# link libs (-lgtk-3, -lgdk-3, -lcairo, …) and extra compile flags (-pthread)
target_link_libraries(tray
PUBLIC # make the deps visible to consumers
PkgConfig::GTK3
PkgConfig::APPINDICATOR
PkgConfig::CAIRO)

# Extra warnings (but **no -Werror** on external code)
if(CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang)$")
target_compile_options(tray PRIVATE -Wall -Wextra -pedantic)
endif()

# Only add -Wno-unused-lambda-capture if the compiler supports it
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag("-Wno-unused-lambda-capture" HAS_WNO_UNUSED_LAMBDA)
if(HAS_WNO_UNUSED_LAMBDA)
target_compile_options(tray PRIVATE -Wno-unused-lambda-capture)
endif()
elseif(APPLE)
# macOS: Enable Objective-C++ for .mm files
enable_language(OBJCXX)
set_source_files_properties(${src_mm} PROPERTIES
COMPILE_FLAGS "-x objective-c++")

target_include_directories(tray SYSTEM PUBLIC "tray/include")
# Find and link Cocoa framework
find_library(COCOA_LIBRARY Cocoa REQUIRED)
target_link_libraries(tray PUBLIC ${COCOA_LIBRARY})

# Extra warnings
if(CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang)$")
target_compile_options(tray PRIVATE -Wall -Wextra -pedantic)
endif()
endif()

# ─── language & misc properties ───────────────────────────────────────────────
target_compile_features(tray PRIVATE cxx_std_17)
set_target_properties(tray PROPERTIES
CXX_STANDARD 17
CXX_EXTENSIONS OFF
CXX_STANDARD_REQUIRED ON)

set_target_properties(tray PROPERTIES VERSION ${PROJECT_VERSION})
set_target_properties(tray PROPERTIES PROJECT_NAME ${PROJECT_NAME})
set_target_properties(tray PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED YES
CXX_EXTENSIONS NO
VERSION ${PROJECT_VERSION})

# (Optional) install rules – comment out if you don’t need “make install”
install(TARGETS tray
EXPORT trayTargets
ARCHIVE DESTINATION lib
INCLUDES DESTINATION include)

install(DIRECTORY tray/include/ DESTINATION include)

export(EXPORT trayTargets
FILE "${CMAKE_CURRENT_BINARY_DIR}/trayTargets.cmake")

13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ A cross-platform C++17 library that allows you to create simple tray menus.
| -------- | -------------- |
| Windows | WinAPI |
| Linux | AppIndicator |
| macOS | NSStatusBar |

## Dependencies
- Linux
- libappindicator-gtk3
- macOS
- Cocoa framework (included with macOS SDK)

## Basic Usage
```cpp
Expand All @@ -29,7 +32,9 @@ int main()
return 0;
}
```
> On Windows it is not necessary to pass an icon path as icon, you can also use an icon-resource or an existing HICON.
> **Platform-specific notes:**
> - **Windows**: You can pass an icon path, icon-resource, or an existing HICON
> - **macOS**: Icon should be a path to a PNG file or a system icon name (e.g., "NSStatusAvailable")

## Menu components
### Button
Expand All @@ -44,11 +49,13 @@ Button(std::string text, std::function<void()> callback);
ImageButton(std::string text, Image image, std::function<void()> callback);
```
**Parameters:**
- `image` - The image tho show
- `image` - The image to show
- Windows
> Image should either be a path to a bitmap or an HBITMAP
- Linux
> Image should either be a path to a png or a GtkImage
> Image should either be a path to a PNG or a GtkImage
- macOS
> Image should be a path to a PNG file or NSImage pointer
- `callback` - The function that is called when the button is pressed
----
### Toggle
Expand Down
2 changes: 1 addition & 1 deletion example/simple/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.1)
cmake_minimum_required(VERSION 3.14)
project(tray-example VERSION 0.1)

add_executable(tray-example "main.cpp")
Expand Down
12 changes: 12 additions & 0 deletions tray/include/core/icon.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,17 @@ namespace Tray

operator HICON();
};
#elif defined(__APPLE__)
class Icon
{
void *nsImage; // NSImage*

public:
~Icon();
Icon(const char *path);
Icon(const std::string &path);

operator void*();
};
#endif
} // namespace Tray
13 changes: 13 additions & 0 deletions tray/include/core/image.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,18 @@ namespace Tray

operator HBITMAP();
};
#elif defined(__APPLE__)
class Image
{
void *nsImage; // NSImage*

public:
~Image();
Image(void *image);
Image(const char *path);
Image(const std::string &path);

operator void*();
};
#endif
} // namespace Tray
5 changes: 3 additions & 2 deletions tray/include/core/linux/tray.hpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#pragma once
#if defined(__linux__)
#include <core/traybase.hpp>
#include <libappindicator/app-indicator.h>
#include <libayatana-appindicator/app-indicator.h>

namespace Tray
{
Expand All @@ -22,6 +22,7 @@ namespace Tray
void run() override;
void exit() override;
void update() override;
void pump() override;
};
} // namespace Tray
#endif
#endif
44 changes: 44 additions & 0 deletions tray/include/core/macos/tray.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#pragma once
#if defined(__APPLE__)
#include <core/traybase.hpp>

// Forward declarations to avoid Objective-C in header
#ifdef __OBJC__
@class NSStatusItem;
@class NSMenu;
@class NSMenuItem;
@class TrayDelegate;
#else
typedef void NSStatusItem;
typedef void NSMenu;
typedef void NSMenuItem;
typedef void TrayDelegate;
#endif

namespace Tray
{
class Tray : public BaseTray
{
NSStatusItem *statusItem;
NSMenu *menu;
TrayDelegate *delegate;

static void constructIntoMenu(NSMenu *menu, const std::vector<std::shared_ptr<TrayEntry>> &, Tray *parent);
static NSMenu *construct(const std::vector<std::shared_ptr<TrayEntry>> &, Tray *parent);
static void menuItemClicked(void *context);

public:
~Tray();
Tray(std::string identifier, Icon icon);
template <typename... T> Tray(std::string identifier, Icon icon, const T &...entries) : Tray(identifier, icon)
{
addEntries(entries...);
}

void run() override;
void exit() override;
void update() override;
void pump() override;
};
} // namespace Tray
#endif
1 change: 1 addition & 0 deletions tray/include/core/traybase.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ namespace Tray
virtual void run() = 0;
virtual void exit() = 0;
virtual void update() = 0;
virtual void pump() = 0;
std::vector<std::shared_ptr<TrayEntry>> getEntries();
};
} // namespace Tray
1 change: 1 addition & 0 deletions tray/include/core/windows/tray.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ namespace Tray
void run() override;
void exit() override;
void update() override;
void pump() override;
};
} // namespace Tray
#endif
2 changes: 2 additions & 0 deletions tray/include/tray.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@
#include <core/windows/tray.hpp>
#elif defined(__linux__)
#include <core/linux/tray.hpp>
#elif defined(__APPLE__)
#include <core/macos/tray.hpp>
#endif
23 changes: 21 additions & 2 deletions tray/src/core/linux/tray.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#if defined(__linux__)
#include <core/linux/tray.hpp>
#include <libappindicator/app-indicator.h>
#include <libayatana-appindicator/app-indicator.h>
#include <stdexcept>

#include <components/button.hpp>
Expand All @@ -20,6 +20,20 @@ Tray::Tray::Tray(std::string identifier, Icon icon) : BaseTray(std::move(identif
}

appIndicator = app_indicator_new(this->identifier.c_str(), this->icon, APP_INDICATOR_CATEGORY_APPLICATION_STATUS);

#if 0
gchar *current_dir = g_get_current_dir();
g_print("%s current_dir:%s\n", G_STRFUNC, current_dir);
gchar *theme_path = g_build_path(G_DIR_SEPARATOR_S,
current_dir,
"data",
NULL);
g_print("%s path:%s\n", G_STRFUNC, theme_path);
app_indicator_set_icon_theme_path(appIndicator, theme_path);
g_free(current_dir);
g_free(theme_path);
#endif

app_indicator_set_status(appIndicator, APP_INDICATOR_STATUS_ACTIVE);
}

Expand Down Expand Up @@ -168,4 +182,9 @@ void Tray::Tray::run()
}
}

#endif
void Tray::Tray::pump()
{
gtk_main_iteration_do(false);
}

#endif
Loading