Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vertical tabs #1815

Merged
merged 8 commits into from
Aug 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion src/BaseTheme.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/BaseTheme.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class AB_THEME_CLASS
TabColors highlighted;
TabColors selected;
QColor border;
QColor bottomLine;
QColor dividerLine;
} tabs;

/// MESSAGES
Expand Down
4 changes: 4 additions & 0 deletions src/singletons/Settings.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "controllers/highlights/HighlightPhrase.hpp"
#include "controllers/moderationactions/ModerationAction.hpp"
#include "singletons/Toasts.hpp"
#include "widgets/Notebook.hpp"

namespace chatterino {

Expand Down Expand Up @@ -78,6 +79,9 @@ class Settings : public ABSettings, public ConcurrentSettings
BoolSetting colorizeNicknames = {"/appearance/messages/colorizeNicknames",
false};

IntSetting tabDirection = {"/appearance/tabDirection",
NotebookTabDirection::Horizontal};

// BoolSetting collapseLongMessages =
// {"/appearance/messages/collapseLongMessages", false};
IntSetting collpseMessagesMinLines = {
Expand Down
259 changes: 195 additions & 64 deletions src/widgets/Notebook.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();

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

auto fitsInLine =
((isLast ? addButtonWidth : 0) + x + item.tab->width()) <= width();
if (!isFirst && !fitsInLine)
{
y += item.tab->height();
x = left;
firstInBottomRow = &item;
}

if (!isFirst && !fitsInLine)
/// Layout tab
item.tab->growWidth(0);
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_)
{
y += item.tab->height();
x = left;
firstInBottomRow = &item;
if (&item == firstInBottomRow)
{
inLastRow = true;
}
item.tab->setInLastRow(inLastRow);
}

/// Layout tab
item.tab->moveAnimated(QPoint(x, y), animated);
x += item.tab->width() + std::max<int>(1, int(scale * 1));
}
// move misc buttons
if (this->showAddButton_)
{
this->addButton_->move(x, y);
}

/// Update which tabs are in the last row
auto inLastRow = false;
for (const auto &item : this->items_)
{
if (&item == firstInBottomRow)
if (this->lineOffset_ != y + tabHeight)
{
inLastRow = true;
this->lineOffset_ = y + tabHeight;
this->update();
}
item.tab->setInLastRow(inLastRow);
}

// move misc buttons
if (this->showAddButton_)
{
this->addButton_->move(x, y);
}
/// Increment for the line at the bottom
y += int(2 * scale);

if (this->lineY_ != y + tabHeight)
{
this->lineY_ = y + tabHeight;
this->update();
}
// raise elements
for (auto &i : this->items_)
{
i.tab->raise();
}

/// Increment for the line at the bottom
y += int(2 * scale);
if (this->showAddButton_)
{
this->addButton_->raise();
}

// raise elements
for (auto &i : this->items_)
{
i.tab->raise();
// set page bounds
if (this->selectedPage_ != nullptr)
{
this->selectedPage_->move(0, y + tabHeight);
this->selectedPage_->resize(width(), height() - y - tabHeight);
this->selectedPage_->raise();
}
}

if (this->showAddButton_)
else
{
this->addButton_->raise();
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();
}
}
}

// set page bounds
if (this->selectedPage_ != nullptr)
void Notebook::setTabDirection(NotebookTabDirection direction)
{
if (direction != this->tabDirection_)
{
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()
Expand Down
7 changes: 6 additions & 1 deletion src/widgets/Notebook.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ class NotebookButton;
class NotebookTab;
class SplitContainer;

enum NotebookTabDirection { Horizontal = 0, Vertical = 1 };

class Notebook : public BaseWidget
{
Q_OBJECT
Expand Down Expand Up @@ -52,6 +54,8 @@ class Notebook : public BaseWidget

void performLayout(bool animate = false);

void setTabDirection(NotebookTabDirection direction);

protected:
virtual void scaleChangedEvent(float scale_) override;
virtual void resizeEvent(QResizeEvent *) override;
Expand Down Expand Up @@ -81,7 +85,8 @@ class Notebook : public BaseWidget

bool allowUserTabManagement_ = false;
bool showAddButton_ = false;
int lineY_ = 20;
int lineOffset_ = 20;
NotebookTabDirection tabDirection_ = NotebookTabDirection::Horizontal;
};

class SplitNotebook : public Notebook, pajlada::Signals::SignalHolder
Expand Down
3 changes: 3 additions & 0 deletions src/widgets/Window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down
Loading