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
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 278 additions and 77 deletions

View file

@ -4,6 +4,7 @@
- Major: We now support image thumbnails coming from the link resolver. This feature is off by default and can be enabled in the settings with the "Show link thumbnail" setting. This feature also requires the "Show link info when hovering" setting to be enabled (#1664)
- Major: Added image upload functionality to i.nuuls.com with an ability to change upload destination. This works by dragging and dropping an image into a split, or pasting an image into the text edit field. (#1332, #1741)
- Major: Added option to display tabs vertically. (#1815)
- Minor: Clicking on `Open in browser` in a whisper split will now open your whispers on twitch. (#1828)
- Minor: Clicking on @mentions will open the User Popup. (#1674)
- Minor: You can now open the Twitch User Card by middle-mouse clicking a username. (#1669)

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

View file

@ -49,7 +49,7 @@ public:
TabColors highlighted;
TabColors selected;
QColor border;
QColor bottomLine;
QColor dividerLine;
} tabs;
/// MESSAGES

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 = {

View file

@ -357,6 +357,8 @@ void Notebook::performLayout(bool animated)
const auto tabHeight = int(NOTEBOOK_TAB_HEIGHT * scale);
const auto addButtonWidth = this->showAddButton_ ? tabHeight : 0;
if (this->tabDirection_ == NotebookTabDirection::Horizontal)
{
auto x = left;
auto y = 0;
@ -384,8 +386,8 @@ void Notebook::performLayout(bool animated)
auto isFirst = &item == &this->items_.front();
auto isLast = &item == &this->items_.back();
auto fitsInLine =
((isLast ? addButtonWidth : 0) + x + item.tab->width()) <= width();
auto fitsInLine = ((isLast ? addButtonWidth : 0) + x +
item.tab->width()) <= width();
if (!isFirst && !fitsInLine)
{
@ -395,6 +397,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<int>(1, int(scale * 1));
}
@ -416,9 +419,9 @@ void Notebook::performLayout(bool animated)
this->addButton_->move(x, y);
}
if (this->lineY_ != y + tabHeight)
if (this->lineOffset_ != y + tabHeight)
{
this->lineY_ = y + tabHeight;
this->lineOffset_ = y + tabHeight;
this->update();
}
@ -443,15 +446,143 @@ void Notebook::performLayout(bool animated)
this->selectedPage_->resize(width(), height() - y - tabHeight);
this->selectedPage_->raise();
}
}
else
{
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();
}
}
}
void Notebook::setTabDirection(NotebookTabDirection direction)
{
if (direction != this->tabDirection_)
{
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()

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

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
{

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();
}

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_;

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