diff options
Diffstat (limited to 'src/components/channeltabswitcherhandy.cpp')
-rw-r--r-- | src/components/channeltabswitcherhandy.cpp | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/src/components/channeltabswitcherhandy.cpp b/src/components/channeltabswitcherhandy.cpp new file mode 100644 index 0000000..f7b0226 --- /dev/null +++ b/src/components/channeltabswitcherhandy.cpp @@ -0,0 +1,202 @@ +#ifdef WITH_LIBHANDY + + #include "channeltabswitcherhandy.hpp" + #include "abaddon.hpp" + +void selected_page_notify_cb(HdyTabView *view, GParamSpec *pspec, ChannelTabSwitcherHandy *switcher) { + auto *page = hdy_tab_view_get_selected_page(view); + if (auto it = switcher->m_pages_rev.find(page); it != switcher->m_pages_rev.end()) { + switcher->m_signal_channel_switched_to.emit(it->second); + } +} + +gboolean close_page_cb(HdyTabView *view, HdyTabPage *page, ChannelTabSwitcherHandy *switcher) { + switcher->ClearPage(page); + hdy_tab_view_close_page_finish(view, page, true); + return GDK_EVENT_STOP; +} + +ChannelTabSwitcherHandy::ChannelTabSwitcherHandy() { + m_tab_bar = hdy_tab_bar_new(); + m_tab_bar_wrapped = Glib::wrap(GTK_WIDGET(m_tab_bar)); + m_tab_view = hdy_tab_view_new(); + m_tab_view_wrapped = Glib::wrap(GTK_WIDGET(m_tab_view)); + + g_signal_connect(m_tab_view, "notify::selected-page", G_CALLBACK(selected_page_notify_cb), this); + g_signal_connect(m_tab_view, "close-page", G_CALLBACK(close_page_cb), this); + + hdy_tab_bar_set_view(m_tab_bar, m_tab_view); + add(*m_tab_bar_wrapped); + m_tab_bar_wrapped->show(); + + auto &discord = Abaddon::Get().GetDiscordClient(); + discord.signal_message_create().connect([this](const Message &data) { + CheckUnread(data.ChannelID); + }); + + discord.signal_message_ack().connect([this](const MessageAckData &data) { + CheckUnread(data.ChannelID); + }); +} + +void ChannelTabSwitcherHandy::AddChannelTab(Snowflake id) { + if (m_pages.find(id) != m_pages.end()) return; + + auto &discord = Abaddon::Get().GetDiscordClient(); + const auto channel = discord.GetChannel(id); + if (!channel.has_value()) return; + + auto *dummy = Gtk::make_managed<Gtk::Box>(); // minimal + auto *page = hdy_tab_view_append(m_tab_view, GTK_WIDGET(dummy->gobj())); + + hdy_tab_page_set_title(page, channel->GetDisplayName().c_str()); + hdy_tab_page_set_tooltip(page, nullptr); + + m_pages[id] = page; + m_pages_rev[page] = id; + + CheckUnread(id); + CheckPageIcon(page, *channel); + AppendPageHistory(page, id); +} + +void ChannelTabSwitcherHandy::ReplaceActiveTab(Snowflake id) { + auto *page = hdy_tab_view_get_selected_page(m_tab_view); + if (page == nullptr) { + AddChannelTab(id); + } else if (auto it = m_pages.find(id); it != m_pages.end()) { + hdy_tab_view_set_selected_page(m_tab_view, it->second); + } else { + auto &discord = Abaddon::Get().GetDiscordClient(); + const auto channel = discord.GetChannel(id); + if (!channel.has_value()) return; + + hdy_tab_page_set_title(page, channel->GetDisplayName().c_str()); + + ClearPage(page); + m_pages[id] = page; + m_pages_rev[page] = id; + + CheckUnread(id); + CheckPageIcon(page, *channel); + AppendPageHistory(page, id); + } +} + +TabsState ChannelTabSwitcherHandy::GetTabsState() { + TabsState state; + + const gint num_pages = hdy_tab_view_get_n_pages(m_tab_view); + for (gint i = 0; i < num_pages; i++) { + auto *page = hdy_tab_view_get_nth_page(m_tab_view, i); + if (page != nullptr) { + if (const auto it = m_pages_rev.find(page); it != m_pages_rev.end()) { + state.Channels.push_back(it->second); + } + } + } + + return state; +} + +void ChannelTabSwitcherHandy::UseTabsState(const TabsState &state) { + for (auto id : state.Channels) { + AddChannelTab(id); + } +} + +void ChannelTabSwitcherHandy::GoBackOnCurrent() { + AdvanceOnCurrent(-1); +} + +void ChannelTabSwitcherHandy::GoForwardOnCurrent() { + AdvanceOnCurrent(1); +} + +int ChannelTabSwitcherHandy::GetNumberOfTabs() const { + return hdy_tab_view_get_n_pages(m_tab_view); +} + +void ChannelTabSwitcherHandy::CheckUnread(Snowflake id) { + if (auto it = m_pages.find(id); it != m_pages.end()) { + auto &discord = Abaddon::Get().GetDiscordClient(); + const bool has_unreads = discord.GetUnreadStateForChannel(id) > -1; + const bool show_indicator = has_unreads && !discord.IsChannelMuted(id); + hdy_tab_page_set_needs_attention(it->second, show_indicator); + } +} + +void ChannelTabSwitcherHandy::ClearPage(HdyTabPage *page) { + if (auto it = m_pages_rev.find(page); it != m_pages_rev.end()) { + m_pages.erase(it->second); + } + m_pages_rev.erase(page); + m_page_icons.erase(page); +} + +void ChannelTabSwitcherHandy::OnPageIconLoad(HdyTabPage *page, const Glib::RefPtr<Gdk::Pixbuf> &pb) { + auto new_pb = pb->scale_simple(16, 16, Gdk::INTERP_BILINEAR); + m_page_icons[page] = new_pb; + hdy_tab_page_set_icon(page, G_ICON(new_pb->gobj())); +} + +void ChannelTabSwitcherHandy::CheckPageIcon(HdyTabPage *page, const ChannelData &data) { + if (data.GuildID.has_value()) { + if (const auto guild = Abaddon::Get().GetDiscordClient().GetGuild(*data.GuildID); guild.has_value() && guild->HasIcon()) { + auto *child_widget = hdy_tab_page_get_child(page); + if (child_widget == nullptr) return; // probably wont happen :---) + // i think this works??? + auto *trackable = Glib::wrap(GTK_WIDGET(child_widget)); + + Abaddon::Get().GetImageManager().LoadFromURL( + guild->GetIconURL("png", "16"), + sigc::track_obj([this, page](const Glib::RefPtr<Gdk::Pixbuf> &pb) { OnPageIconLoad(page, pb); }, + *trackable)); + return; + } + return; + } + + hdy_tab_page_set_icon(page, nullptr); +} + +void ChannelTabSwitcherHandy::AppendPageHistory(HdyTabPage *page, Snowflake channel) { + auto it = m_page_history.find(page); + if (it == m_page_history.end()) { + m_page_history[page] = PageHistory { { channel }, 0 }; + return; + } + + // drop everything beyond current position + it->second.Visited.resize(++it->second.CurrentVisitedIndex); + it->second.Visited.push_back(channel); +} + +void ChannelTabSwitcherHandy::AdvanceOnCurrent(size_t by) { + auto *current = hdy_tab_view_get_selected_page(m_tab_view); + if (current == nullptr) return; + auto history = m_page_history.find(current); + if (history == m_page_history.end()) return; + if (by + history->second.CurrentVisitedIndex < 0 || by + history->second.CurrentVisitedIndex >= history->second.Visited.size()) return; + + history->second.CurrentVisitedIndex += by; + const auto to_id = history->second.Visited.at(history->second.CurrentVisitedIndex); + + // temporarily point current index to the end so that it doesnt fuck up the history + // remove it immediately after cuz the emit will call ReplaceActiveTab + const auto real = history->second.CurrentVisitedIndex; + history->second.CurrentVisitedIndex = history->second.Visited.size() - 1; + m_signal_channel_switched_to.emit(to_id); + // iterator might not be valid + history = m_page_history.find(current); + if (history != m_page_history.end()) { + history->second.Visited.pop_back(); + } + history->second.CurrentVisitedIndex = real; +} + +ChannelTabSwitcherHandy::type_signal_channel_switched_to ChannelTabSwitcherHandy::signal_channel_switched_to() { + return m_signal_channel_switched_to; +} + +#endif |