diff --git a/bin/assets/ui/breeze.css b/bin/assets/ui/breeze.css index 679ed504c..5d84bfa9a 100644 --- a/bin/assets/ui/breeze.css +++ b/bin/assets/ui/breeze.css @@ -881,7 +881,9 @@ Splitter::separator:hover { } tableview::header, -listview::header { +listview::header, +tableview::rowheader, +listview::rowheader { background-color: var(--back); } @@ -900,9 +902,24 @@ listview::header::column { text-align: left; } +tableview::rowheader::row, +treeview::rowheader::row, +listview::rowheader::row { + background-color: var(--back); + border-right-color: var(--tab-line); + border-right-width: 1dp; + border-bottom-color: var(--tab-line); + border-bottom-width: 1dp; + border-type: inside; + color: var(--font); +} + tableview::header::column:hover, treeview::header::column:hover, -listview::header::column:hover { +listview::header::column:hover, +tableview::rowheader::row:hover, +treeview::rowheader::row:hover, +listview::rowheader::row:hover { background-color: var(--tab-hover); } diff --git a/docs/articles/cssspecification.md b/docs/articles/cssspecification.md index d113a78a9..46243d1a4 100644 --- a/docs/articles/cssspecification.md +++ b/docs/articles/cssspecification.md @@ -2203,24 +2203,52 @@ Read [border](https://developer.mozilla.org/en-US/docs/Web/CSS/border) documenta --- +### border-bottom + +Read [border](https://developer.mozilla.org/en-US/docs/Web/CSS/border-bottom) documentation. +`border-style` is not implemented yet. + +--- + ### border-color Read [border-color](https://developer.mozilla.org/en-US/docs/Web/CSS/border-color) documentation. --- +### border-left + +Read [border](https://developer.mozilla.org/en-US/docs/Web/CSS/border-left) documentation. +`border-style` is not implemented yet. + +--- + ### border-radius Read [border-radius](https://developer.mozilla.org/en-US/docs/Web/CSS/border-radius) documentation. --- +### border-right + +Read [border](https://developer.mozilla.org/en-US/docs/Web/CSS/border-right) documentation. +`border-style` is not implemented yet. + +--- + ### border-width Read [border-width](https://developer.mozilla.org/en-US/docs/Web/CSS/border-width) documentation. --- +### border-top + +Read [border](https://developer.mozilla.org/en-US/docs/Web/CSS/border-top) documentation. +`border-style` is not implemented yet. + +--- + ### box-margin Shorthand for [column-margin](#column-margin) and [row-margin](#row-margin) (in that order). diff --git a/include/eepp/ui/abstract/uiabstracttableview.hpp b/include/eepp/ui/abstract/uiabstracttableview.hpp index 88818171e..3d09c7428 100644 --- a/include/eepp/ui/abstract/uiabstracttableview.hpp +++ b/include/eepp/ui/abstract/uiabstracttableview.hpp @@ -124,6 +124,14 @@ class EE_API UIAbstractTableView : public UIAbstractView { virtual void onOpenMenuModelIndex( const ModelIndex& index, const Event* triggerEvent = nullptr ); + bool isRowHeaderVisible() const; + + void setRowHeaderVisible( bool rowHeaderVisible ); + + Float getRowHeaderWidth() const; + + void setRowHeaderWidth( Float rowHeaderWidth ); + protected: friend class EE::UI::UITableHeaderColumn; @@ -140,7 +148,8 @@ class EE_API UIAbstractTableView : public UIAbstractView { mutable std::vector mRows; mutable std::vector mColumn; mutable std::vector> mWidgets; - UILinearLayout* mHeader; + UILinearLayout* mHeader{ nullptr }; + UILinearLayout* mRowHeader{ nullptr }; Float mDragBorderDistance{ 8 }; size_t mIconSize{ 12 }; size_t mSortIconSize{ 16 }; @@ -153,6 +162,7 @@ class EE_API UIAbstractTableView : public UIAbstractView { std::string mSearchText; size_t mMainColumn{ 0 }; std::unordered_map> mWidgetsClickCbId; + Float mRowHeaderWidth{ 0 }; virtual ~UIAbstractTableView(); @@ -210,6 +220,10 @@ class EE_API UIAbstractTableView : public UIAbstractView { int visibleColumn(); void resetColumnData(); + + void buildRowHeader(); + + void updateRowHeader( int realRowIndex, const ModelIndex& index, Float yOffset ); }; }}} // namespace EE::UI::Abstract diff --git a/include/eepp/ui/models/model.hpp b/include/eepp/ui/models/model.hpp index d184dd12b..1b5d6d1a1 100644 --- a/include/eepp/ui/models/model.hpp +++ b/include/eepp/ui/models/model.hpp @@ -70,6 +70,8 @@ class EE_API Model { virtual std::string columnName( const size_t& /*column*/ ) const { return {}; } + virtual std::string rowName( const size_t& /*row*/ ) const { return {}; } + virtual Variant data( const ModelIndex&, ModelRole = ModelRole::Display ) const = 0; virtual void update() { onModelUpdate(); } diff --git a/src/eepp/ui/abstract/uiabstracttableview.cpp b/src/eepp/ui/abstract/uiabstracttableview.cpp index aa9b9f038..5538da28b 100644 --- a/src/eepp/ui/abstract/uiabstracttableview.cpp +++ b/src/eepp/ui/abstract/uiabstracttableview.cpp @@ -238,7 +238,7 @@ Sizef UIAbstractTableView::getContentSize() const { if ( !getModel() ) return {}; size_t count = getModel()->columnCount(); - Sizef size; + Sizef size( mRowHeaderWidth, 0.f ); for ( size_t i = 0; i < count; i++ ) if ( !isColumnHidden( i ) ) size.x += columnData( i ).width; @@ -490,7 +490,8 @@ UITableRow* UIAbstractTableView::updateRow( const int& rowIndex, const ModelInde } rowWidget->setCurIndex( index ); rowWidget->setPixelsSize( getContentSize().getWidth(), getRowHeight() ); - rowWidget->setPixelsPosition( { -mScrollOffset.x, yOffset - mScrollOffset.y } ); + rowWidget->setPixelsPosition( + { mRowHeaderWidth + -mScrollOffset.x, yOffset - mScrollOffset.y } ); if ( isRowSelection() ) { if ( getSelection().contains( index ) ) { rowWidget->pushState( UIState::StateSelected ); @@ -502,7 +503,7 @@ UITableRow* UIAbstractTableView::updateRow( const int& rowIndex, const ModelInde } void UIAbstractTableView::onScrollChange() { - mHeader->setPixelsPosition( -mScrollOffset.x, 0 ); + mHeader->setPixelsPosition( mRowHeaderWidth + -mScrollOffset.x, 0 ); } void UIAbstractTableView::bindNavigationClick( UIWidget* widget ) { @@ -684,6 +685,64 @@ void UIAbstractTableView::onOpenMenuModelIndex( const ModelIndex& index, sendEvent( &event ); } +Float UIAbstractTableView::getRowHeaderWidth() const { + return mRowHeaderWidth; +} + +void UIAbstractTableView::setRowHeaderWidth( Float rowHeaderWidth ) { + if ( mRowHeaderWidth == rowHeaderWidth ) + return; + mRowHeaderWidth = rowHeaderWidth; + onScrollChange(); + buildRowHeader(); +} + +void UIAbstractTableView::buildRowHeader() { + if ( mRowHeaderWidth == 0 ) { + if ( mRowHeader ) + mRowHeader->setVisible( false )->setEnabled( false ); + return; + } + + if ( mRowHeader == nullptr ) { + mRowHeader = UILinearLayout::NewWithTag( mTag + "::rowheader", UIOrientation::Vertical ); + mRowHeader->setLayoutSizePolicy( SizePolicy::Fixed, SizePolicy::Fixed ); + mRowHeader->setParent( this )->setVisible( true )->setEnabled( true ); + } + + mRowHeader->setPaddingTop( mHeader->getSize().getHeight() ); + mRowHeader->setPixelsSize( { mRowHeaderWidth, getPixelsSize().getHeight() } ); + mRowHeader->setClipType( ClipType::PaddingBox ); + + int rowsCount = Math::roundUp( mSize.getHeight() / getRowHeight() ) + 1; + + if ( mRowHeader->getChildCount() < rowsCount ) { + int createCount = rowsCount - mRowHeader->getChildCount(); + for ( int i = 0; i < createCount; i++ ) { + UIWidget* row = UIPushButton::NewWithTag( mTag + "::rowheader::row" ); + row->setLayoutSizePolicy( SizePolicy::Fixed, SizePolicy::Fixed ); + row->setParent( mRowHeader ); + row->setPixelsSize( mRowHeaderWidth, getRowHeight() ); + } + } +} + +void UIAbstractTableView::updateRowHeader( int realRowIndex, const ModelIndex& index, + Float yOffset ) { + if ( !mRowHeader || mRowHeaderWidth == 0 ) + return; + Node* child = mRowHeader->getChildAt( realRowIndex ); + if ( !child ) + return; + UIPushButton* row = child->asType(); + + row->setPixelsSize( mRowHeaderWidth, getRowHeight() ); + row->setLayoutMarginTop( PixelDensity::pxToDp( yOffset ) ); + + if ( getModel() ) + row->setText( getModel()->rowName( index.row() ) ); +} + void UIAbstractTableView::onRowCreated( UITableRow* row ) { RowCreatedEvent rowEvent( this, Event::OnRowCreated, row ); sendEvent( &rowEvent ); diff --git a/src/eepp/ui/css/stylesheetspecification.cpp b/src/eepp/ui/css/stylesheetspecification.cpp index 50f80578f..e4dfefd24 100644 --- a/src/eepp/ui/css/stylesheetspecification.cpp +++ b/src/eepp/ui/css/stylesheetspecification.cpp @@ -459,6 +459,17 @@ void StyleSheetSpecification::registerDefaultProperties() { "color-vector2" ); registerShorthand( "hint-shadow", { "hint-shadow-color", "hint-shadow-offset" }, "color-vector2" ); + registerShorthand( "border-left", + { "border-left-width", "border-left-style", "border-left-color" }, + "border-side" ); + registerShorthand( "border-right", + { "border-right-width", "border-right-style", "border-right-color" }, + "border-side" ); + registerShorthand( "border-top", { "border-top-width", "border-top-style", "border-top-color" }, + "border-side" ); + registerShorthand( "border-bottom", + { "border-bottom-width", "border-bottom-style", "border-bottom-color" }, + "border-side" ); } void StyleSheetSpecification::registerNodeSelector( const std::string& name, @@ -939,6 +950,38 @@ void StyleSheetSpecification::registerDefaultShorthandParsers() { return properties; }; + mShorthandParsers["border-side"] = []( const ShorthandDefinition* shorthand, + std::string value ) -> std::vector { + value = String::trim( value ); + if ( value.empty() || "none" == value ) + return {}; + + std::vector properties; + const std::vector& propNames = shorthand->getProperties(); + std::vector tokens = String::split( value, " ", "", "(" ); + + for ( auto& tok : tokens ) { + if ( -1 != + String::valueIndex( + tok, "none;hidden;dotted;dashed;solid;double;groove;ridge;inset;outset" ) ) { + int pos = getIndexEndingWith( propNames, "-style" ); + // boder-style is not implemented yet + if ( pos != -1 ) + continue; + } else if ( Color::isColorString( tok ) || String::startsWith( tok, "var(" ) ) { + int pos = getIndexEndingWith( propNames, "-color" ); + if ( pos != -1 ) + properties.emplace_back( StyleSheetProperty( propNames[pos], tok ) ); + } else { + int pos = getIndexEndingWith( propNames, "-width" ); + if ( pos != -1 ) + properties.emplace_back( StyleSheetProperty( propNames[pos], tok ) ); + } + } + + return properties; + }; + mShorthandParsers["color-vector2"] = []( const ShorthandDefinition* shorthand, std::string value ) -> std::vector { diff --git a/src/eepp/ui/uitableview.cpp b/src/eepp/ui/uitableview.cpp index fc0f44059..f0d4732b9 100644 --- a/src/eepp/ui/uitableview.cpp +++ b/src/eepp/ui/uitableview.cpp @@ -30,6 +30,7 @@ void UITableView::drawChilds() { int realRowIndex = 0; int realColIndex = 0; ConditionalLock l( getModel() != nullptr, getModel() ? &getModel()->resourceMutex() : nullptr ); + buildRowHeader(); if ( getModel() ) { Float rowHeight = getRowHeight(); size_t start = mScrollOffset.y / rowHeight; @@ -66,11 +67,18 @@ void UITableView::drawChilds() { realColIndex++; } rowNode->nodeDraw(); + if ( mRowHeaderWidth ) { + updateRowHeader( realRowIndex, rowIndex, + realRowIndex == 0 ? std::fmodf( -mScrollOffset.y, rowHeight ) + : 0.f ); + } realRowIndex++; } } if ( mHeader && mHeader->isVisible() ) mHeader->nodeDraw(); + if ( mRowHeader && mRowHeader->isVisible() ) + mRowHeader->nodeDraw(); if ( mHScroll->isVisible() ) mHScroll->nodeDraw(); if ( mVScroll->isVisible() ) @@ -94,6 +102,8 @@ Node* UITableView::overFind( const Vector2f& point ) { return pOver; if ( mHeader && ( pOver = mHeader->overFind( point ) ) ) return pOver; + if ( mRowHeader && ( pOver = mRowHeader->overFind( point ) ) ) + return pOver; Float rowHeight = getRowHeight(); Float headerHeight = getHeaderHeight(); Float itemCount = getItemCount(); diff --git a/src/examples/7guis/cells/cells.cpp b/src/examples/7guis/cells/cells.cpp index 7fb653e6b..afed7c7cc 100644 --- a/src/examples/7guis/cells/cells.cpp +++ b/src/examples/7guis/cells/cells.cpp @@ -2,13 +2,14 @@ #include // Referece https://eugenkiss.github.io/7guis/tasks/#cells -// Row header pending EE_MAIN_FUNC int main( int, char** ) { UIApplication app( { 1024, 768, "eepp - 7GUIs - Cells" } ); UIWidget* rlay = app.getUI()->loadLayoutFromString( R"xml( @@ -19,6 +20,7 @@ EE_MAIN_FUNC int main( int, char** ) { auto model = std::make_shared(); table->setModel( model ); table->setColumnsWidth( PixelDensity::dpToPx( 80 ) ); + table->setRowHeaderWidth( PixelDensity::dpToPx( 32 ) ); table->setSelectionType( UITableView::SelectionType::Cell ); table->setEditable( true ); table->setEditTriggers( UIAbstractView::EditTrigger::DoubleClicked | diff --git a/src/examples/7guis/cells/spreadsheet.hpp b/src/examples/7guis/cells/spreadsheet.hpp index be13591cb..2c0cc7f9a 100644 --- a/src/examples/7guis/cells/spreadsheet.hpp +++ b/src/examples/7guis/cells/spreadsheet.hpp @@ -83,6 +83,10 @@ class Spreadsheet : public Model { return String::format( "%c", column + 'A' ); } + virtual std::string rowName( const size_t& row ) const { + return String::format( "%zu", row + 1 ); + } + virtual Variant data( const ModelIndex& index, ModelRole role ) const; void createCell( int col, int row );