Bazaar 是一款專為 GNOME 設計的現代化應用商店,專注於從 Flatpak 遠程倉庫發現和安裝應用程序及插件。它強調對 Linux 桌面開發者的支持,提供流暢的多線程體驗和本地化內容推薦。
Bazaar
Bazaar 是一款專為 GNOME 桌面環境設計的新一代應用商店,專注於從 Flatpak 遠程倉庫(特別是 Flathub)發現和安裝應用程序及插件。
功能特性
- 流暢的用户體驗:高度多線程設計,保證界面流暢運行
- 後台服務架構:即使關閉所有窗口也能保持狀態,支持後台任務執行
- 本地化內容推薦:可配置的"精選"標籤頁,支持地區化體驗
- 完整的 Flatpak 支持:支持應用程序、運行時和插件的安裝管理
- GNOME Shell 集成:實現搜索提供者 DBus 接口
- KDE Plasma 支持:提供 KRunner 插件
- 多語言支持:完善的國際化翻譯系統
安裝指南
預構建版本
通過 Flathub 安裝預構建版本:
從源碼編譯
需要安裝以下依賴項:
|
依賴包
|
pkg-config 名稱
|
最低版本
|
用途
|
|
gtk4
|
|
libadwaita 要求
|
用户界面
|
|
libadwaita
|
|
|
GNOME 外觀
|
|
libdex
|
|
|
異步助手
|
|
flatpak
|
|
|
Flatpak 管理
|
|
appstream
|
|
|
應用元數據獲取
|
|
xmlb
|
|
|
XML 處理
|
|
glycin
|
|
|
圖像解碼
|
|
glycin-gtk4
|
|
|
圖像紋理轉換
|
|
libyaml
|
|
|
YAML 文件解析
|
|
libsoup
|
|
|
HTTP 操作
|
|
json-glib
|
|
|
Flathub HTTP 響應解析
|
編譯安裝步驟:
meson setup build --prefix=/usr/local
ninja -C build
sudo ninja -C build install
bazaar
使用説明
基本使用
啓動 Bazaar 後,您可以:
- 瀏覽 Flathub 上的應用程序
- 查看精選推薦內容
- 搜索特定應用程序
- 管理已安裝的應用
開發調試
獲取已安裝版本信息:
flatpak info io.github.kolunmi.Bazaar
啓用詳細輸出:
G_MESSAGES_DEBUG=all flatpak run io.github.kolunmi.Bazaar
核心代碼
應用圖塊組件 (bz-app-tile.c)
/* bz-app-tile.c - 應用圖塊顯示組件 */
struct _BzAppTile {
GtkButton parent_instance;
BzEntryGroup *group;
};
G_DEFINE_FINAL_TYPE(BzAppTile, bz_app_tile, GTK_TYPE_BUTTON);
BzAppTile *bz_app_tile_new(void) {
return g_object_new(BZ_TYPE_APP_TILE, NULL);
}
BzEntryGroup *bz_app_tile_get_group(BzAppTile *self) {
g_return_val_if_fail(BZ_IS_APP_TILE(self), NULL);
return self->group;
}
void bz_app_tile_set_group(BzAppTile *self, BzEntryGroup *group) {
g_return_if_fail(BZ_IS_APP_TILE(self));
g_clear_object(&self->group);
if (group != NULL)
self->group = g_object_ref(group);
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_GROUP]);
}
異步紋理加載 (bz-async-texture.c)
/* bz-async-texture.c - 異步圖像加載和緩存 */
#define MAX_CONCURRENT_GLYCIN 32
#define CACHE_INVALID_AGE (G_TIME_SPAN_DAY * 1)
struct _BzAsyncTexture {
GObject parent_instance;
GFile *source;
char *source_uri;
GFile *cache_into;
gboolean lazy;
DexFuture *task;
};
BzAsyncTexture *bz_async_texture_new(GFile *source, GFile *cache_into) {
return g_object_new(BZ_TYPE_ASYNC_TEXTURE,
"source", source,
"cache-into", cache_into,
NULL);
}
void bz_async_texture_ensure(BzAsyncTexture *self) {
// 確保紋理加載開始
if (self->task == NULL && !self->lazy) {
self->task = dex_future_new_async(load_fiber, load_data_new(self));
}
}
內容提供者 (bz-content-provider.c)
/* bz-content-provider.c - 內容管理和提供 */
struct _BzContentProvider {
GObject parent_instance;
BzYamlParser *yaml_parser;
GListModel *input_files;
BzApplicationMapFactory *factory;
GListStore *outputs;
};
BzContentProvider *bz_content_provider_new(void) {
return g_object_new(BZ_TYPE_CONTENT_PROVIDER, NULL);
}
void bz_content_provider_set_input_files(BzContentProvider *self,
GListModel *input_files) {
g_return_if_fail(BZ_IS_CONTENT_PROVIDER(self));
if (self->input_files != NULL)
g_signal_handlers_disconnect_by_func(self->input_files,
items_changed, self);
g_set_object(&self->input_files, input_files);
if (input_files != NULL)
g_signal_connect(input_files, "items-changed",
G_CALLBACK(items_changed), self);
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_INPUT_FILES]);
}
Flatpak 後端處理 (bz-flatpak-instance.c)
/* bz-flatpak-instance.c - Flatpak 後端實現 */
struct _BzFlatpakInstance {
GObject parent_instance;
DexScheduler *scheduler;
FlatpakInstallation *system;
FlatpakInstallation *user;
GPtrArray *notif_channels;
};
DexFuture *bz_flatpak_instance_new(void) {
g_autoptr(BzFlatpakInstance) instance = NULL;
g_autoptr(DexPromise) promise = NULL;
instance = g_object_new(BZ_TYPE_FLATPAK_INSTANCE, NULL);
promise = dex_promise_new();
// 初始化 Flatpak 實例
dex_future_wait_for(bz_flatpak_instance_init_flatpak(instance));
return DEX_FUTURE(g_steal_pointer(&promise));
}
動態列表視圖 (bz-dynamic-list-view.c)
/* bz-dynamic-list-view.c - 動態列表視圖管理 */
struct _BzDynamicListView {
AdwBin parent_instance;
GListModel *model;
gboolean scroll;
BzDynamicListViewKind noscroll_kind;
GType child_type;
};
BzDynamicListView *bz_dynamic_list_view_new(void) {
return g_object_new(BZ_TYPE_DYNAMIC_LIST_VIEW, NULL);
}
void bz_dynamic_list_view_set_model(BzDynamicListView *self,
GListModel *model) {
g_return_if_fail(BZ_IS_DYNAMIC_LIST_VIEW(self));
if (self->model == model)
return;
if (self->model != NULL)
g_signal_handlers_disconnect_by_func(self->model,
items_changed, self);
g_set_object(&self->model, model);
if (model != NULL)
g_signal_connect(model, "items-changed",
G_CALLBACK(items_changed), self);
refresh(self);
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_MODEL]);
}