Vertical tabs (#1815)

* Vertical tabs

* Vertical tab wrapping

* Fix formatting

* Cleanup code

* Updated changelog

* Hide button row if no buttons exist

* Fix tab width expansion

Co-authored-by: fourtf <tf.four@gmail.com>
This commit is contained in:
Daniel
2020-08-13 09:43:08 -04:00
committed by GitHub
parent b52d15b9b4
commit 0e6ca6b097
10 changed files with 278 additions and 77 deletions
+2 -1
View File
@@ -148,7 +148,8 @@ void AB_THEME_CLASS::actuallyUpdate(double hue, double multiplier)
// this->tabs.highlighted = {fg, {QColor("#777"),
// QColor("#777"), QColor("#666")}};
this->tabs.bottomLine = this->tabs.selected.backgrounds.regular.color();
this->tabs.dividerLine =
this->tabs.selected.backgrounds.regular.color();
}
// Message
+1 -1
View File
@@ -49,7 +49,7 @@ public:
TabColors highlighted;
TabColors selected;
QColor border;
QColor bottomLine;
QColor dividerLine;
} tabs;
/// MESSAGES
+4
View File
@@ -9,6 +9,7 @@
#include "controllers/highlights/HighlightPhrase.hpp"
#include "controllers/moderationactions/ModerationAction.hpp"
#include "singletons/Toasts.hpp"
#include "widgets/Notebook.hpp"
namespace chatterino {
@@ -78,6 +79,9 @@ public:
BoolSetting colorizeNicknames = {"/appearance/messages/colorizeNicknames",
false};
IntSetting tabDirection = {"/appearance/tabDirection",
NotebookTabDirection::Horizontal};
// BoolSetting collapseLongMessages =
// {"/appearance/messages/collapseLongMessages", false};
IntSetting collpseMessagesMinLines = {
+203 -72
View File
@@ -357,101 +357,232 @@ void Notebook::performLayout(bool animated)
const auto tabHeight = int(NOTEBOOK_TAB_HEIGHT * scale);
const auto addButtonWidth = this->showAddButton_ ? tabHeight : 0;
auto x = left;
auto y = 0;
// set size of custom buttons (settings, user, ...)
for (auto *btn : this->customButtons_)
if (this->tabDirection_ == NotebookTabDirection::Horizontal)
{
if (!btn->isVisible())
auto x = left;
auto y = 0;
// set size of custom buttons (settings, user, ...)
for (auto *btn : this->customButtons_)
{
continue;
if (!btn->isVisible())
{
continue;
}
btn->setFixedSize(tabHeight, tabHeight - 1);
btn->move(x, 0);
x += tabHeight;
}
btn->setFixedSize(tabHeight, tabHeight - 1);
btn->move(x, 0);
x += tabHeight;
}
// layout tabs
/// Notebook tabs need to know if they are in the last row.
auto firstInBottomRow =
this->items_.size() ? &this->items_.front() : nullptr;
// layout tabs
/// Notebook tabs need to know if they are in the last row.
auto firstInBottomRow =
this->items_.size() ? &this->items_.front() : nullptr;
for (auto &item : this->items_)
{
/// Break line if element doesn't fit.
auto isFirst = &item == &this->items_.front();
auto isLast = &item == &this->items_.back();
auto fitsInLine =
((isLast ? addButtonWidth : 0) + x + item.tab->width()) <= width();
if (!isFirst && !fitsInLine)
for (auto &item : this->items_)
{
y += item.tab->height();
x = left;
firstInBottomRow = &item;
/// Break line if element doesn't fit.
auto isFirst = &item == &this->items_.front();
auto isLast = &item == &this->items_.back();
auto fitsInLine = ((isLast ? addButtonWidth : 0) + x +
item.tab->width()) <= width();
if (!isFirst && !fitsInLine)
{
y += item.tab->height();
x = left;
firstInBottomRow = &item;
}
/// Layout tab
item.tab->growWidth(0);
item.tab->moveAnimated(QPoint(x, y), animated);
x += item.tab->width() + std::max<int>(1, int(scale * 1));
}
/// Layout tab
item.tab->moveAnimated(QPoint(x, y), animated);
x += item.tab->width() + std::max<int>(1, int(scale * 1));
}
/// Update which tabs are in the last row
auto inLastRow = false;
for (const auto &item : this->items_)
{
if (&item == firstInBottomRow)
/// Update which tabs are in the last row
auto inLastRow = false;
for (const auto &item : this->items_)
{
inLastRow = true;
if (&item == firstInBottomRow)
{
inLastRow = true;
}
item.tab->setInLastRow(inLastRow);
}
// move misc buttons
if (this->showAddButton_)
{
this->addButton_->move(x, y);
}
if (this->lineOffset_ != y + tabHeight)
{
this->lineOffset_ = y + tabHeight;
this->update();
}
/// Increment for the line at the bottom
y += int(2 * scale);
// raise elements
for (auto &i : this->items_)
{
i.tab->raise();
}
if (this->showAddButton_)
{
this->addButton_->raise();
}
// set page bounds
if (this->selectedPage_ != nullptr)
{
this->selectedPage_->move(0, y + tabHeight);
this->selectedPage_->resize(width(), height() - y - tabHeight);
this->selectedPage_->raise();
}
item.tab->setInLastRow(inLastRow);
}
// move misc buttons
if (this->showAddButton_)
else
{
this->addButton_->move(x, y);
const int lineThickness = int(2 * scale);
auto x = left;
auto y = 0;
// set size of custom buttons (settings, user, ...)
for (auto *btn : this->customButtons_)
{
if (!btn->isVisible())
{
continue;
}
btn->setFixedSize(tabHeight, tabHeight - 1);
btn->move(x, y);
x += tabHeight;
}
if (this->customButtons_.size() > 0)
y = tabHeight;
int buttonWidth = x;
int top = y;
x = left;
int verticalRowSpace = (this->height() - top) / tabHeight;
if (verticalRowSpace == 0) // window hasn't properly rendered yet
return;
int count = this->items_.size() + (this->showAddButton_ ? 1 : 0);
int columnCount = ceil((float)count / verticalRowSpace);
for (int col = 0; col < columnCount; col++)
{
auto largestWidth = 0;
int colStart = col * verticalRowSpace;
int colEnd =
std::min((col + 1) * verticalRowSpace, this->items_.size());
for (int i = colStart; i < colEnd; i++)
{
largestWidth = std::max(
this->items_.at(i).tab->normalTabWidth(), largestWidth);
}
if (col == columnCount - 1 && this->showAddButton_ &&
largestWidth == 0)
{
largestWidth = this->addButton_->width();
}
if (largestWidth + x < buttonWidth && col == columnCount - 1)
largestWidth = buttonWidth - x;
for (int i = colStart; i < colEnd; i++)
{
auto item = this->items_.at(i);
/// Layout tab
item.tab->growWidth(largestWidth);
item.tab->moveAnimated(QPoint(x, y), animated);
y += tabHeight;
}
if (col == columnCount - 1 && this->showAddButton_)
{
this->addButton_->move(x, y);
}
x += largestWidth + lineThickness;
y = top;
}
x = std::max(x, buttonWidth);
if (this->lineOffset_ != x - lineThickness)
{
this->lineOffset_ = x - lineThickness;
this->update();
}
// raise elements
for (auto &i : this->items_)
{
i.tab->raise();
}
if (this->showAddButton_)
{
this->addButton_->raise();
}
// set page bounds
if (this->selectedPage_ != nullptr)
{
this->selectedPage_->move(x, 0);
this->selectedPage_->resize(width() - x, height());
this->selectedPage_->raise();
}
}
}
if (this->lineY_ != y + tabHeight)
void Notebook::setTabDirection(NotebookTabDirection direction)
{
if (direction != this->tabDirection_)
{
this->lineY_ = y + tabHeight;
this->update();
}
/// Increment for the line at the bottom
y += int(2 * scale);
// raise elements
for (auto &i : this->items_)
{
i.tab->raise();
}
if (this->showAddButton_)
{
this->addButton_->raise();
}
// set page bounds
if (this->selectedPage_ != nullptr)
{
this->selectedPage_->move(0, y + tabHeight);
this->selectedPage_->resize(width(), height() - y - tabHeight);
this->selectedPage_->raise();
this->tabDirection_ = direction;
this->performLayout();
}
}
void Notebook::paintEvent(QPaintEvent *event)
{
BaseWidget::paintEvent(event);
auto scale = this->scale();
QPainter painter(this);
painter.fillRect(0, this->lineY_, this->width(), int(2 * this->scale()),
this->theme->tabs.bottomLine);
if (this->tabDirection_ == NotebookTabDirection::Horizontal)
{
/// horizontal line
painter.fillRect(0, this->lineOffset_, this->width(), int(2 * scale),
this->theme->tabs.dividerLine);
}
else
{
if (this->customButtons_.size() > 0)
{
painter.fillRect(0, int(NOTEBOOK_TAB_HEIGHT * scale),
this->lineOffset_, int(2 * scale),
this->theme->tabs.dividerLine);
}
/// vertical line
painter.fillRect(this->lineOffset_, 0, int(2 * scale), this->height(),
this->theme->tabs.dividerLine);
}
}
NotebookButton *Notebook::getAddButton()
+6 -1
View File
@@ -16,6 +16,8 @@ class NotebookButton;
class NotebookTab;
class SplitContainer;
enum NotebookTabDirection { Horizontal = 0, Vertical = 1 };
class Notebook : public BaseWidget
{
Q_OBJECT
@@ -52,6 +54,8 @@ public:
void performLayout(bool animate = false);
void setTabDirection(NotebookTabDirection direction);
protected:
virtual void scaleChangedEvent(float scale_) override;
virtual void resizeEvent(QResizeEvent *) override;
@@ -81,7 +85,8 @@ private:
bool allowUserTabManagement_ = false;
bool showAddButton_ = false;
int lineY_ = 20;
int lineOffset_ = 20;
NotebookTabDirection tabDirection_ = NotebookTabDirection::Horizontal;
};
class SplitNotebook : public Notebook, pajlada::Signals::SignalHolder
+3
View File
@@ -64,6 +64,9 @@ Window::Window(WindowType type)
if (type == WindowType::Main)
{
this->resize(int(600 * this->scale()), int(500 * this->scale()));
getSettings()->tabDirection.connect([this](int val) {
this->notebook_->setTabDirection(NotebookTabDirection(val));
});
}
else
{
+28 -2
View File
@@ -89,7 +89,20 @@ void NotebookTab::themeChangedEvent()
this->setMouseEffectColor(this->theme->tabs.regular.text);
}
void NotebookTab::updateSize()
void NotebookTab::growWidth(int width)
{
if (this->growWidth_ != width)
{
this->growWidth_ = width;
this->updateSize();
}
else
{
this->growWidth_ = width;
}
}
int NotebookTab::normalTabWidth()
{
float scale = this->scale();
int width;
@@ -114,8 +127,21 @@ void NotebookTab::updateSize()
{
width = clamp(width, this->height(), int(150 * scale));
}
return width;
}
void NotebookTab::updateSize()
{
float scale = this->scale();
int width = this->normalTabWidth();
auto height = int(NOTEBOOK_TAB_HEIGHT * scale);
if (width < this->growWidth_)
{
width = this->growWidth_;
}
if (this->width() != width || this->height() != height)
{
this->resize(width, height);
@@ -175,7 +201,7 @@ void NotebookTab::titleUpdated()
{
// Queue up save because: Tab title changed
getApp()->windows->queueSave();
this->notebook_->performLayout();
this->updateSize();
this->update();
}
+5
View File
@@ -51,6 +51,9 @@ public:
QRect getDesiredRect() const;
void hideTabXChanged();
void growWidth(int width);
int normalTabWidth();
protected:
virtual void themeChangedEvent() override;
@@ -98,6 +101,8 @@ private:
bool isLive_{};
int growWidth_ = 0;
QMenu menu_;
std::vector<pajlada::Signals::ScopedConnection> managedConnections_;
+25
View File
@@ -299,6 +299,31 @@ void GeneralPage::initLayout(SettingsLayout &layout)
return QString::number(val) + "x";
},
[](auto args) { return fuzzyToFloat(args.value, 1.f); });
layout.addDropdown<int>(
"Tab direction", {"Horizontal", "Vertical"}, s.tabDirection,
[](auto val) {
switch (val)
{
case NotebookTabDirection::Horizontal:
return "Horizontal";
case NotebookTabDirection::Vertical:
return "Vertical";
}
return "";
},
[](auto args) {
if (args.value == "Vertical")
{
return NotebookTabDirection::Vertical;
}
else
{
// default to horizontal
return NotebookTabDirection::Horizontal;
}
});
layout.addCheckbox("Show tab close button", s.showTabCloseButton);
layout.addCheckbox("Always on top", s.windowTopMost);
#ifdef USEWINSDK