Skip to content

Commit

Permalink
highlight tabs only on unviewed messages (Chatterino#5649)
Browse files Browse the repository at this point in the history
  • Loading branch information
hemirt authored Nov 2, 2024
1 parent 101a45f commit db8047e
Show file tree
Hide file tree
Showing 7 changed files with 286 additions and 9 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
- Minor: Indicate when subscriptions and resubscriptions are for multiple months. (#5642)
- Minor: Proxy URL information is now included in the `/debug-env` command. (#5648)
- Minor: Make raid entry message usernames clickable. (#5651)
- Minor: Tabs unhighlight when their content is read in other tabs. (#5649)
- Bugfix: Fixed tab move animation occasionally failing to start after closing a tab. (#5426, #5612)
- Bugfix: If a network request errors with 200 OK, Qt's error code is now reported instead of the HTTP status. (#5378)
- Bugfix: Fixed restricted users usernames not being clickable. (#5405)
Expand Down
4 changes: 2 additions & 2 deletions src/widgets/Notebook.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ void Notebook::duplicatePage(QWidget *page)
{
newTabPosition = tabPosition + 1;
}
auto newTabHighlightState = item->tab->highlightState();

QString newTabTitle = "";
if (item->tab->hasCustomTitle())
{
Expand All @@ -213,7 +213,7 @@ void Notebook::duplicatePage(QWidget *page)

auto *tab =
this->addPageAt(newContainer, newTabPosition, newTabTitle, false);
tab->setHighlightState(newTabHighlightState);
tab->copyHighlightStateAndSourcesFrom(item->tab);

newContainer->setTab(tab);
}
Expand Down
27 changes: 27 additions & 0 deletions src/widgets/helper/ChannelView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1064,6 +1064,8 @@ void ChannelView::setChannel(const ChannelPtr &underlyingChannel)

this->underlyingChannel_ = underlyingChannel;

this->updateID();

this->performLayout();
this->queueUpdate();

Expand All @@ -1082,6 +1084,8 @@ void ChannelView::setChannel(const ChannelPtr &underlyingChannel)
void ChannelView::setFilters(const QList<QUuid> &ids)
{
this->channelFilters_ = std::make_shared<FilterSet>(ids);

this->updateID();
}

QList<QUuid> ChannelView::getFilterIds() const
Expand Down Expand Up @@ -3243,4 +3247,27 @@ void ChannelView::pendingLinkInfoStateChanged()
this->tooltipWidget_->applyLastBoundsCheck();
}

void ChannelView::updateID()
{
if (!this->underlyingChannel_)
{
// cannot update
return;
}

std::size_t seed = 0;
auto first = qHash(this->underlyingChannel_->getName());
auto second = qHash(this->getFilterIds());

boost::hash_combine(seed, first);
boost::hash_combine(seed, second);

this->id_ = seed;
}

ChannelView::ChannelViewID ChannelView::getID() const
{
return this->id_;
}

} // namespace chatterino
13 changes: 12 additions & 1 deletion src/widgets/helper/ChannelView.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,14 @@ class ChannelView final : public BaseWidget

Scrollbar *scrollbar();

using ChannelViewID = std::size_t;
///
/// \brief Get the ID of this ChannelView
///
/// The ID is made of the underlying channel's name
/// combined with the filter set IDs
ChannelViewID getID() const;

pajlada::Signals::Signal<QMouseEvent *> mouseDown;
pajlada::Signals::NoArgSignal selectionChanged;
pajlada::Signals::Signal<HighlightState> tabHighlightRequested;
Expand Down Expand Up @@ -315,6 +323,9 @@ class ChannelView final : public BaseWidget
void showReplyThreadPopup(const MessagePtr &message);
bool canReplyToMessages() const;

void updateID();
ChannelViewID id_{};

bool layoutQueued_ = false;
bool bufferInvalidationQueued_ = false;

Expand Down Expand Up @@ -376,7 +387,7 @@ class ChannelView final : public BaseWidget
FilterSetPtr channelFilters_;

// Returns true if message should be included
bool shouldIncludeMessage(const MessagePtr &m) const;
bool shouldIncludeMessage(const MessagePtr &message) const;

// Returns whether the scrollbar should have highlights
bool showScrollbarHighlights() const;
Expand Down
210 changes: 208 additions & 2 deletions src/widgets/helper/NotebookTab.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "widgets/helper/NotebookTab.hpp"

#include "Application.hpp"
#include "common/Channel.hpp"
#include "common/Common.hpp"
#include "controllers/hotkeys/HotkeyCategory.hpp"
#include "controllers/hotkeys/HotkeyController.hpp"
Expand All @@ -12,9 +13,11 @@
#include "widgets/dialogs/SettingsDialog.hpp"
#include "widgets/Notebook.hpp"
#include "widgets/splits/DraggedSplit.hpp"
#include "widgets/splits/Split.hpp"
#include "widgets/splits/SplitContainer.hpp"

#include <boost/bind/bind.hpp>
#include <boost/container_hash/hash.hpp>
#include <QAbstractAnimation>
#include <QApplication>
#include <QDebug>
Expand Down Expand Up @@ -302,10 +305,134 @@ bool NotebookTab::isSelected() const
return this->selected_;
}

void NotebookTab::removeHighlightStateChangeSources(
const HighlightSources &toRemove)
{
for (const auto &[source, _] : toRemove)
{
this->removeHighlightSource(source);
}
}

void NotebookTab::removeHighlightSource(
const ChannelView::ChannelViewID &source)
{
this->highlightSources_.erase(source);
}

void NotebookTab::newHighlightSourceAdded(const ChannelView &channelViewSource)
{
auto channelViewId = channelViewSource.getID();
this->removeHighlightSource(channelViewId);
this->updateHighlightStateDueSourcesChange();

auto *splitNotebook = dynamic_cast<SplitNotebook *>(this->notebook_);
if (splitNotebook)
{
for (int i = 0; i < splitNotebook->getPageCount(); ++i)
{
auto *splitContainer =
dynamic_cast<SplitContainer *>(splitNotebook->getPageAt(i));
if (splitContainer)
{
auto *tab = splitContainer->getTab();
if (tab && tab != this)
{
tab->removeHighlightSource(channelViewId);
tab->updateHighlightStateDueSourcesChange();
}
}
}
}
}

void NotebookTab::updateHighlightStateDueSourcesChange()
{
if (std::ranges::any_of(this->highlightSources_, [](const auto &keyval) {
return keyval.second == HighlightState::Highlighted;
}))
{
assert(this->highlightState_ == HighlightState::Highlighted);
return;
}

if (std::ranges::any_of(this->highlightSources_, [](const auto &keyval) {
return keyval.second == HighlightState::NewMessage;
}))
{
if (this->highlightState_ != HighlightState::NewMessage)
{
this->highlightState_ = HighlightState::NewMessage;
this->update();
}
}
else
{
if (this->highlightState_ != HighlightState::None)
{
this->highlightState_ = HighlightState::None;
this->update();
}
}

assert(this->highlightState_ != HighlightState::Highlighted);
}

void NotebookTab::copyHighlightStateAndSourcesFrom(const NotebookTab *sourceTab)
{
if (this->isSelected())
{
assert(this->highlightSources_.empty());
assert(this->highlightState_ == HighlightState::None);
return;
}

this->highlightSources_ = sourceTab->highlightSources_;

if (!this->highlightEnabled_ &&
sourceTab->highlightState_ == HighlightState::NewMessage)
{
return;
}

if (this->highlightState_ == sourceTab->highlightState_ ||
this->highlightState_ == HighlightState::Highlighted)
{
return;
}

this->highlightState_ = sourceTab->highlightState_;
this->update();
}

void NotebookTab::setSelected(bool value)
{
this->selected_ = value;

if (value)
{
auto *splitNotebook = dynamic_cast<SplitNotebook *>(this->notebook_);
if (splitNotebook)
{
for (int i = 0; i < splitNotebook->getPageCount(); ++i)
{
auto *splitContainer =
dynamic_cast<SplitContainer *>(splitNotebook->getPageAt(i));
if (splitContainer)
{
auto *tab = splitContainer->getTab();
if (tab && tab != this)
{
tab->removeHighlightStateChangeSources(
this->highlightSources_);
tab->updateHighlightStateDueSourcesChange();
}
}
}
}
}

this->highlightSources_.clear();
this->highlightState_ = HighlightState::None;

this->update();
Expand Down Expand Up @@ -358,13 +485,22 @@ bool NotebookTab::isLive() const
return this->isLive_;
}

HighlightState NotebookTab::highlightState() const
{
return this->highlightState_;
}

void NotebookTab::setHighlightState(HighlightState newHighlightStyle)
{
if (this->isSelected())
{
assert(this->highlightSources_.empty());
assert(this->highlightState_ == HighlightState::None);
return;
}

this->highlightSources_.clear();

if (!this->highlightEnabled_ &&
newHighlightStyle == HighlightState::NewMessage)
{
Expand All @@ -381,9 +517,79 @@ void NotebookTab::setHighlightState(HighlightState newHighlightStyle)
this->update();
}

HighlightState NotebookTab::highlightState() const
void NotebookTab::updateHighlightState(HighlightState newHighlightStyle,
const ChannelView &channelViewSource)
{
return this->highlightState_;
if (this->isSelected())
{
assert(this->highlightSources_.empty());
assert(this->highlightState_ == HighlightState::None);
return;
}

if (!this->shouldMessageHighlight(channelViewSource))
{
return;
}

if (!this->highlightEnabled_ &&
newHighlightStyle == HighlightState::NewMessage)
{
return;
}

// message is highlighting unvisible tab

auto channelViewId = channelViewSource.getID();

switch (newHighlightStyle)
{
case HighlightState::Highlighted:
// override lower states
this->highlightSources_.insert_or_assign(channelViewId,
newHighlightStyle);
case HighlightState::NewMessage: {
// only insert if no state already there to avoid overriding
if (!this->highlightSources_.contains(channelViewId))
{
this->highlightSources_.emplace(channelViewId,
newHighlightStyle);
}
break;
}
case HighlightState::None:
break;
}

if (this->highlightState_ == newHighlightStyle ||
this->highlightState_ == HighlightState::Highlighted)
{
return;
}

this->highlightState_ = newHighlightStyle;
this->update();
}

bool NotebookTab::shouldMessageHighlight(
const ChannelView &channelViewSource) const
{
auto *visibleSplitContainer =
dynamic_cast<SplitContainer *>(this->notebook_->getSelectedPage());
if (visibleSplitContainer != nullptr)
{
const auto &visibleSplits = visibleSplitContainer->getSplits();
for (const auto &visibleSplit : visibleSplits)
{
if (channelViewSource.getID() ==
visibleSplit->getChannelView().getID())
{
return false;
}
}
}

return true;
}

void NotebookTab::setHighlightsEnabled(const bool &newVal)
Expand Down
Loading

0 comments on commit db8047e

Please sign in to comment.