Skip to content

Commit

Permalink
[81_4] Implement multi-line display for numerous tab pages
Browse files Browse the repository at this point in the history
## What
See in
[https://github.com/XmacsLabs/mogan/pull/1961](https://github.com/XmacsLabs/mogan/pull/1961)
## How to test your changes?
Create numerous tab pages using `Ctrl+N`, and then maximize/minimize the
window to view changes.
  • Loading branch information
Minzihao authored Aug 1, 2024
1 parent b4c3b41 commit 161c60f
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 20 deletions.
116 changes: 109 additions & 7 deletions src/Plugins/Qt/QTMTabPage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,21 @@

#include "QTMTabPage.hpp"

// The minimum width of a single tab page (in pixels).
#define MIN_TAB_PAGE_WIDTH 150

/******************************************************************************
* QTMTabPage
******************************************************************************/

QTMTabPage::QTMTabPage (url p_url, QAction* p_title, QAction* p_closeBtn,
bool p_isActive)
: bufferUrl (p_url) {
: m_bufferUrl (p_url) {
p_title->setCheckable (p_isActive);
p_title->setChecked (p_isActive);
closeBtn= new QToolButton (this);
closeBtn->setDefaultAction (p_closeBtn);
closeBtn->setFixedSize (20, 20); // position will be updated in resizeEvent
m_closeBtn= new QToolButton (this);
m_closeBtn->setDefaultAction (p_closeBtn);
m_closeBtn->setFixedSize (20, 20); // position will be updated in resizeEvent

setStyleSheet (
"QToolButton{ padding: 0 26px; }"); // reserve space for closeBtn
Expand All @@ -31,10 +34,109 @@ QTMTabPage::QTMTabPage (url p_url, QAction* p_title, QAction* p_closeBtn,

void
QTMTabPage::resizeEvent (QResizeEvent* e) {
int w= closeBtn->width ();
int h= closeBtn->height ();
int w= m_closeBtn->width ();
int h= m_closeBtn->height ();
int x= e->size ().width () - w - 8;
int y= e->size ().height () / 2 - h / 2;

closeBtn->setGeometry (x, y, w, h);
m_closeBtn->setGeometry (x, y, w, h);
}

/******************************************************************************
* QTMTabPageContainer
******************************************************************************/

QTMTabPageContainer::QTMTabPageContainer (QWidget* p_parent)
: QWidget (p_parent) {
setSizePolicy (QSizePolicy::Expanding, QSizePolicy::Preferred);
}

QTMTabPageContainer::~QTMTabPageContainer () { removeAllTabPages (); }

void
QTMTabPageContainer::replaceTabPages (QList<QAction*>* p_src) {
removeAllTabPages (); // remove old tabs
extractTabPages (p_src); // extract new tabs

const int windowWidth= this->width ();
int rowCount = 0;
int accumWidth = 0;
int accumTab = 0;

for (int i= 0; i < m_tabPageList.size (); ++i) {
QTMTabPage* tab= m_tabPageList[i];
tab->setParent (this);

QSize tabSize = tab->minimumSizeHint ();
int tabWidth= max (MIN_TAB_PAGE_WIDTH, tabSize.width ());
if (accumWidth + tabWidth >= windowWidth) {
rowCount+= 1;
accumWidth= 0;
accumTab = 0;
}
tab->setGeometry (accumWidth - accumTab, rowCount * m_rowHeight - rowCount,
tabWidth, m_rowHeight);
accumWidth+= tabWidth;
accumTab+= 1;
}

adjustHeight (rowCount);
}

void
QTMTabPageContainer::removeAllTabPages () {
for (int i= 0; i < m_tabPageList.size (); ++i) {
delete m_tabPageList[i];
}
m_tabPageList.clear ();
}

void
QTMTabPageContainer::extractTabPages (QList<QAction*>* p_src) {
if (!p_src) return;
for (int i= 0; i < p_src->size (); ++i) {
// see the definition of QTMTabPageAction why we're using it
QTMTabPageAction* carrier= qobject_cast<QTMTabPageAction*> ((*p_src)[i]);
ASSERT (carrier, "QTMTabPageAction expected")

QTMTabPage* tab= qobject_cast<QTMTabPage*> (carrier->m_widget);
if (tab) m_tabPageList.append (tab);

delete carrier; // we don't need it anymore
}
}

void
QTMTabPageContainer::adjustHeight (int p_rowCount) {
int h= m_rowHeight * (p_rowCount + 1);
// parentWidget's resizeEvent() will resize me
parentWidget ()->setFixedHeight (h - p_rowCount + 1);
}

/******************************************************************************
* QTMTabPageBar
******************************************************************************/

QTMTabPageBar::QTMTabPageBar (const QString& p_title, QWidget* p_parent)
: QToolBar (p_title, p_parent) {
m_container= new QTMTabPageContainer (this);
}

void
QTMTabPageBar::replaceTabPages (QList<QAction*>* p_src) {
setUpdatesEnabled (false);
bool visible= this->isVisible ();
if (visible) hide (); // TRICK: to avoid flicker of the dest widget

m_container->replaceTabPages (p_src);

if (visible) show (); // TRICK: see above
setUpdatesEnabled (true);
}

void
QTMTabPageBar::resizeEvent (QResizeEvent* e) {
QSize size= e->size ();
// Reserve 7px space on the left for the handle of QToolbar
m_container->setGeometry (7, 0, size.width (), size.height ());
}
54 changes: 50 additions & 4 deletions src/Plugins/Qt/QTMTabPage.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@
*/
class QTMTabPage : public QToolButton {
Q_OBJECT
QToolButton* closeBtn;
QToolButton* m_closeBtn;

public:
const url bufferUrl;
const url m_bufferUrl;

public:
explicit QTMTabPage (url p_url, QAction* p_title, QAction* p_closeBtn,
Expand All @@ -34,15 +34,61 @@ class QTMTabPage : public QToolButton {
virtual void resizeEvent (QResizeEvent* e) override;
};

/*! QTMTabPageAction is used as a carrier of QTMTabPage widget.
Why:
If we use the QWidgetAction, once we call setDefaultWidget,
we can't take out the defaultWidget from QWidgetAction, because
when we delete the QWidgetAction, the defaultWidget will also be
deleted. You can see this behavior in the source code of QWidgetAction.
*/
class QTMTabPageAction : public QAction {
Q_OBJECT
public:
explicit QTMTabPageAction (QWidget* p_widget) : m_widget (p_widget) {}
QWidget* const m_widget;
};

/*! QTMTabPageContainer is used to build the container for QTMTabPage.
In order to:
1. Support multi-line display for numerous tab pages;
2. Support drag-and-drop to sort tab page
*/
class QTMTabPageContainer : public QWidget {
QList<QTMTabPage*> m_tabPageList;
int m_rowHeight= 0;

public:
explicit QTMTabPageContainer (QWidget* p_parent);
~QTMTabPageContainer ();

inline void setRowHeight (int p_height) { m_rowHeight= p_height; }
void replaceTabPages (QList<QAction*>* p_src);

protected:
void removeAllTabPages ();
void extractTabPages (QList<QAction*>* p_src);
void adjustHeight (int p_rowCount);
};

/*! QTMTabPageBar is used to wrap the QTMTabPageContainer.
In order to:
1. Add this to the QMainWindow as QToolBar, just like the main icon toolbar;
2. Support dragging and docking like the main icon toolbar.
*/
class QTMTabPageBar : public QToolBar {
QTMTabPageContainer* m_container;

public:
explicit QTMTabPageBar (const QString& p_title, QWidget* p_parent)
: QToolBar (p_title, p_parent) {}
explicit QTMTabPageBar (const QString& p_title, QWidget* p_parent);

inline void setRowHeight (int p_height) {
m_container->setRowHeight (p_height);
}

void replaceTabPages (QList<QAction*>* p_src);

protected:
virtual void resizeEvent (QResizeEvent* e) override;
};

#endif // QTMTABPAGE_HPP
8 changes: 4 additions & 4 deletions src/Plugins/Qt/qt_tm_widget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -248,13 +248,13 @@ qt_tm_widget_rep::qt_tm_widget_rep (int mask, command _quit)
mainToolBar->setFixedHeight (toolbarHeight + 8 * retina_icons);
modeToolBar->setFixedHeight (toolbarHeight + 4 * retina_icons);
focusToolBar->setFixedHeight (toolbarHeight);
tabToolBar->setFixedHeight (toolbarHeight + 4 * retina_icons);
tabToolBar->setRowHeight (toolbarHeight + 4 * retina_icons);
#else
int toolbarHeight= 30;
mainToolBar->setFixedHeight (toolbarHeight + 8);
modeToolBar->setFixedHeight (toolbarHeight + 4);
focusToolBar->setFixedHeight (toolbarHeight);
tabToolBar->setFixedHeight (toolbarHeight + 4);
tabToolBar->setRowHeight (toolbarHeight + 4);
#endif
if (tm_style_sheet != "") {
double scale= retina_scale;
Expand All @@ -264,7 +264,7 @@ qt_tm_widget_rep::qt_tm_widget_rep (int mask, command _quit)
mainToolBar->setFixedHeight (h1);
modeToolBar->setFixedHeight (h2);
focusToolBar->setFixedHeight (h3);
tabToolBar->setFixedHeight (h2);
tabToolBar->setRowHeight (h2);
}

QWidget* cw= new QWidget ();
Expand Down Expand Up @@ -897,7 +897,7 @@ qt_tm_widget_rep::write (slot s, blackbox index, widget w) {
tab_bar_widget = concrete (w);
QList<QAction*>* list= tab_bar_widget->get_qactionlist ();
if (list) {
replaceButtons (tabToolBar, list);
tabToolBar->replaceTabPages (list);
update_visibility ();
}
}
Expand Down
12 changes: 7 additions & 5 deletions src/Plugins/Qt/qt_ui_element.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -525,11 +525,13 @@ qt_ui_element_rep::as_qaction () {
case tab_page_widget: {
typedef quartet<url, widget, widget, bool> T;
T x= open_box<T> (load);
QTMTabPage* tab= new QTMTabPage (x.x1, concrete (x.x2)->as_qaction (),
concrete (x.x3)->as_qaction (), x.x4);
QWidgetAction* a = new QWidgetAction (nullptr);
a->setDefaultWidget (tab);
act= a;
QTMTabPage* tab= new QTMTabPage (x.x1, concrete (x.x2)->as_qaction (),
concrete (x.x3)->as_qaction (), x.x4);
// use QTMTabPageAction to wrap the QTMTabPage(QWidget type)
// then we take out the QTMTabPage and add it to the QTMTabPageBar
// see the definition of QTMTabPageAction why we're using it
QTMTabPageAction* a= new QTMTabPageAction (tab);
act = a;
} break;

default:
Expand Down

0 comments on commit 161c60f

Please sign in to comment.