Skip to content

Commit

Permalink
feat: remove the limit of term splitting (linuxdeepin#353)
Browse files Browse the repository at this point in the history
* feat: remove the limit of term splitting

a feature request from here: https://bbs.deepin.org/post/277237

* fix: no term gets focus after closing one

fix no term gets focus after closing one, which will cause crashes
if you hit Alt+Q in sequence.

* fix: change minimum size set on TermWidget

original minimum size relies on the limit of one can only split
the terminal once, since we removed the limits, the minimum size
should change accordingly

* fix: new term not showing in the right position

fix the bug that new term not showing right after the old one after
the splitting

* fix: new term resized wrongly

fix the bug that new term has wrong size after splitting

* fix: disable splitting menu if there's no room

disable splitting menu when there's no enough room for splitting

* fix: shortcut can still do splitting on room left

fix shortcut still work even when there's no room for splitting

* fix: crash of unfound size

createSubSplit has side effects, it removes the term form its
parent, so find size of it in QSplitter::sizes crashes.

* chore: some code refactory

- change CanSplit to a public funtion of TermWidget
- change MIN_WIDTH and MIN_HEIGHT of TermWidget to private
  • Loading branch information
hualet authored and mhduiy committed Oct 22, 2024
1 parent 08013d7 commit 4591760
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 108 deletions.
24 changes: 6 additions & 18 deletions src/main/mainwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1131,15 +1131,9 @@ inline void MainWindow::slotShortcutHorizonzalSplit()
// 判读数量是否允许分屏
if (Service::instance()->isCountEnable()) {
TermWidgetPage *page = currentPage();
if (page) {
if (page->currentTerminal()) {
int layer = page->currentTerminal()->getTermLayer();
DSplitter *splitter = qobject_cast<DSplitter *>(page->currentTerminal()->parentWidget());
if (1 == layer || (2 == layer && splitter && Qt::Horizontal == splitter->orientation())) {
page->split(Qt::Horizontal);
return ;
}
}
if (page && page->currentTerminal()->canSplit(Qt::Vertical)) {
page->split(Qt::Horizontal);
return ;
}
}
qInfo() << "can't split vertical again";
Expand All @@ -1150,15 +1144,9 @@ inline void MainWindow::slotShortcutVerticalSplit()
// 判读数量是否允许分屏
if (Service::instance()->isCountEnable()) {
TermWidgetPage *page = currentPage();
if (page) {
if (page->currentTerminal()) {
int layer = page->currentTerminal()->getTermLayer();
DSplitter *splitter = qobject_cast<DSplitter *>(page->currentTerminal()->parentWidget());
if (1 == layer || (2 == layer && splitter && Qt::Vertical == splitter->orientation())) {
page->split(Qt::Vertical);
return ;
}
}
if (page && page->currentTerminal()->canSplit(Qt::Horizontal)) {
page->split(Qt::Vertical);
return ;
}
}
qInfo() << "can't split vertical again";
Expand Down
66 changes: 38 additions & 28 deletions src/views/termwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,7 @@ TermWidget::TermWidget(const TermProperties &properties, QWidget *parent) : QTer
// 按键滚动
setPressingScroll(Settings::instance()->PressingScroll());

/******** Modify by ut000439 wangpeili 2020-07-27: fix bug 39371: 分屏线可以拉到边****/
// 以最小mainwindow分4屏为标准的最小大小
/******** Modify by ut001000 renfeixiang 2020-08-07:修改成根据全局变量m_MinWidth,m_MinHeight计算出term的最小高度和宽度***************/
setMinimumSize(MainWindow::m_MinWidth / 2, (MainWindow::m_MinHeight - WIN_TITLE_BAR_HEIGHT) / 2);
/********************* Modify by n014361 wangpeili End ************************/
setMinimumSize(MIN_WIDTH, MIN_HEIGHT);

QString currentEnvLanguage = Utils::getCurrentEnvLanguage();
// 判断是维吾尔语或者藏语时
Expand Down Expand Up @@ -481,15 +477,11 @@ void TermWidget::addMenuActions(const QPoint &pos)

m_menu->addSeparator();


DSplitter *splitter = qobject_cast<DSplitter *>(parentWidget());
int layer = getTermLayer();

if (1 == layer || (2 == layer && splitter && Qt::Horizontal == splitter->orientation()))
m_menu->addAction(tr("Horizontal split"), this, &TermWidget::onHorizontalSplit);

if (1 == layer || (2 == layer && splitter && Qt::Vertical == splitter->orientation()))
m_menu->addAction(tr("Vertical split"), this, &TermWidget::onVerticalSplit);
QAction *action = 0;
action = m_menu->addAction(tr("Horizontal split"), this, &TermWidget::onHorizontalSplit);
action->setEnabled(canSplit(Qt::Vertical));
action = m_menu->addAction(tr("Vertical split"), this, &TermWidget::onVerticalSplit);
action->setEnabled(canSplit(Qt::Horizontal));

/******** Modify by n014361 wangpeili 2020-02-21: 增加关闭窗口和关闭其它窗口菜单 ****************/
m_menu->addAction(QObject::tr("Close workspace"), this, &TermWidget::onCloseCurrWorkSpace);
Expand Down Expand Up @@ -555,14 +547,12 @@ void TermWidget::addMenuActions(const QPoint &pos)

inline void TermWidget::onHorizontalSplit()
{
getTermLayer();
// menu关闭与分屏同时进行时,会导致QT计算光标位置异常。
QTimer::singleShot(10, this, &TermWidget::splitHorizontal);
}

inline void TermWidget::onVerticalSplit()
{
getTermLayer();
// menu关闭与分屏同时进行时,会导致QT计算光标位置异常。
QTimer::singleShot(10, this, &TermWidget::splitVertical);
}
Expand Down Expand Up @@ -824,18 +814,6 @@ void TermWidget::setDeleteMode(const EraseMode &deleteMode)
QTermWidget::setDeleteMode(&ch, length);
}

int TermWidget::getTermLayer()
{
int layer = 1;
QWidget *currentW = this;
while (currentW->parentWidget() != parentPage()) {
layer++;
currentW = currentW->parentWidget();
}
qInfo() << "getTermLayer = " << layer;
return layer;
}

void TermWidget::setTabFormat(const QString &tabFormat)
{
// 非全局设置优先级更高
Expand Down Expand Up @@ -1033,6 +1011,38 @@ bool TermWidget::isInRemoteServer()
return false;
}

bool TermWidget::canSplit(Qt::Orientation ori) {
qDebug() << "CanSplit:" << ori;
QSplitter *splitter = qobject_cast<QSplitter *>(this->parentWidget());
int minimumSize = ori == Qt::Horizontal ? TermWidget::MIN_WIDTH : TermWidget::MIN_HEIGHT;
if (splitter) {
if (splitter->orientation() == ori) {
QList<int> sizes = splitter->sizes();
// new term has same size portion as the current one.
sizes.append(sizes.at(splitter->indexOf(this)));

double sum = 0;
for (int i = 0; i < sizes.count(); i++) {
sum += sizes.at(i);
}

for(int i = 0; i < sizes.count(); i++) {
int totalSize = ori == Qt::Horizontal ? splitter->width() : splitter->height();
int actualSize = (totalSize) * (sizes.at(i) / sum);
if (actualSize < minimumSize)
return false;
}
} else {
int splitterSize = ori == Qt::Horizontal ? splitter->width() : splitter->height();
if (splitterSize / 2.0 < minimumSize)
return false;
}
}

return true;
}


void TermWidget::setTermOpacity(qreal opacity)
{
//这里再次判断一遍,因为刚启动时,还是需要判断一次当前是否开启了窗口特效
Expand Down
14 changes: 7 additions & 7 deletions src/views/termwidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ class TermWidget : public QTermWidget
{
Q_OBJECT
public:
bool canSplit(Qt::Orientation ori);

TermWidget(const TermProperties &properties, QWidget *parent = nullptr);
~TermWidget();
/**
Expand All @@ -79,6 +81,7 @@ class TermWidget : public QTermWidget
* @return
*/
bool isInRemoteServer();

public:
/**
* @brief 设置不透明度
Expand Down Expand Up @@ -189,13 +192,6 @@ class TermWidget : public QTermWidget
*/
void setDeleteMode(const EraseMode &deleteMode);

/**
* @brief 获取当前terminal距离page的层次.用于限定分屏
* @author ut000439 王培利
* @return
*/
int getTermLayer();

/**
* @brief 设置标签标题格式(全局设置)
* @author ut000610 戴正文
Expand Down Expand Up @@ -465,6 +461,10 @@ private slots:
QString m_remotePassword;
//是否准备远程
bool m_remotePasswordIsReady = false;

// 9:6
static const int MIN_WIDTH = 180;
static const int MIN_HEIGHT = 120;
};

#endif // TERMWIDGET_H
148 changes: 93 additions & 55 deletions src/views/termwidgetpage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,27 @@
#include <QVBoxLayout>
#include <QApplication>

// Find the previous term widget in the widget tree.
static TermWidget* WidgetTreeReverseFindTerm(QWidget *widget)
{
QList<TermWidget*> termList = widget->findChildren<TermWidget *>();
for (TermWidget *t : termList) {
if (t) {
qInfo() << "TermWidget found in current widget:" << t;
return t;
}
}

QWidget *parent = widget->parentWidget();
if (parent) {
qInfo() << "Searching in parent widget:" << parent;
return WidgetTreeReverseFindTerm(parent);
}
qInfo() << "No TermWidget found in the widget tree.";
return nullptr;
}


TermWidgetPage::TermWidgetPage(const TermProperties &properties, QWidget *parent)
: QWidget(parent), m_findBar(new PageSearchBar(this))
{
Expand Down Expand Up @@ -85,21 +106,26 @@ void TermWidgetPage::setParentMainWindow(MainWindow *mainWin)
m_MainWindow = mainWin;
}

// TODO(hualet): maybe implement a subclass of DSplitter and
// override the createHandle method, all setSplitStyle should
// be removed.
void TermWidgetPage::setSplitStyle(DSplitter *splitter)
{
splitter->setHandleWidth(1);
QSplitterHandle *handle = splitter->handle(1);

if (handle) {
//分割线颜色暂时设置为Highlight颜色,需要和UI确认下
//此处代码暂时保留 //DPalette pa = DPaletteHelper::instance()->palette(handle);
//bug#57044 中的分割线颜色,保留的代码对默认主题,和十个内置主题的颜色是正确获取,但是在自定义的颜色获取存在异常,采取如下方式获取
DPalette pa = DGuiApplicationHelper::instance()->applicationPalette();
QColor splitBrush = pa.color(DPalette::Highlight);
pa.setBrush(DPalette::Background, splitBrush);
handle->setPalette(pa);
handle->setBackgroundRole(QPalette::Background);
handle->setAutoFillBackground(true);

for (int i = 1; i < splitter->count(); ++i) {
QSplitterHandle *handle = splitter->handle(i);
if (handle) {
//分割线颜色暂时设置为Highlight颜色,需要和UI确认下
//此处代码暂时保留 //DPalette pa = DPaletteHelper::instance()->palette(handle);
//bug#57044 中的分割线颜色,保留的代码对默认主题,和十个内置主题的颜色是正确获取,但是在自定义的颜色获取存在异常,采取如下方式获取
DPalette pa = DGuiApplicationHelper::instance()->applicationPalette();
QColor splitBrush = pa.color(DPalette::Highlight);
pa.setBrush(DPalette::Background, splitBrush);
handle->setPalette(pa);
handle->setBackgroundRole(QPalette::Background);
handle->setAutoFillBackground(true);
}
}
}

Expand All @@ -119,22 +145,42 @@ void TermWidgetPage::split(Qt::Orientation orientation)
{
parentMainWindow()->showPlugin(MainWindow::PLUGIN_TYPE_NONE);
TermWidget *term = m_currentTerm;
if (1 == getTerminalCount()) {
qInfo() << "first split";
QSplitter *firstSplit = createSubSplit(term, orientation);
m_layout->addWidget(firstSplit);
//return ;

QSplitter *splitter = qobject_cast<QSplitter *>(term->parent());
int index = splitter ? splitter->indexOf(term) : m_layout->indexOf(term);

// if there's already a splitter, and the orientation is correct,
// just add a new term to the splitter.
if (splitter && splitter->orientation() != orientation) {
TermProperties properties(term->workingDirectory());
TermWidget *newTerm = createTerm(properties);

// copy the size of the current term to the new term, so the new term will
// keep the same size portion as the current term after the splitter relayout.
// this behavior is copied form iTerm2.
QList<int> sizes = splitter->sizes();
sizes.insert(index+1, sizes.at(index));
splitter->insertWidget(index+1, newTerm); // insert after the current term
splitter->setSizes(sizes);

setSplitStyle(splitter);
setCurrentTerminal(newTerm);
} else {
qInfo() << "not first split";
QSplitter *upSplit = qobject_cast<QSplitter *>(term->parent());
int index = upSplit->indexOf(term);
QList<int> parentSizes = upSplit->sizes();

// 用新的Split分割布局替换原来的位置
QSplitter *subSplit = createSubSplit(term, orientation);
upSplit->insertWidget(index, subSplit);
upSplit->setSizes(parentSizes);
setSplitStyle(upSplit);
// if there's no splitter, or the orientation is not correct,
// create a new splitter, put the 2 terms into the splitter,
// and replace the old term with the splitter.
if (splitter) {
// see above splitter->insertWidget part to know why.
QList<int> sizes = splitter->sizes();
sizes.insert(index, sizes.at(index));
QSplitter *newSplitter = createSubSplit(term, orientation);
splitter->insertWidget(index, newSplitter);
splitter->setSizes(sizes);
setSplitStyle(splitter);
} else {
QSplitter *newSplitter = createSubSplit(term, orientation);
m_layout->insertWidget(index, newSplitter);
}
}

/******** Add by ut001000 renfeixiang 2020-08-07:新增分屏时改变大小,bug#41436***************/
Expand All @@ -154,6 +200,7 @@ DSplitter *TermWidgetPage::createSubSplit(TermWidget *term, Qt::Orientation orie
TermProperties properties(term->workingDirectory());
term->setParent(nullptr);
TermWidget *newTerm = createTerm(properties);
newTerm->resize(term->size());

// 意义与名称是相反的
DSplitter *subSplit = new DSplitter(orientation == Qt::Horizontal ? Qt::Vertical : Qt::Horizontal,
Expand All @@ -162,7 +209,6 @@ DSplitter *TermWidgetPage::createSubSplit(TermWidget *term, Qt::Orientation orie
subSplit->setFocusPolicy(Qt::NoFocus);
subSplit->insertWidget(0, term);
subSplit->insertWidget(1, newTerm);
subSplit->setSizes({ 1, 1 });
setSplitStyle(subSplit);
setCurrentTerminal(newTerm);
/******** Modify by ut000439 wangpeili 2020-07-27: fix bug 39371: 分屏线可以拉到边****/
Expand All @@ -180,40 +226,31 @@ void TermWidgetPage::closeSplit(TermWidget *term, bool hasConfirmed)
showExitConfirmDialog(Utils::CloseType_Terminal, 1, parentMainWindow());
return;
}
QSplitter *upSplit = qobject_cast<QSplitter *>(term->parent());
term->setParent(nullptr);

// 另一个兄弟也可能是终端,也可能是split,
QWidget *brother = upSplit->widget(0);
TermWidget *nextTerm = upSplit->findChild<TermWidget *>();
// 如果上级是分屏
if ("QSplitter" == QString(upSplit->parent()->metaObject()->className())) {
QSplitter *upupSplit = qobject_cast<QSplitter *>(upSplit->parent());
//兄弟替换parent split
upupSplit->replaceWidget(upupSplit->indexOf(upSplit), brother);
}
// 上级不是分屏控件,就是布局在控制了
else {
qInfo() << "TermWidgetPage only one term exist!";
m_layout->addWidget(brother);
}

// 子控件的变化会引起焦点的变化,控制焦点要放在最后
if (nextTerm != nullptr) {
qInfo() << "nextTerm change" << m_currentTerm->getSessionId();
nextTerm->setFocus();
} else {
qInfo() << "can not found nextTerm in TermWidget";
}
QWidget *parentWidget = term->parentWidget();

// step1, delete the term
// 释放控件,并隐藏term、upSplit,避免出现闪现窗口bug#80809
term->setParent(nullptr);
term->hide();
term->deleteLater();
// 断开相关的连接:(UT_MainWindow_Test, slotShortcutCloseWorkspace)出现的崩溃问题
Settings::instance()->disconnect(term);
upSplit->hide();
upSplit->setParent(nullptr);
upSplit->deleteLater();

// step2, find the next term to get focus
TermWidget *nextTerm = WidgetTreeReverseFindTerm(parentWidget);
if (nextTerm) {
setCurrentTerminal(nextTerm);
}

// step3, futurer clean the parent splitter if it's empty
QSplitter *upSplit = qobject_cast<QSplitter *>(parentWidget);
if (upSplit && upSplit->count() == 0) {
upSplit->setParent(nullptr);
upSplit->deleteLater();
upSplit = nullptr;
}

qInfo() << "page terminal count =" << getTerminalCount();
/******** Add by ut001000 renfeixiang 2020-08-07:关闭分屏时改变大小,bug#41436***************/
parentMainWindow()->updateMinHeight();
Expand Down Expand Up @@ -709,6 +746,7 @@ void TermWidgetPage::setCurrentTerminal(TermWidget *term)
TermWidget *oldTerm = m_currentTerm;
m_currentTerm = term;
if (oldTerm != m_currentTerm) {
m_currentTerm->setFocus();
// 当前界面切换
qInfo() << "m_currentTerm change" << m_currentTerm->getSessionId();
QString tabTitle = term->getTabTitle();
Expand Down

0 comments on commit 4591760

Please sign in to comment.