Add support for more symbols in the emoji panel.

This commit is contained in:
fanlusky committed 2026-08-18 22:30:24 +08:00
1 parent e012bb74ce
commit f2199e94fc
2 files changed
+194 -26

No files matched your search

+182 -12
View File
@@ -511,6 +511,7 @@ EmojiPanel::EmojiPanel(bool lightTheme) : lightTheme_(lightTheme)
{
LoadEmojiCatalog();
LoadKaomojiCatalog();
LoadSymbolCatalog();
searchBox_ = std::make_shared<TextBox>(kSearchHeight * kPanelScale, L"Search");
searchBox_->SetFontSize(14.0f);
searchBox_->SetPlaceholderFontSize(12.0f);
@@ -677,6 +678,88 @@ void EmojiPanel::LoadKaomojiCatalog()
displayDirty_ = true;
}
void EmojiPanel::LoadSymbolCatalog()
{
symbolGroups_.clear();
symbolTabs_.clear();
const auto path = OthersDatabasePath();
if (path.empty() || !std::filesystem::exists(path))
{
return;
}
sqlite3 *db = nullptr;
const auto widePath = path.wstring();
if (sqlite3_open16(widePath.c_str(), &db) != SQLITE_OK)
{
if (db)
{
sqlite3_close(db);
}
return;
}
sqlite3_busy_timeout(db, 3000);
sqlite3_stmt *stmt = nullptr;
const char *sql =
"SELECT symbol, category, parent_category, keywords FROM symbol_catalog ORDER BY sort_order";
if (sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr) != SQLITE_OK)
{
sqlite3_close(db);
return;
}
size_t loaded = 0;
while (sqlite3_step(stmt) == SQLITE_ROW)
{
auto symbol = Utf8ToWide(reinterpret_cast<const char *>(sqlite3_column_text(stmt, 0)));
auto category = Utf8ToWide(reinterpret_cast<const char *>(sqlite3_column_text(stmt, 1)));
auto parent = Utf8ToWide(reinterpret_cast<const char *>(sqlite3_column_text(stmt, 2)));
auto keywords = Utf8ToWide(reinterpret_cast<const char *>(sqlite3_column_text(stmt, 3)));
if (symbol.empty() || category.empty())
{
continue;
}
if (parent.empty())
{
parent = category;
}
if (symbolTabs_.empty() || symbolTabs_.back().title != parent)
{
symbolTabs_.push_back({parent, symbol, {}});
}
if (symbolGroups_.empty() || symbolGroups_.back().title != category)
{
symbolGroups_.push_back({category, symbol, {}});
symbolTabs_.back().groupIndices.push_back(symbolGroups_.size() - 1);
if (symbolTabs_.back().icon.empty())
{
symbolTabs_.back().icon = symbol;
}
}
if (keywords.empty())
{
keywords = category;
}
const bool longText = symbol.size() > 4;
auto keywordsLower = Lower(keywords);
symbolGroups_.back().items.push_back(
{std::move(symbol), std::move(keywords), std::move(keywordsLower), longText});
if ((++loaded % 120) == 0 && EmojiPanelSplash::IsVisible())
{
EmojiPanelSplash::Pump();
}
}
sqlite3_finalize(stmt);
sqlite3_close(db);
if (symbolGroups_.empty())
{
symbolTabs_.clear();
}
displayDirty_ = true;
}
SizeF EmojiPanel::Measure(const SizeF &availableSize)
{
return availableSize;
@@ -836,6 +919,10 @@ bool EmojiPanel::InDetailPage() const
size_t EmojiPanel::EmojiSubTabCount() const
{
if (page_ == Page::Symbols)
{
return symbolTabs_.size();
}
return emojiGroups_.size() + 1;
}
@@ -848,6 +935,15 @@ const EmojiPanel::Group *EmojiPanel::ActiveEmojiGroup() const
return &emojiGroups_[emojiSubTab_ - 1];
}
const EmojiPanel::SymbolTab *EmojiPanel::ActiveSymbolTab() const
{
if (emojiSubTab_ >= symbolTabs_.size())
{
return nullptr;
}
return &symbolTabs_[emojiSubTab_];
}
std::vector<const EmojiPanel::Item *> EmojiPanel::CollectPreviewItems(const std::vector<Group> &groups,
size_t limit) const
{
@@ -867,6 +963,40 @@ std::vector<const EmojiPanel::Item *> EmojiPanel::CollectPreviewItems(const std:
return items;
}
std::vector<const EmojiPanel::Item *> EmojiPanel::CollectDiversePreviewItems(const std::vector<Group> &groups,
size_t limit) const
{
std::vector<const Item *> items;
if (limit == 0 || groups.empty())
{
return items;
}
items.reserve(limit);
size_t round = 0;
while (items.size() < limit)
{
bool added = false;
for (const auto &group : groups)
{
if (round < group.items.size())
{
items.push_back(&group.items[round]);
added = true;
if (items.size() >= limit)
{
return items;
}
}
}
if (!added)
{
break;
}
++round;
}
return items;
}
size_t EmojiPanel::HitMainTab(const PointF &point) const
{
if (InDetailPage())
@@ -885,7 +1015,11 @@ size_t EmojiPanel::HitMainTab(const PointF &point) const
size_t EmojiPanel::HitEmojiSubTab(const PointF &point) const
{
if (page_ != Page::Emoji)
if (page_ != Page::Emoji && page_ != Page::Symbols)
{
return kInvalidIndex;
}
if (page_ == Page::Symbols && symbolTabs_.empty())
{
return kInvalidIndex;
}
@@ -1022,16 +1156,27 @@ void EmojiPanel::EnsureDisplayLayout() const
}
appendGroup(L"Kaomoji", std::move(kaomojiPreview), Page::Kaomoji, true);
auto symbolPreview = CollectPreviewItems(symbolGroups_, kPreviewLimit);
auto symbolPreview = CollectDiversePreviewItems(symbolGroups_, kPreviewLimit);
if (searching)
{
symbolPreview.clear();
if (!symbolGroups_.empty())
for (const auto &group : symbolGroups_)
{
symbolPreview = filterItems(symbolGroups_.front().items);
if (symbolPreview.size() > kPreviewLimit)
for (const auto &item : group.items)
{
symbolPreview.resize(kPreviewLimit);
if (item.keywordsLower.find(query) != std::wstring::npos ||
item.text.find(searchText_) != std::wstring::npos)
{
symbolPreview.push_back(&item);
if (symbolPreview.size() >= kPreviewLimit)
{
break;
}
}
}
if (symbolPreview.size() >= kPreviewLimit)
{
break;
}
}
}
@@ -1065,12 +1210,27 @@ void EmojiPanel::EnsureDisplayLayout() const
}
else if (page_ == Page::Symbols)
{
for (const auto &group : symbolGroups_)
if (searching || symbolTabs_.empty())
{
auto items = filterItems(group.items);
if (!items.empty() || !searching)
for (const auto &group : symbolGroups_)
{
appendGroup(group.title, std::move(items), Page::Home, false);
auto items = filterItems(group.items);
if (!items.empty() || !searching)
{
appendGroup(group.title, std::move(items), Page::Home, false);
}
}
}
else if (const SymbolTab *tab = ActiveSymbolTab())
{
for (const size_t groupIndex : tab->groupIndices)
{
if (groupIndex >= symbolGroups_.size())
{
continue;
}
const auto &group = symbolGroups_[groupIndex];
appendGroup(group.title, filterItems(group.items), Page::Home, false);
}
}
}
@@ -1605,8 +1765,9 @@ void EmojiPanel::Render(DeviceResources &resources)
}
DrawChevron(resources, back, text, true);
if (page_ == Page::Emoji)
if (page_ == Page::Emoji || (page_ == Page::Symbols && !symbolTabs_.empty()))
{
const bool isSymbolPage = page_ == Page::Symbols;
for (size_t index = 0; index < EmojiSubTabCount(); ++index)
{
const RectF rect = EmojiSubTabRect(index);
@@ -1615,7 +1776,14 @@ void EmojiPanel::Render(DeviceResources &resources)
FillRect(resources, rect, hover, 6.0f);
}
std::wstring icon = L"\u23F1";
if (index > 0 && index - 1 < emojiGroups_.size())
if (isSymbolPage)
{
if (index < symbolTabs_.size())
{
icon = symbolTabs_[index].icon;
}
}
else if (index > 0 && index - 1 < emojiGroups_.size())
{
icon = emojiGroups_[index - 1].icon;
}
@@ -1819,6 +1987,8 @@ void EmojiPanel::Render(DeviceResources &resources)
emptyText = L"Clipboard history can be connected here";
else if (page_ == Page::Emoji && emojiSubTab_ == 0 && recentItems_.empty())
emptyText = L"Your recently used items will appear here";
else if (page_ == Page::Symbols && symbolGroups_.empty())
emptyText = L"Symbol catalog not found";
DrawText(resources, emptyText,
{bounds_.x + 30.0f, bounds_.y + kContentTop + 50.0f, bounds_.width - 60.0f, 60.0f},
20.0f, mutedText, L"Segoe UI", DWRITE_TEXT_ALIGNMENT_CENTER);
+12 -14
View File
@@ -59,6 +59,13 @@ class EmojiPanel final : public Visual
std::vector<Item> items;
};
struct SymbolTab
{
std::wstring title;
std::wstring icon;
std::vector<size_t> groupIndices;
};
struct LayoutGroup
{
std::wstring title;
@@ -113,6 +120,7 @@ class EmojiPanel final : public Visual
void ResetView();
void LoadEmojiCatalog();
void LoadKaomojiCatalog();
void LoadSymbolCatalog();
void UpdateSearchPlaceholder();
void ShowToast(std::wstring text);
void DismissToast();
@@ -121,24 +129,14 @@ class EmojiPanel final : public Visual
bool InDetailPage() const;
size_t EmojiSubTabCount() const;
const Group *ActiveEmojiGroup() const;
const SymbolTab *ActiveSymbolTab() const;
std::vector<const Item *> CollectPreviewItems(const std::vector<Group> &groups, size_t limit) const;
std::vector<const Item *> CollectDiversePreviewItems(const std::vector<Group> &groups, size_t limit) const;
std::vector<Group> emojiGroups_;
std::vector<Group> kaomojiGroups_;
std::vector<Group> symbolGroups_ = {
{L"Symbols", L"\u2605",
{{L"★", L"star \u661F\u661F", L"star \u661F\u661F", false},
{L"☆", L"star outline \u661F\u661F", L"star outline \u661F\u661F", false},
{L"✓", L"check \u5BF9\u52FE", L"check \u5BF9\u52FE", false},
{L"✕", L"cross \u9519", L"cross \u9519", false},
{L"→", L"right arrow \u53F3\u7BAD\u5934", L"right arrow \u53F3\u7BAD\u5934", false},
{L"←", L"left arrow \u5DE6\u7BAD\u5934", L"left arrow \u5DE6\u7BAD\u5934", false},
{L"↑", L"up arrow \u4E0A\u7BAD\u5934", L"up arrow \u4E0A\u7BAD\u5934", false},
{L"↓", L"down arrow", L"down arrow", false}, {L"∞", L"infinity", L"infinity", false},
{L"©", L"copyright", L"copyright", false}, {L"®", L"registered", L"registered", false},
{L"™", L"trademark", L"trademark", false}, {L"§", L"section", L"section", false},
{L"•", L"bullet", L"bullet", false}}},
};
std::vector<Group> symbolGroups_;
std::vector<SymbolTab> symbolTabs_;
std::vector<Item> recentItems_;
std::wstring searchText_;
std::wstring toastText_;