/*
Copyright (C) 2012, University of Cambridge, Department of Psychiatry.
Created by Rudolf Cardinal (rnc1001@cam.ac.uk).
This file is part of CamCOPS.
CamCOPS is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
CamCOPS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with CamCOPS. If not, see <https://www.gnu.org/licenses/>.
*/
/*
OPTIONAL LGPL: Alternatively, this file may be used under the terms of the
GNU Lesser General Public License version 3 as published by the Free
Software Foundation. You should have received a copy of the GNU Lesser
General Public License along with CamCOPS. If not, see
<https://www.gnu.org/licenses/>.
*/
#pragma once
#define GRIDLAYOUTHFW_ALTER_FROM_QGRIDLAYOUT
// ... comment out to revert to QGridLayout behaviour
#include <QHash>
#include <QLayout>
#include <QVector>
#include "layouts/qtlayouthelpers.h"
#include "lib/margins.h"
class QQGridBox;
struct QQGridLayoutSizeTriple;
class GridLayoutHfw : public QLayout
{
// This is to QGridLayout as BoxLayoutHfw (q.v.) is to QBoxLayout.
// Main changes are:
// - the layout handling, conditional on #define
// GRIDLAYOUTHFW_ALTER_FROM_QGRIDLAYOUT
// - change from PIMPL to conventional class idiom
// - changes from:
// MyClass::constFunction() const
// {
// MyClass* that = const_cast<MyClass*>(this);
// that->nonConstFunction(); // modifies m_member
// }
// to using "mutable"
// - Margins objects
Q_OBJECT
using QLayoutStruct = qtlayouthelpers::QQLayoutStruct;
struct GeomInfo
{ // RNC
// Describes the geometry of the whole grid.
// One is created for every grid rectangle we want to try.
// QLayoutStruct (and QQLayoutStruct) are small objects containing
// measurements, used for layout calculations.
QVector<QLayoutStruct> m_row_data;
QVector<QLayoutStruct> m_col_data;
QVector<QLayoutStruct> m_hfw_data; // was a pointer
QSize m_size_hint; // grid preferred size
QSize m_min_size; // grid minimum size
QSize m_max_size; // grid maximum size
Qt::Orientations m_expanding;
// ... can it expand horizontally? vertically?
bool m_has_hfw; // grid has height-for-width property
int m_hfw_height; // preferred height of the grid based on HFW calcs
int m_hfw_min_height; // minimum height of the grid based on HFW calcs
};
public:
explicit GridLayoutHfw(QWidget* parent = nullptr);
~GridLayoutHfw() override;
QSize sizeHint() const override;
QSize minimumSize() const override;
QSize maximumSize() const override;
void setHorizontalSpacing(int spacing);
int horizontalSpacing() const;
void setVerticalSpacing(int spacing);
int verticalSpacing() const;
void setSpacing(int spacing) override;
int spacing() const override;
void setRowStretch(int row, int stretch);
// Spare horizontal space (i.e. space available in excess of the minimum
// width) is allocated to columns in proportion to their stretch factors.
void setColumnStretch(int column, int stretch);
int rowStretch(int row) const;
int columnStretch(int column) const;
void setRowMinimumHeight(int row, int min_size);
void setColumnMinimumWidth(int column, int min_size);
int rowMinimumHeight(int row) const;
int columnMinimumWidth(int column) const;
int columnCount() const;
int rowCount() const;
QRect cellRect(const GeomInfo& gi, int row, int column) const;
bool hasHeightForWidth() const override;
int heightForWidth(int) const override;
int minimumHeightForWidth(int) const override;
Qt::Orientations expandingDirections() const override;
void invalidate() override;
void addWidget(QWidget* w);
void addWidget(
QWidget* w, int row, int column, Qt::Alignment = Qt::Alignment()
);
void addWidget(
QWidget* w,
int row,
int column,
int row_span,
int column_span,
Qt::Alignment = Qt::Alignment()
);
void addLayout(
QLayout* w, int row, int column, Qt::Alignment = Qt::Alignment()
);
void addLayout(
QLayout* w,
int row,
int column,
int row_span,
int column_span,
Qt::Alignment = Qt::Alignment()
);
void setOriginCorner(Qt::Corner);
Qt::Corner originCorner() const;
QLayoutItem* itemAt(int index) const override;
QLayoutItem* itemAtPosition(int row, int column) const;
QLayoutItem* takeAt(int index) override;
int count() const override;
// Main function to lay out the grid of widgets.
void setGeometry(const QRect&) override;
void addItem(
QLayoutItem* item,
int row,
int column,
int row_span = 1,
int column_span = 1,
Qt::Alignment = Qt::Alignment()
);
void setDefaultPositioning(int n, Qt::Orientation orient);
void getItemPosition(
int idx, int* row, int* column, int* row_span, int* column_span
) const;
protected:
void addItem(QLayoutItem* item) override;
private:
// Disable copy-constructor and copy-assignment-operator:
GridLayoutHfw(GridLayoutHfw const&) = delete;
void operator=(GridLayoutHfw const& x) = delete;
// ------------------------------------------------------------------------
// From QGridLayoutPrivate:
// ------------------------------------------------------------------------
private:
void add(QQGridBox*, int row, int col);
void add(QQGridBox*, int row1, int row2, int col1, int col2);
void distribute(const QRect& layout_rect);
inline int numRows() const
{
return m_nrow;
}
inline int numCols() const
{
return m_ncol;
}
inline int rowSpacing(int r) const
{
return m_r_min_heights.at(r);
}
inline int colSpacing(int c) const
{
return m_c_min_widths.at(c);
}
inline void setReversed(bool r, bool c)
{
m_h_reversed = c;
m_v_reversed = r;
}
inline bool horReversed() const
{
return m_h_reversed;
}
inline bool verReversed() const
{
return m_v_reversed;
}
void setDirty(); // RNC: was inline and defined here
inline bool isDirty() const
{
return m_dirty;
}
inline void getNextPos(int& row, int& col)
{
row = m_next_r;
col = m_next_c;
}
QLayoutItem* replaceAt(int index, QLayoutItem* newitem);
// ... RNC: was override (of QLayoutPrivate)
void deleteAll();
void expand(int rows, int cols);
private:
// Sets the "widget auto-insert" point to be the box following the one
// specified.
void setNextPosAfter(int r, int c);
// Function removed:
// void recalcHFW(int w) const;
// Alters "gi.m_hfw_data" to update details for the single row containing
// "box" based on information from "box", where "width" is the candidate
// width for that box's widget.
void addHfwData(GeomInfo& gi, QQGridBox* box, int width) const;
// Function removed:
// void init();
// Update "gi" information for the row (if r is true) and column (if c is
// true) that contains "box".
void addData(
GeomInfo& gi,
QQGridBox* box,
const QQGridLayoutSizeTriple& sizes,
bool r,
bool c
) const;
// Sets the overall grid size.
void setSize(int rows, int cols);
// Sets chain[<rownum>].spacing across the grid.
// Used either with orientation == Qt::Horizontal for columns,
// or with orientation == Qt::Vertical for rows.
void setupSpacings(
QVector<QLayoutStruct>& chain,
QQGridBox* grid[],
int fixedSpacing,
Qt::Orientation orientation
) const;
// Function removed:
// void setupHfwLayoutData() const;
// Translates margins under MacOS (does nothing otherwise).
// void effectiveMargins(int* left, int* top,
// int* right, int* bottom) const;
Margins effectiveMargins(const Margins& contents_margins) const; // RNC
// Returns the margins of this grid (the unusable bit).
Margins effectiveMargins() const; // RNC
// Gets the active contents rect from the overall layout rect (by
// subtracting margins).
QRect getContentsRect(const QRect& layout_rect) const; // RNC
// Returns the overall size of a hypothetical grid (from a GeomInfo),
// where the size parameter says "which sort of size?" (e.g. min, max).
QSize findSize(const GeomInfo& gi, int QLayoutStruct::*size) const;
#ifdef GRIDLAYOUTHFW_ALTER_FROM_QGRIDLAYOUT
// Clear all caches.
void clearCaches() const; // RNC
// What should our parent widget's height be, for a given GeomInfo?
// Returns -1 if no change required.
// Assumes that the parent comprises this layout plus parent_margins.
int getParentTargetHeight(
QWidget* parent, const Margins& parent_margins, const GeomInfo& gi
) const;
// Gets geometry information for a given layout rectangle.
// The main calculation function.
GeomInfo getGeomInfo(const QRect& layout_rect) const; // RNC
#else
GeomInfo getGeomInfo() const; // RNC
#endif
// Create a GeomInfo for a hypothetical layout of width w, used for
// whole-grid HFW calculations.
GeomInfo getGeomInfoForHfw(int w) const; // RNC
// These describe what items we have:
int m_nrow; // was rr; number of rows
int m_ncol; // was cc; number of columns
mutable QVector<int> m_r_stretches; // stretch information for each row
mutable QVector<int> m_c_stretches; // stretch information for each column
QVector<int> m_r_min_heights; // minimum heights for each row
QVector<int> m_c_min_widths; // minimum widths for each column
QVector<QQGridBox*> m_things; // list of owned objects
// These govern where new inserted items are put:
bool m_add_vertical; // autoinsert in columns, not rows? RNC: was uint : 1
int m_next_r; // row for next "autoinserted" widget
int m_next_c; // column for next "autoinserted" widget
// Global settings
int m_horizontal_spacing; // spacing between columns
int m_vertical_spacing; // spacing between rows
bool m_h_reversed;
// ... right-to-left display; RNC: was uint : 1 -- and not sure it
// ever changes
bool m_v_reversed;
// ... bottom-to-top display; RNC: was uint : 1 -- and not sure it
// ever changes
// This is layout/geometry/HFW data:
#ifdef GRIDLAYOUTHFW_ALTER_FROM_QGRIDLAYOUT
mutable int m_width_last_size_constraints_based_on;
// ... the width we last based our size information on
mutable QRect m_rect_for_next_size_constraints;
// ... the layout_rect we will base our size information on
mutable QHash<QRect, GeomInfo> m_geom_cache;
// ... RNC; maps layout_rect to GeomInfo
#else
mutable GeomInfo m_cached_geominfo;
mutable int m_cached_hfw_width;
#endif
mutable Margins m_effective_margins;
// ... RNC; replacing leftMargin, topMargin, rightMargin, bottomMargin
mutable bool m_dirty;
// ... need to clear caches? RNC: was uint : 1 -- was needRecalc
int m_reentry_depth;
// ... RNC; reentry counter; nasty bit for resizing parent widget
public:
friend QDebug operator<<(QDebug debug, const GeomInfo& gi);
};