Fixes dynamic layout sizes after widget show (#241)

* handles most cases

* Works with every edge case
Works similar to QGridLayout

* performance fix

* Adds calculate to sizehint
This commit is contained in:
Atul R 2019-12-04 00:21:47 +01:00 committed by GitHub
parent 420f6db56b
commit bae4776747
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 110 additions and 85 deletions

View File

@ -23,11 +23,14 @@ class FlexLayout : public QLayout, public EventWidget {
YGNodeRef node;
YGNodeRef getRootNode(YGNodeRef node) const;
void calculateLayout() const;
void restoreNodeMinStyle(YGValue &previousMinWidth,
YGValue &previousMinHeight);
public:
FlexLayout(QWidget *parentWidget = nullptr, YGNodeRef parentNode = nullptr);
~FlexLayout() override;
QSize sizeHint() const override;
QSize minimumSize() const override;
void addItem(QLayoutItem *) override;
QLayoutItem *itemAt(int index) const override;
QLayoutItem *takeAt(int index) override;
@ -38,7 +41,6 @@ class FlexLayout : public QLayout, public EventWidget {
void removeWidget(QWidget *childWidget, YGNodeRef childNode);
void setGeometry(const QRect &rect) override;
void setFlexNode(YGNodeRef parentNode);
Qt::Orientations expandingDirections() const override;
bool hasHeightForWidth() const override;
EVENTWIDGET_IMPLEMENTATIONS(QLayout)

View File

@ -19,9 +19,10 @@ class FlexNodeContext {
namespace flexutils {
YGSize measureQtWidget(YGNodeRef node, float width, YGMeasureMode widthMode,
float height, YGMeasureMode heightMode);
const QRect getFlexNodeGeometry(YGNodeRef node);
QRect getFlexNodeGeometry(YGNodeRef node);
void setFlexNodeGeometry(YGNodeRef node, const QRect& geometry);
FlexNodeContext* getFlexNodeContext(YGNodeRef node);
bool isFlexNodeSizeControlled(YGNodeRef node);
void configureFlexNode(QWidget* widget, YGNodeRef node,
bool isLeafNode = false);

View File

@ -131,57 +131,72 @@ YGNodeRef FlexLayout::getRootNode(YGNodeRef node) const {
}
}
Qt::Orientations FlexLayout::expandingDirections() const {
return Qt::Vertical | Qt::Horizontal;
}
bool FlexLayout::hasHeightForWidth() const { return false; }
QSize FlexLayout::sizeHint() const {
calculateLayout();
QSize sizeHint = QSize(YGNodeLayoutGetWidth(this->node),
YGNodeLayoutGetHeight(this->node));
// qDebug() << "sizeHint" << this->parentWidget() << sizeHint;
return sizeHint;
return QSize(YGNodeLayoutGetWidth(this->node),
YGNodeLayoutGetHeight(this->node));
}
QSize FlexLayout::minimumSize() const {
calculateLayout();
QSize minSize = QSize(YGNodeLayoutGetWidth(this->node),
YGNodeLayoutGetHeight(this->node));
return minSize;
}
void FlexLayout::setGeometry(const QRect& rect) {
if (!this->node) {
return;
}
// qDebug() << "setGeometry" << rect << this->parentWidget();
FlexNodeContext* layoutNodeCtx = flexutils::getFlexNodeContext(this->node);
if (parentWidget()->isWindow() || layoutNodeCtx->isSizeControlled) {
// qDebug() << "controlled" << this->parentWidget();
YGNodeStyleSetWidth(this->node, rect.width());
YGNodeStyleSetHeight(this->node, rect.height());
}
calculateLayout();
QRect calculatedRect = flexutils::getFlexNodeGeometry(this->node);
QLayout::setGeometry(calculatedRect);
// qDebug() << "calculatedRect" << calculatedRect << this->parentWidget();
uint count = YGNodeGetChildCount(this->node);
for (uint i = 0; i < count; ++i) {
YGNode* childNode = YGNodeGetChild(this->node, i);
QRect childRect = flexutils::getFlexNodeGeometry(childNode);
FlexNodeContext* ctx = flexutils::getFlexNodeContext(childNode);
QLayoutItem* childItem = ctx->layoutItem();
// qDebug() << "child" << childRect << childItem->widget();
childItem->setGeometry(childRect);
if (!rect.isValid() || rect != geometry()) {
bool isSizeControlled = flexutils::isFlexNodeSizeControlled(this->node);
YGNodeMarkDirtyAndPropogateToDescendants(this->node);
YGValue prevStyleMinWidth = YGNodeStyleGetMinWidth(this->node);
YGValue prevStyleMinHeight = YGNodeStyleGetMinHeight(this->node);
if (isSizeControlled) {
YGNodeStyleSetMinHeight(this->node, rect.height());
YGNodeStyleSetMinWidth(this->node, rect.width());
}
calculateLayout();
uint count = YGNodeGetChildCount(this->node);
for (uint i = 0; i < count; ++i) {
YGNode* childNode = YGNodeGetChild(this->node, i);
QRect childRect = flexutils::getFlexNodeGeometry(childNode);
FlexNodeContext* ctx = flexutils::getFlexNodeContext(childNode);
QLayoutItem* childItem = ctx->layoutItem();
childItem->setGeometry(childRect);
}
if (isSizeControlled) {
restoreNodeMinStyle(prevStyleMinWidth, prevStyleMinHeight);
}
}
QLayout::setGeometry(rect);
}
void FlexLayout::setFlexNode(YGNodeRef parentNode) { this->node = parentNode; }
void FlexLayout::calculateLayout() const {
YGNodeRef parentNode = this->node;
YGNodeRef rootNode = getRootNode(parentNode);
YGDirection rootDirection = YGNodeStyleGetDirection(rootNode);
float rootWidth = YGNodeLayoutGetWidth(rootNode);
float rootHeight = YGNodeLayoutGetHeight(rootNode);
YGNodeCalculateLayout(rootNode, rootWidth, rootHeight, rootDirection);
if (YGNodeIsDirty(this->node)) {
YGNodeRef rootNode = getRootNode(this->node);
YGDirection rootDirection = YGNodeStyleGetDirection(rootNode);
YGNodeCalculateLayout(rootNode, YGUndefined, YGUndefined, rootDirection);
}
}
void FlexLayout::restoreNodeMinStyle(YGValue& previousMinWidth,
YGValue& previousMinHeight) {
if (previousMinHeight.unit == YGUnitPercent) {
YGNodeStyleSetMinHeightPercent(this->node, previousMinHeight.value);
} else {
YGNodeStyleSetMinHeight(this->node, previousMinHeight.value);
}
if (previousMinWidth.unit == YGUnitPercent) {
YGNodeStyleSetMinWidthPercent(this->node, previousMinWidth.value);
} else {
YGNodeStyleSetMinWidth(this->node, previousMinWidth.value);
}
}

View File

@ -17,11 +17,11 @@ void FlexNodeContext::setLayoutItem(QLayoutItem* item) {
this->_layoutItem = item;
}
const QRect flexutils::getFlexNodeGeometry(YGNodeRef node) {
int width = static_cast<uint>(YGNodeLayoutGetWidth(node));
int height = static_cast<uint>(YGNodeLayoutGetHeight(node));
int left = static_cast<uint>(YGNodeLayoutGetLeft(node));
int top = static_cast<uint>(YGNodeLayoutGetTop(node));
QRect flexutils::getFlexNodeGeometry(YGNodeRef node) {
int width = static_cast<int>(YGNodeLayoutGetWidth(node));
int height = static_cast<int>(YGNodeLayoutGetHeight(node));
int left = static_cast<int>(YGNodeLayoutGetLeft(node));
int top = static_cast<int>(YGNodeLayoutGetTop(node));
const QRect geometry(left, top, width, height);
return geometry;
}
@ -47,6 +47,19 @@ FlexNodeContext* flexutils::getFlexNodeContext(YGNodeRef node) {
return ctx;
}
// if true, it means this node's size can controlled by external things like
// resize handles in case of qmainwindow etc
bool flexutils::isFlexNodeSizeControlled(YGNodeRef node) {
if (!node) {
return false;
}
FlexNodeContext* ctx = getFlexNodeContext(node);
if (ctx->widget()->isWindow() || ctx->isSizeControlled) {
return true;
}
return false;
}
YGSize flexutils::measureQtWidget(YGNodeRef node, float _width,
YGMeasureMode widthMode, float _height,
YGMeasureMode heightMode) {

View File

@ -1,50 +1,40 @@
import { QWidget, QMainWindow, FlexLayout, QLabel } from './index';
import { QWidget, QMainWindow, FlexLayout, QGridLayout, QLabel } from './index';
import { QScrollArea } from './lib/QtWidgets/QScrollArea';
const win = new QMainWindow();
const center = new QWidget();
const label = new QLabel();
const scrollArea = new QScrollArea();
scrollArea.setObjectName('scrollArea');
scrollArea.setWidgetResizable(true);
// scrollArea.resize(500, 500);
const center = new QWidget();
center.setObjectName('center');
const label = new QLabel();
label.setObjectName('label');
label.setText(`
😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
Hellloooooo
`);
Hellloooooo123
Hellloooooo
`);
center.setInlineStyle(`border: 3px solid blue;`);
label.setInlineStyle(`border: 2px solid green;padding: 10;font-family: "Sans serif";`);
scrollArea.setWidget(label);
label.setInlineStyle(`border: 2px solid green;padding: 10;flex:1;font-family: "Sans serif";`);
// center.setLayout(new QGridLayout());
center.setLayout(new FlexLayout());
center.layout?.addWidget(scrollArea);
win.setCentralWidget(center);
win.show();
scrollArea.setInlineStyle(`flex: 1;`);
center.layout?.addWidget(label);
scrollArea.setWidget(center);
console.log('SHOW scrollArea');
scrollArea.show();
console.log('SET TEXT');
setTimeout(() => {
label.setText(`Heloo
Heloo
Jello
Hoell
jaksjd
asjdkajdk
aksjdkajsdkja
ajksjdakjsd
jkasjdkajd
ajksdjakjs`);
}, 3000);
(global as any).win = win;
(global as any).scrollArea = scrollArea;

View File

@ -25,6 +25,7 @@ export class QScrollArea extends QAbstractScrollArea {
// react:✓, //TODO:getter
this.contentWidget = widget;
this.native.setWidget(widget.native);
this.contentWidget.setFlexNodeSizeControlled(this.widgetResizable());
}
takeWidget(): NodeWidget | null {
// react:✓
@ -38,6 +39,9 @@ export class QScrollArea extends QAbstractScrollArea {
}
setWidgetResizable(resizable: boolean): void {
this.native.setWidgetResizable(resizable);
if (this.contentWidget) {
this.contentWidget.setFlexNodeSizeControlled(resizable);
}
}
widgetResizable(): boolean {
return this.native.widgetResizable();