diff --git a/CHANGELOG.md b/CHANGELOG.md index d313f52df..03c32a35b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -76,6 +76,7 @@ - Minor: Migrated /chatters to Helix API. (#4088, #4097, #4114) - Minor: Migrated /mods to Helix API. (#4103) - Minor: Add settings tooltips. (#3437) +- Minor: Improved look of tabs when using a layout other than top. (#3925) - Bugfix: Connection to Twitch PubSub now recovers more reliably. (#3643, #3716) - Bugfix: Fixed `Smooth scrolling on new messages` setting sometimes hiding messages. (#4028) - Bugfix: Fixed a crash that can occur when closing and quickly reopening a split, then running a command. (#3852) diff --git a/src/widgets/Notebook.cpp b/src/widgets/Notebook.cpp index d90043eee..df8ef5dea 100644 --- a/src/widgets/Notebook.cpp +++ b/src/widgets/Notebook.cpp @@ -73,6 +73,7 @@ NotebookTab *Notebook::addPage(QWidget *page, QString title, bool select) tab->page = page; tab->setCustomTitle(title); + tab->setTabLocation(this->tabLocation_); Item item; item.page = page; @@ -493,6 +494,7 @@ void Notebook::performLayout(bool animated) const auto minimumTabAreaSpace = int(tabHeight * 0.5); const auto addButtonWidth = this->showAddButton_ ? tabHeight : 0; const auto lineThickness = int(2 * scale); + const auto tabSpacer = std::max(1, int(scale * 1)); const auto buttonWidth = tabHeight; const auto buttonHeight = tabHeight - 1; @@ -544,7 +546,7 @@ void Notebook::performLayout(bool animated) /// Layout tab item.tab->growWidth(0); item.tab->moveAnimated(QPoint(x, y), animated); - x += item.tab->width() + std::max(1, int(scale * 1)); + x += item.tab->width() + tabSpacer; } /// Update which tabs are in the last row @@ -605,10 +607,12 @@ void Notebook::performLayout(bool animated) } if (this->visibleButtonCount() > 0) - y = tabHeight; + y = tabHeight + lineThickness; // account for divider line int totalButtonWidths = x; - int top = y; + const int top = y + tabSpacer; // add margin + + y = top; x = left; // zneix: if we were to remove buttons when tabs are hidden @@ -654,7 +658,8 @@ void Notebook::performLayout(bool animated) /// Layout tab item.tab->growWidth(largestWidth); item.tab->moveAnimated(QPoint(x, y), animated); - y += tabHeight; + item.tab->setInLastRow(isLastColumn); + y += tabHeight + tabSpacer; } if (isLastColumn && this->showAddButton_) @@ -704,10 +709,12 @@ void Notebook::performLayout(bool animated) } if (this->visibleButtonCount() > 0) - y = tabHeight; + y = tabHeight + lineThickness; // account for divider line int consumedButtonWidths = right - x; - int top = y; + const int top = y + tabSpacer; // add margin + + y = top; x = right; // zneix: if we were to remove buttons when tabs are hidden @@ -758,7 +765,8 @@ void Notebook::performLayout(bool animated) /// Layout tab item.tab->growWidth(largestWidth); item.tab->moveAnimated(QPoint(x, y), animated); - y += tabHeight; + item.tab->setInLastRow(isLastColumn); + y += tabHeight + tabSpacer; } if (isLastColumn && this->showAddButton_) @@ -817,7 +825,7 @@ void Notebook::performLayout(bool animated) if (this->showTabs_) { // reset vertical position regardless - y = bottom - tabHeight; + y = bottom - tabHeight - tabSpacer; // layout tabs /// Notebook tabs need to know if they are in the last row. @@ -843,7 +851,7 @@ void Notebook::performLayout(bool animated) /// Layout tab item.tab->growWidth(0); item.tab->moveAnimated(QPoint(x, y), animated); - x += item.tab->width() + std::max(1, int(scale * 1)); + x += item.tab->width() + tabSpacer; } /// Update which tabs are in the last row @@ -866,7 +874,7 @@ void Notebook::performLayout(bool animated) int consumedBottomSpace = std::max({bottom - y, consumedButtonHeights, minimumTabAreaSpace}); - int tabsStart = bottom - consumedBottomSpace; + int tabsStart = bottom - consumedBottomSpace - lineThickness; if (this->lineOffset_ != tabsStart) { @@ -917,6 +925,13 @@ void Notebook::setTabLocation(NotebookTabLocation location) if (location != this->tabLocation_) { this->tabLocation_ = location; + + // Update all tabs + for (const auto &item : this->items_) + { + item.tab->setTabLocation(location); + } + this->performLayout(); } } diff --git a/src/widgets/helper/NotebookTab.cpp b/src/widgets/helper/NotebookTab.cpp index b92768898..9aea1a18c 100644 --- a/src/widgets/helper/NotebookTab.cpp +++ b/src/widgets/helper/NotebookTab.cpp @@ -34,6 +34,29 @@ namespace { return 1.0; #endif } + + // Translates the given rectangle by an amount in the direction to appear like the tab is selected. + // For example, if location is Top, the rectangle will be translated in the negative Y direction, + // or "up" on the screen, by amount. + void translateRectForLocation(QRect &rect, NotebookTabLocation location, + int amount) + { + switch (location) + { + case NotebookTabLocation::Top: + rect.translate(0, -amount); + break; + case NotebookTabLocation::Left: + rect.translate(-amount, 0); + break; + case NotebookTabLocation::Right: + rect.translate(amount, 0); + break; + case NotebookTabLocation::Bottom: + rect.translate(0, amount); + break; + } + } } // namespace NotebookTab::NotebookTab(Notebook *notebook) @@ -294,6 +317,15 @@ void NotebookTab::setInLastRow(bool value) } } +void NotebookTab::setTabLocation(NotebookTabLocation location) +{ + if (this->tabLocation_ != location) + { + this->tabLocation_ = location; + this->update(); + } +} + void NotebookTab::setLive(bool isLive) { if (this->isLive_ != isLive) @@ -401,19 +433,56 @@ void NotebookTab::paintEvent(QPaintEvent *) (windowFocused ? colors.backgrounds.regular : colors.backgrounds.unfocused); + auto selectionOffset = ceil((this->selected_ ? 0.f : 1.f) * scale); + // fill the tab background auto bgRect = this->rect(); - bgRect.setTop(ceil((this->selected_ ? 0.f : 1.f) * scale)); + switch (this->tabLocation_) + { + case NotebookTabLocation::Top: + bgRect.setTop(selectionOffset); + break; + case NotebookTabLocation::Left: + bgRect.setLeft(selectionOffset); + break; + case NotebookTabLocation::Right: + bgRect.setRight(bgRect.width() - selectionOffset); + break; + case NotebookTabLocation::Bottom: + bgRect.setBottom(bgRect.height() - selectionOffset); + break; + } painter.fillRect(bgRect, tabBackground); - // top line - painter.fillRect( - QRectF(0, ceil((this->selected_ ? 0.f : 1.f) * scale), this->width(), - ceil((this->selected_ ? 2.f : 1.f) * scale)), - this->mouseOver_ - ? colors.line.hover - : (windowFocused ? colors.line.regular : colors.line.unfocused)); + // draw color indicator line + auto lineThickness = ceil((this->selected_ ? 2.f : 1.f) * scale); + auto lineColor = this->mouseOver_ ? colors.line.hover + : (windowFocused ? colors.line.regular + : colors.line.unfocused); + + QRect lineRect; + switch (this->tabLocation_) + { + case NotebookTabLocation::Top: + lineRect = + QRect(bgRect.left(), bgRect.y(), bgRect.width(), lineThickness); + break; + case NotebookTabLocation::Left: + lineRect = + QRect(bgRect.x(), bgRect.top(), lineThickness, bgRect.height()); + break; + case NotebookTabLocation::Right: + lineRect = QRect(bgRect.right() - lineThickness, bgRect.top(), + lineThickness, bgRect.height()); + break; + case NotebookTabLocation::Bottom: + lineRect = QRect(bgRect.left(), bgRect.bottom() - lineThickness, + bgRect.width(), lineThickness); + break; + } + + painter.fillRect(lineRect, lineColor); // draw live indicator if (this->isLive_ && getSettings()->showTabLive) @@ -426,9 +495,12 @@ void NotebookTab::paintEvent(QPaintEvent *) painter.setBrush(b); auto x = this->width() - (7 * scale); - auto y = 4 * scale + (this->isSelected() ? 0 : 1); + auto y = 4 * scale; auto diameter = 4 * scale; - painter.drawEllipse(QRectF(x, y, diameter, diameter)); + QRect liveIndicatorRect(x, y, diameter, diameter); + translateRectForLocation(liveIndicatorRect, this->tabLocation_, + this->selected_ ? 0 : -1); + painter.drawEllipse(liveIndicatorRect); } // set the pen color @@ -440,8 +512,9 @@ void NotebookTab::paintEvent(QPaintEvent *) // draw text int offset = int(scale * 8); - QRect textRect(offset, this->selected_ ? 1 : 2, - this->width() - offset - offset, height); + QRect textRect(offset, 0, this->width() - offset - offset, height); + translateRectForLocation(textRect, this->tabLocation_, + this->selected_ ? -1 : -2); if (this->shouldDrawXButton()) { @@ -465,9 +538,6 @@ void NotebookTab::paintEvent(QPaintEvent *) QRect xRect = this->getXRect(); if (!xRect.isNull()) { - if (this->selected_) - xRect.moveTop(xRect.top() - 1); - painter.setBrush(QColor("#fff")); if (this->mouseOverX_) @@ -495,11 +565,26 @@ void NotebookTab::paintEvent(QPaintEvent *) this->fancyPaint(painter); } - // draw line at bottom + // draw line at border if (!this->selected_ && this->isInLastRow_) { - painter.fillRect(0, this->height() - 1, this->width(), 1, - app->themes->window.background); + QRect borderRect; + switch (this->tabLocation_) + { + case NotebookTabLocation::Top: + borderRect = QRect(0, this->height() - 1, this->width(), 1); + break; + case NotebookTabLocation::Left: + borderRect = QRect(this->width() - 1, 0, 1, this->height()); + break; + case NotebookTabLocation::Right: + borderRect = QRect(0, 0, 1, this->height()); + break; + case NotebookTabLocation::Bottom: + borderRect = QRect(0, 0, this->width(), 1); + break; + } + painter.fillRect(borderRect, app->themes->window.background); } } @@ -683,14 +768,26 @@ void NotebookTab::wheelEvent(QWheelEvent *event) QRect NotebookTab::getXRect() { - // if (!this->notebook->getAllowUserTabManagement()) { - // return QRect(); - // } - + QRect rect = this->rect(); float s = this->scale(); - return QRect(this->width() - static_cast(20 * s), - static_cast(9 * s), static_cast(16 * s), - static_cast(16 * s)); + int size = static_cast(16 * s); + + int centerAdjustment = + this->tabLocation_ == + (NotebookTabLocation::Top || + this->tabLocation_ == NotebookTabLocation::Bottom) + ? (size / 3) // slightly off true center + : (size / 2); // true center + + QRect xRect(rect.right() - static_cast(20 * s), + rect.center().y() - centerAdjustment, size, size); + + if (this->selected_) + { + translateRectForLocation(xRect, this->tabLocation_, 1); + } + + return xRect; } } // namespace chatterino diff --git a/src/widgets/helper/NotebookTab.hpp b/src/widgets/helper/NotebookTab.hpp index 1bd893efb..6fd4db9e7 100644 --- a/src/widgets/helper/NotebookTab.hpp +++ b/src/widgets/helper/NotebookTab.hpp @@ -2,6 +2,7 @@ #include "common/Common.hpp" #include "widgets/BaseWidget.hpp" +#include "widgets/Notebook.hpp" #include "widgets/helper/Button.hpp" #include @@ -40,6 +41,7 @@ public: void setSelected(bool value); void setInLastRow(bool value); + void setTabLocation(NotebookTabLocation location); void setLive(bool isLive); void setHighlightState(HighlightState style); @@ -94,6 +96,7 @@ private: bool mouseDownX_{}; bool isInLastRow_{}; int mouseWheelDelta_ = 0; + NotebookTabLocation tabLocation_ = NotebookTabLocation::Top; HighlightState highlightState_ = HighlightState::None; bool highlightEnabled_ = true;