mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-21 22:24:07 +01:00
highlight tabs only on unviewed messages (#5649)
This commit is contained in:
parent
101a45fd3a
commit
db8047ea7b
7 changed files with 286 additions and 9 deletions
|
@ -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)
|
||||
|
|
|
@ -204,7 +204,7 @@ void Notebook::duplicatePage(QWidget *page)
|
|||
{
|
||||
newTabPosition = tabPosition + 1;
|
||||
}
|
||||
auto newTabHighlightState = item->tab->highlightState();
|
||||
|
||||
QString newTabTitle = "";
|
||||
if (item->tab->hasCustomTitle())
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -1064,6 +1064,8 @@ void ChannelView::setChannel(const ChannelPtr &underlyingChannel)
|
|||
|
||||
this->underlyingChannel_ = underlyingChannel;
|
||||
|
||||
this->updateID();
|
||||
|
||||
this->performLayout();
|
||||
this->queueUpdate();
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
|
|
@ -212,6 +212,14 @@ public:
|
|||
|
||||
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;
|
||||
|
@ -315,6 +323,9 @@ private:
|
|||
void showReplyThreadPopup(const MessagePtr &message);
|
||||
bool canReplyToMessages() const;
|
||||
|
||||
void updateID();
|
||||
ChannelViewID id_{};
|
||||
|
||||
bool layoutQueued_ = false;
|
||||
bool bufferInvalidationQueued_ = false;
|
||||
|
||||
|
@ -376,7 +387,7 @@ private:
|
|||
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;
|
||||
|
|
|
@ -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"
|
||||
|
@ -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>
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "common/Common.hpp"
|
||||
#include "widgets/helper/Button.hpp"
|
||||
#include "widgets/helper/ChannelView.hpp"
|
||||
#include "widgets/Notebook.hpp"
|
||||
|
||||
#include <pajlada/settings/setting.hpp>
|
||||
|
@ -59,11 +60,24 @@ public:
|
|||
**/
|
||||
bool isLive() const;
|
||||
|
||||
/**
|
||||
* @brief Sets the highlight state of this tab clearing highlight sources
|
||||
*
|
||||
* Obeys the HighlightsEnabled setting and highlight states hierarchy
|
||||
*/
|
||||
void setHighlightState(HighlightState style);
|
||||
HighlightState highlightState() const;
|
||||
|
||||
/**
|
||||
* @brief Updates the highlight state and highlight sources of this tab
|
||||
*
|
||||
* Obeys the HighlightsEnabled setting and the highlight state hierarchy and tracks the highlight state update sources
|
||||
*/
|
||||
void updateHighlightState(HighlightState style,
|
||||
const ChannelView &channelViewSource);
|
||||
void copyHighlightStateAndSourcesFrom(const NotebookTab *sourceTab);
|
||||
void setHighlightsEnabled(const bool &newVal);
|
||||
void newHighlightSourceAdded(const ChannelView &channelViewSource);
|
||||
bool hasHighlightsEnabled() const;
|
||||
HighlightState highlightState() const;
|
||||
|
||||
void moveAnimated(QPoint targetPos, bool animated = true);
|
||||
|
||||
|
@ -107,6 +121,16 @@ private:
|
|||
|
||||
int normalTabWidthForHeight(int height) const;
|
||||
|
||||
bool shouldMessageHighlight(const ChannelView &channelViewSource) const;
|
||||
|
||||
using HighlightSources =
|
||||
std::unordered_map<ChannelView::ChannelViewID, HighlightState>;
|
||||
HighlightSources highlightSources_;
|
||||
|
||||
void removeHighlightStateChangeSources(const HighlightSources &toRemove);
|
||||
void removeHighlightSource(const ChannelView::ChannelViewID &source);
|
||||
void updateHighlightStateDueSourcesChange();
|
||||
|
||||
QPropertyAnimation positionChangedAnimation_;
|
||||
QPoint positionAnimationDesiredPoint_;
|
||||
|
||||
|
|
|
@ -214,10 +214,18 @@ void SplitContainer::addSplit(Split *split)
|
|||
auto &&conns = this->connectionsPerSplit_[split];
|
||||
|
||||
conns.managedConnect(split->getChannelView().tabHighlightRequested,
|
||||
[this](HighlightState state) {
|
||||
[this, split](HighlightState state) {
|
||||
if (this->tab_ != nullptr)
|
||||
{
|
||||
this->tab_->setHighlightState(state);
|
||||
this->tab_->updateHighlightState(
|
||||
state, split->getChannelView());
|
||||
}
|
||||
});
|
||||
|
||||
conns.managedConnect(split->channelChanged, [this, split] {
|
||||
if (this->tab_ != nullptr)
|
||||
{
|
||||
this->tab_->newHighlightSourceAdded(split->getChannelView());
|
||||
}
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in a new issue