Bazaar 是一款專為 GNOME 設計的現代化應用商店,專注於從 Flatpak 遠程倉庫發現和安裝應用程序及插件。它強調對 Linux 桌面開發者的支持,提供流暢的多線程體驗和本地化內容推薦。

Bazaar


Bazaar 是一款專為 GNOME 桌面環境設計的新一代應用商店,專注於從 Flatpak 遠程倉庫(特別是 Flathub)發現和安裝應用程序及插件。

功能特性

  • 流暢的用户體驗:高度多線程設計,保證界面流暢運行
  • 後台服務架構:即使關閉所有窗口也能保持狀態,支持後台任務執行
  • 本地化內容推薦:可配置的"精選"標籤頁,支持地區化體驗
  • 完整的 Flatpak 支持:支持應用程序、運行時和插件的安裝管理
  • GNOME Shell 集成:實現搜索提供者 DBus 接口
  • KDE Plasma 支持:提供 KRunner 插件
  • 多語言支持:完善的國際化翻譯系統

安裝指南

預構建版本

通過 Flathub 安裝預構建版本:


從源碼編譯

需要安裝以下依賴項:

依賴包

pkg-config 名稱

最低版本

用途

gtk4

gtk4

libadwaita 要求

用户界面

libadwaita

libadwaita-1

1.7

GNOME 外觀

libdex

libdex-1

0.11.1

異步助手

flatpak

flatpak

1.9

Flatpak 管理

appstream

appstream

1.0

應用元數據獲取

xmlb

xmlb

0.3.4

XML 處理

glycin

glycin-1

1.0

圖像解碼

glycin-gtk4

glycin-gtk4-1

1.0

圖像紋理轉換

libyaml

yaml-0.1

0.2.5

YAML 文件解析

libsoup

libsoup-3.0

3.6.0

HTTP 操作

json-glib

json-glib-1.0

1.10.0

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]);
}