2023-04-18 14:14:46 +08:00
# include "qgui.h"
2023-09-19 11:49:20 +08:00
# include <QPainter>
2023-06-20 16:09:25 +08:00
# include <QResizeEvent>
2023-09-19 11:49:20 +08:00
# include <QProxyStyle>
2023-04-18 14:14:46 +08:00
2023-09-19 11:49:20 +08:00
const Qt : : Alignment AlignRight = Qt : : AlignRight | Qt : : AlignVCenter ;
2023-04-18 14:14:46 +08:00
2023-09-19 11:49:20 +08:00
ColItem TreeWidget : : addCol ( const QString & field , const QString & text , int width , QHeaderView : : ResizeMode resizeMode ) {
2023-10-23 11:44:22 +08:00
int i = ( int ) fdmap . size ( ) ;
2023-09-19 11:49:20 +08:00
auto item = headerItem ( ) ;
item - > setText ( i , text ) ;
item - > setData ( i , FieldRole , field ) ;
if ( width > 0 ) {
blocked = true ;
if ( header ( ) - > minimumSectionSize ( ) > width ) header ( ) - > setMinimumSectionSize ( width ) ;
header ( ) - > resizeSection ( i , width ) ;
blocked = false ;
}
if ( resizeMode ! = QHeaderView : : Interactive ) {
if ( resizeMode = = QHeaderView : : Stretch & & width > 0 ) {
item - > setData ( i , WidthRole , width ) ;
if ( noStretch ) {
2023-06-20 16:09:25 +08:00
noStretch = false ;
2023-09-19 11:49:20 +08:00
connect ( header ( ) , & QHeaderView : : sectionResized , this , & TreeWidget : : onSectionResized ) ;
header ( ) - > installEventFilter ( this ) ;
}
} else header ( ) - > setSectionResizeMode ( i , resizeMode ) ;
2023-04-18 14:14:46 +08:00
}
2023-09-19 11:49:20 +08:00
fdmap . emplace ( field , i ) ;
if ( i = = 0 ) hasRowNum = field = = " # " ;
return { item , i } ;
2023-04-18 14:14:46 +08:00
}
2023-09-19 11:49:20 +08:00
bool TreeWidget : : eventFilter ( QObject * watched , QEvent * event ) {
if ( watched = = header ( ) ) {
if ( event - > type ( ) = = QEvent : : Resize ) {
auto eve = ( QResizeEvent * ) event ;
if ( eve - > size ( ) . width ( ) ! = eve - > oldSize ( ) . width ( ) ) adjSections ( - 1 , 0 ) ;
2024-01-28 20:28:02 +08:00
return true ;
2023-09-19 11:49:20 +08:00
} else if ( isSectionResized & & event - > type ( ) = = QEvent : : Leave ) {
isSectionResized = false ;
auto item = headerItem ( ) ;
for ( int cc = 0 ; cc < columnCount ( ) ; cc + + ) if ( item - > data ( cc , WidthRole ) . isValid ( ) ) item - > setData ( cc , WidthRole , header ( ) - > sectionSize ( cc ) ) ;
2024-01-28 20:28:02 +08:00
return true ;
2023-09-19 11:49:20 +08:00
}
}
return QTreeWidget : : eventFilter ( watched , event ) ;
}
void TreeWidget : : rowsInserted ( const QModelIndex & parent , int start , int end ) {
QTreeWidget : : rowsInserted ( parent , start , end ) ;
if ( parent . internalId ( ) ) return ;
if ( minRowHeight ) for ( int rr = start ; rr < = end ; rr + + ) {
auto item = topLevelItem ( rr ) ;
if ( item - > sizeHint ( 0 ) . height ( ) < minRowHeight ) item - > setSizeHint ( 0 , { 0 , minRowHeight } ) ;
}
int align ;
for ( int cc = 0 ; cc < columnCount ( ) ; cc + + ) if ( ( align = headerItem ( ) - > data ( cc , AlignRole ) . toInt ( ) ) ) for ( int rr = start ; rr < = end ; rr + + ) topLevelItem ( rr ) - > setTextAlignment ( cc , ( Qt : : Alignment ) align ) ;
}
void TreeWidget : : drawRow ( QPainter * painter , const QStyleOptionViewItem & options , const QModelIndex & index ) const {
QTreeWidget : : drawRow ( painter , options , index ) ;
if ( hasRowNum ) {
QRect rect ( columnViewportPosition ( 0 ) , options . rect . top ( ) , columnWidth ( 0 ) , options . rect . height ( ) ) ;
painter - > fillRect ( rect , header ( ) - > palette ( ) . button ( ) ) ;
painter - > drawText ( rect , Qt : : AlignCenter , QString : : number ( index . row ( ) + 1 ) ) ;
}
if ( hasGrid ) {
QBrush color ( { 128 , 128 , 128 , 128 } ) ;
QRect rec ( options . rect . left ( ) - 1 - horizontalOffset ( ) , options . rect . top ( ) , 1 , options . rect . height ( ) ) ;
auto last = columnCount ( ) - 1 ;
if ( hasGrid > 1 ) for ( int i = 0 ; i < last ; i + + ) {
rec . translate ( columnWidth ( header ( ) - > logicalIndex ( i ) ) , 0 ) ;
painter - > fillRect ( rec , color ) ;
} else {
int ttlwidth = columnWidth ( header ( ) - > logicalIndex ( last ) ) ;
for ( int i = 0 ; i < last ; i + + ) {
auto width = columnWidth ( header ( ) - > logicalIndex ( i ) ) ;
rec . translate ( width , 0 ) ;
painter - > fillRect ( rec , color ) ;
ttlwidth + = width ;
}
painter - > fillRect ( options . rect . left ( ) , options . rect . bottom ( ) , ttlwidth , 1 , color ) ;
}
}
}
void TreeWidget : : onSectionResized ( int logicalIndex , int oldSize , int newSize ) {
if ( blocked | | newSize = = 0 | | oldSize = = 0 ) return ;
if ( ! headerItem ( ) - > data ( logicalIndex , WidthRole ) . isValid ( ) ) return ;
if ( adjSections ( logicalIndex , newSize ) ) isSectionResized = true ;
}
bool TreeWidget : : adjSections ( int index , int size ) {
auto item = headerItem ( ) ;
int remain = header ( ) - > width ( ) - size , stretchWidth = 0 , width ;
for ( int cc = 0 ; cc < columnCount ( ) ; cc + + ) if ( cc ! = index & & ! header ( ) - > isSectionHidden ( cc ) ) {
width = item - > data ( cc , WidthRole ) | - 1 ;
if ( width > - 1 ) {
if ( width = = 0 ) item - > setData ( cc , WidthRole , width = header ( ) - > sectionSize ( cc ) ) ;
stretchWidth + = width ;
} else remain - = header ( ) - > sectionSize ( cc ) ;
}
if ( remain < = 0 | | stretchWidth = = 0 ) return false ;
auto min = header ( ) - > minimumSectionSize ( ) ;
blocked = true ;
for ( int cc = 0 ; cc < columnCount ( ) ; cc + + ) if ( cc ! = index & & ! header ( ) - > isSectionHidden ( cc ) & & ( width = item - > data ( cc , WidthRole ) | - 1 ) > - 1 ) header ( ) - > resizeSection ( cc , qMax ( min , width * remain / stretchWidth ) ) ;
blocked = false ;
return true ;
2023-06-20 16:09:25 +08:00
}
2023-09-19 11:49:20 +08:00
class TreeItemMarginStyle : public QProxyStyle {
public :
2023-10-23 11:44:22 +08:00
TreeItemMarginStyle ( TreeWidget * wgt ) : QProxyStyle ( wgt - > style ( ) ) , _wgt ( wgt ) { }
# if(QT_VERSION_MAJOR > 5)
void drawPrimitive ( PrimitiveElement element , const QStyleOption * option , QPainter * painter , const QWidget * widget = nullptr ) const override { //draw big indicator and cell focus mask
if ( option & & option - > type = = QStyleOption : : SO_ViewItem & & ( ( QStyleOptionViewItem * ) option ) - > features & QStyleOptionViewItem : : HasCheckIndicator & & widget = = _wgt ) { //PE_PanelItemViewItem PE_IndicatorItemViewItemCheck
auto opt = ( QStyleOptionViewItem * ) option ;
auto add = _wgt - > headerItem ( ) - > data ( opt - > index . column ( ) , MarginRole ) . toInt ( ) ;
if ( add ) {
if ( element = = PE_IndicatorItemViewItemCheck ) opt - > rect . translate ( add , 0 ) ; //move big indicator
else if ( ( opt - > features & QStyleOptionViewItem : : HasDisplay ) = = 0 ) opt - > rect . setLeft ( opt - > rect . right ( ) + 1 ) ; //remove cell focus mask
else {
opt - > rect . setLeft ( opt - > rect . x ( ) + ( add < < 1 ) ) ; //move cell focus mask
if ( opt - > rect . width ( ) < 0 ) opt - > rect . setWidth ( 0 ) ;
}
}
}
QProxyStyle : : drawPrimitive ( element , option , painter , widget ) ;
}
void drawControl ( ControlElement element , const QStyleOption * option , QPainter * painter , const QWidget * widget = nullptr ) const override { //draw text, small indicator and focus mask around text
if ( option & & widget = = _wgt ) {
auto opt = ( QStyleOptionViewItem * ) option ;
opt - > state & = ~ State_HasFocus ; //remove focus mask around text
if ( element = = CE_ItemViewItem & & option - > type = = QStyleOption : : SO_ViewItem ) {
auto add = _wgt - > headerItem ( ) - > data ( opt - > index . column ( ) , MarginRole ) . toInt ( ) ;
if ( add ) { //move text and small indicator
if ( opt - > displayAlignment & Qt : : AlignRight ) opt - > rect . setRight ( opt - > rect . right ( ) - add ) ;
if ( opt - > features & QStyleOptionViewItem : : HasCheckIndicator ) opt - > rect . setLeft ( opt - > rect . x ( ) + ( add < < 1 ) ) ;
else if ( ( opt - > displayAlignment & ( Qt : : AlignHCenter | Qt : : AlignRight ) ) = = 0 ) opt - > rect . setLeft ( opt - > rect . x ( ) + add ) ;
if ( opt - > rect . width ( ) < 0 ) opt - > rect . setWidth ( 0 ) ;
}
}
}
QProxyStyle : : drawControl ( element , option , painter , widget ) ;
}
# else
2023-09-19 11:49:20 +08:00
QRect subElementRect ( SubElement element , const QStyleOption * option , const QWidget * widget ) const override {
auto res = QProxyStyle : : subElementRect ( element , option , widget ) ;
auto width = res . width ( ) ;
if ( width & & option & & option - > type = = QStyleOption : : SO_ViewItem & & widget = = _wgt ) {
if ( element = = SE_ItemViewItemText ) {
auto index = ( ( QStyleOptionViewItem * ) option ) - > index ;
auto add = ( ( TreeWidget * ) widget ) - > headerItem ( ) - > data ( index . column ( ) , MarginRole ) . toInt ( ) ;
if ( add ) {
auto align = ( ( TreeWidget * ) widget ) - > item ( index . row ( ) ) - > textAlignment ( index . column ( ) ) ;
if ( align & Qt : : AlignRight ) res . setWidth ( width < add ? 0 : width - add ) ;
else if ( ( align & Qt : : AlignHCenter ) = = 0 ) {
if ( ( ( QStyleOptionViewItem * ) option ) - > features & QStyleOptionViewItem : : HasCheckIndicator ) add + = add > > 1 ;
res . setLeft ( res . x ( ) + add ) ;
if ( width < add ) res . setWidth ( 0 ) ;
}
}
} else if ( ( ( QStyleOptionViewItem * ) option ) - > features & QStyleOptionViewItem : : HasCheckIndicator ) {
2023-10-23 11:44:22 +08:00
auto index = ( ( QStyleOptionViewItem * ) option ) - > index ;
auto add = ( ( TreeWidget * ) widget ) - > headerItem ( ) - > data ( index . column ( ) , MarginRole ) . toInt ( ) ;
2023-09-19 11:49:20 +08:00
if ( add ) {
2023-10-23 11:44:22 +08:00
if ( width < ( add + 3 ) < < 1 ) add = width ;
else add + = add > > 1 ;
2023-09-19 11:49:20 +08:00
res . setLeft ( res . x ( ) + add ) ;
if ( width < add ) res . setWidth ( 0 ) ;
}
}
}
return res ;
}
int pixelMetric ( QStyle : : PixelMetric metric , const QStyleOption * option , const QWidget * widget ) const override {
auto res = QProxyStyle : : pixelMetric ( metric , option , widget ) ;
if ( metric = = PM_FocusFrameHMargin & & option & & option - > type = = QStyleOption : : SO_ViewItem & & ( ( QStyleOptionViewItem * ) option ) - > features & QStyleOptionViewItem : : HasCheckIndicator & & widget = = _wgt ) res + = ( ( TreeWidget * ) widget ) - > headerItem ( ) - > data ( ( ( QStyleOptionViewItem * ) option ) - > index . column ( ) , MarginRole ) . toInt ( ) ;
return res ;
}
2023-10-23 11:44:22 +08:00
# endif
TreeWidget * _wgt = 0 ;
2023-09-19 11:49:20 +08:00
} ;
ColItem & ColItem : : margin ( int margin ) {
item - > setData ( i , MarginRole , margin ) ;
auto tree = ( TreeWidget * ) item - > treeWidget ( ) ;
if ( tree - > noMargin ) {
tree - > noMargin = false ;
tree - > setStyle ( new TreeItemMarginStyle ( tree ) ) ;
2023-08-07 09:04:53 +08:00
}
2023-09-19 11:49:20 +08:00
return * this ;
2023-04-18 14:14:46 +08:00
}
2023-08-07 09:04:53 +08:00
2023-09-19 11:49:20 +08:00
TableWidget : : TableWidget ( std : : initializer_list < ColAttr > colAttrs , int rows , QWidget * parent ) : QTableWidget { rows , ( int ) colAttrs . size ( ) , parent } {
int i = 0 ;
for ( auto attr = colAttrs . begin ( ) ; attr < colAttrs . end ( ) ; + + attr ) {
auto item = horizontalHeaderItem ( i ) ;
if ( item = = 0 ) setHorizontalHeaderItem ( i , item = new QTableWidgetItem ) ;
item - > setText ( attr - > text ) ;
item - > setData ( FieldRole , attr - > field ) ;
if ( attr - > width > 0 ) horizontalHeader ( ) - > resizeSection ( i , attr - > width ) ;
if ( attr - > resizeMode ! = QHeaderView : : Interactive ) {
if ( attr - > resizeMode = = QHeaderView : : Stretch & & attr - > width > 0 ) {
item - > setData ( WidthRole , attr - > width ) ;
if ( noStretch ) {
noStretch = false ;
connect ( horizontalHeader ( ) , & QHeaderView : : sectionResized , this , & TableWidget : : onSectionResized ) ;
horizontalHeader ( ) - > installEventFilter ( this ) ;
}
} else horizontalHeader ( ) - > setSectionResizeMode ( i , attr - > resizeMode ) ;
}
fdmap . emplace ( attr - > field , i + + ) ;
}
}
bool TableWidget : : eventFilter ( QObject * watched , QEvent * event ) {
if ( watched = = horizontalHeader ( ) ) {
if ( event - > type ( ) = = QEvent : : Resize ) {
auto eve = ( QResizeEvent * ) event ;
if ( eve - > size ( ) . width ( ) ! = eve - > oldSize ( ) . width ( ) ) adjSections ( - 1 , 0 ) ;
2024-01-28 20:28:02 +08:00
return true ;
2023-09-19 11:49:20 +08:00
} else if ( isSectionResized & & event - > type ( ) = = QEvent : : Leave ) {
isSectionResized = false ;
QTableWidgetItem * item ;
for ( int cc = 0 ; cc < columnCount ( ) ; cc + + ) if ( ( item = horizontalHeaderItem ( cc ) ) & & item - > data ( WidthRole ) . isValid ( ) ) item - > setData ( WidthRole , horizontalHeader ( ) - > sectionSize ( cc ) ) ;
2024-01-28 20:28:02 +08:00
return true ;
2023-09-19 11:49:20 +08:00
}
}
return QTableWidget : : eventFilter ( watched , event ) ;
}
void TableWidget : : onSectionResized ( int logicalIndex , int oldSize , int newSize ) {
if ( blocked | | newSize = = 0 | | oldSize = = 0 ) return ;
if ( ! horizontalHeaderItem ( logicalIndex ) - > data ( WidthRole ) . isValid ( ) ) return ;
if ( adjSections ( logicalIndex , newSize ) ) isSectionResized = true ;
}
bool TableWidget : : adjSections ( int index , int size ) {
QTableWidgetItem * item ;
int remain = horizontalHeader ( ) - > width ( ) - size , stretchWidth = 0 , width ;
for ( int cc = 0 ; cc < columnCount ( ) ; cc + + ) if ( cc ! = index & & ! horizontalHeader ( ) - > isSectionHidden ( cc ) & & ( item = horizontalHeaderItem ( cc ) ) ) {
width = item - > data ( WidthRole ) | - 1 ;
if ( width > - 1 ) {
if ( width = = 0 ) item - > setData ( WidthRole , width = horizontalHeader ( ) - > sectionSize ( cc ) ) ;
stretchWidth + = width ;
} else remain - = horizontalHeader ( ) - > sectionSize ( cc ) ;
}
if ( remain < = 0 | | stretchWidth = = 0 ) return false ;
auto min = horizontalHeader ( ) - > minimumSectionSize ( ) ;
blocked = true ;
for ( int cc = 0 ; cc < columnCount ( ) ; cc + + ) if ( cc ! = index & & ! horizontalHeader ( ) - > isSectionHidden ( cc ) & & ( item = horizontalHeaderItem ( cc ) ) & & ( width = item - > data ( WidthRole ) | - 1 ) > - 1 ) horizontalHeader ( ) - > resizeSection ( cc , qMax ( min , width * remain / stretchWidth ) ) ;
blocked = false ;
return true ;
2023-08-07 09:04:53 +08:00
}