mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-10-02 11:10:31 +03:00
Fix UIDropDown listbox mis-positioning.
Fix sort indicator in UIAbstractTableView not displaying when sorted.
This commit is contained in:
@@ -152,6 +152,10 @@ class EE_API UIAbstractTableView : public UIAbstractView {
|
|||||||
|
|
||||||
void setAutoColumnsWidth( bool autoColumnsWidth );
|
void setAutoColumnsWidth( bool autoColumnsWidth );
|
||||||
|
|
||||||
|
/** Sorts @p colIndex through the model and reflects it in the header indicator, as if the user
|
||||||
|
* had clicked that column header. */
|
||||||
|
virtual void sortByColumn( const size_t& colIndex, const SortOrder& sortOrder );
|
||||||
|
|
||||||
const size_t& getMainColumn() const;
|
const size_t& getMainColumn() const;
|
||||||
|
|
||||||
/** The main column is the column that should be prioritized to occupy as much space as
|
/** The main column is the column that should be prioritized to occupy as much space as
|
||||||
@@ -246,6 +250,9 @@ class EE_API UIAbstractTableView : public UIAbstractView {
|
|||||||
bool mUpdatingColumnsForScrollbars{ false };
|
bool mUpdatingColumnsForScrollbars{ false };
|
||||||
bool mAutoExpandedColumnUsesVerticalScroll{ false };
|
bool mAutoExpandedColumnUsesVerticalScroll{ false };
|
||||||
std::string mPendingSerializedColumnWidths;
|
std::string mPendingSerializedColumnWidths;
|
||||||
|
// Last sort state drawn in the header, so the indicator is only touched when it changes.
|
||||||
|
int mSortIndicatorColumn{ -1 };
|
||||||
|
SortOrder mSortIndicatorOrder{ SortOrder::None };
|
||||||
|
|
||||||
virtual ~UIAbstractTableView();
|
virtual ~UIAbstractTableView();
|
||||||
|
|
||||||
@@ -296,6 +303,10 @@ class EE_API UIAbstractTableView : public UIAbstractView {
|
|||||||
|
|
||||||
virtual void onSortColumn( const size_t& colIndex );
|
virtual void onSortColumn( const size_t& colIndex );
|
||||||
|
|
||||||
|
/** Draws the sort indicator on @p colIndex, clearing any indicator left on another column.
|
||||||
|
* Pass SortOrder::None to clear the indicator entirely. */
|
||||||
|
void applySortIndicator( const size_t& colIndex, const SortOrder& sortOrder );
|
||||||
|
|
||||||
virtual Uint32 onTextInput( const TextInputEvent& event );
|
virtual Uint32 onTextInput( const TextInputEvent& event );
|
||||||
|
|
||||||
virtual Uint32 onKeyDown( const KeyEvent& event );
|
virtual Uint32 onKeyDown( const KeyEvent& event );
|
||||||
|
|||||||
@@ -440,6 +440,17 @@ void UIAbstractTableView::createOrUpdateColumns( bool resetColumnData ) {
|
|||||||
mHeader->setVisible( visible );
|
mHeader->setVisible( visible );
|
||||||
|
|
||||||
updateColumnsWidth();
|
updateColumnsWidth();
|
||||||
|
|
||||||
|
// Reflect the model's sort state in the header. The indicator is otherwise only produced by
|
||||||
|
// the header-click path, which leaves a programmatic sort (and the initial sort order of a
|
||||||
|
// freshly attached model) showing no indicator at all.
|
||||||
|
if ( model->isSortable() ) {
|
||||||
|
int keyColumn = model->keyColumn();
|
||||||
|
// A model with no key column is unsorted, and its reported sort order is meaningless.
|
||||||
|
SortOrder sortOrder = keyColumn < 0 ? SortOrder::None : model->sortOrder();
|
||||||
|
if ( keyColumn != mSortIndicatorColumn || sortOrder != mSortIndicatorOrder )
|
||||||
|
applySortIndicator( keyColumn < 0 ? 0 : static_cast<size_t>( keyColumn ), sortOrder );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Float UIAbstractTableView::getHeaderHeight() const {
|
Float UIAbstractTableView::getHeaderHeight() const {
|
||||||
@@ -1184,34 +1195,67 @@ void UIAbstractTableView::onRowCreated( UITableRow* row ) {
|
|||||||
sendEvent( &rowEvent );
|
sendEvent( &rowEvent );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UIAbstractTableView::applySortIndicator( const size_t& colIndex, const SortOrder& sortOrder ) {
|
||||||
|
// Clear any indicator left on another column.
|
||||||
|
for ( size_t i = 0; i < mColumn.size(); ++i ) {
|
||||||
|
if ( i == colIndex || !mColumn[i].widget )
|
||||||
|
continue;
|
||||||
|
UIImage* other = mColumn[i].widget->getExtraInnerWidget()->asType<UIImage>();
|
||||||
|
if ( !other )
|
||||||
|
continue;
|
||||||
|
other->setForegroundFillEnabled( false );
|
||||||
|
other->setDrawable( DrawablePtr{} );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( sortOrder == SortOrder::None || colIndex >= mColumn.size() || !mColumn[colIndex].widget ) {
|
||||||
|
mSortIndicatorColumn = -1;
|
||||||
|
mSortIndicatorOrder = SortOrder::None;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UIPushButton* button = mColumn[colIndex].widget;
|
||||||
|
UIImage* image = button->getExtraInnerWidget()->asType<UIImage>();
|
||||||
|
if ( !image ) {
|
||||||
|
mSortIndicatorColumn = -1;
|
||||||
|
mSortIndicatorOrder = SortOrder::None;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mSortIndicatorColumn = static_cast<int>( colIndex );
|
||||||
|
mSortIndicatorOrder = sortOrder;
|
||||||
|
|
||||||
|
std::string tag = button->getElementTag() + "::arrow";
|
||||||
|
image->setElementTag( sortOrder == SortOrder::Ascending ? tag + "-up" : tag + "-down" );
|
||||||
|
image->setForegroundFillEnabled( true );
|
||||||
|
image->reloadStyle();
|
||||||
|
if ( image->getForeground() )
|
||||||
|
image->getForeground()->setAlpha( 255 );
|
||||||
|
if ( image->getForeground() == nullptr ) {
|
||||||
|
DrawablePtr icon = mUISceneNode->findIconDrawable(
|
||||||
|
sortOrder == SortOrder::Ascending ? "arrow-down" : "arrow-up", mSortIconSize );
|
||||||
|
if ( icon )
|
||||||
|
image->setDrawable( std::move( icon ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UIAbstractTableView::sortByColumn( const size_t& colIndex, const SortOrder& sortOrder ) {
|
||||||
|
Model* model = getModel();
|
||||||
|
if ( !model || !model->isSortable() || !model->isColumnSortable( colIndex ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Sorting notifies the views, which refresh the header and pick the indicator up from the
|
||||||
|
// model's new state.
|
||||||
|
model->sort( colIndex, sortOrder );
|
||||||
|
}
|
||||||
|
|
||||||
void UIAbstractTableView::onSortColumn( const size_t& colIndex ) {
|
void UIAbstractTableView::onSortColumn( const size_t& colIndex ) {
|
||||||
Model* model = getModel();
|
Model* model = getModel();
|
||||||
if ( !model )
|
if ( !model )
|
||||||
return;
|
return;
|
||||||
if ( model->isSortable() && model->isColumnSortable( colIndex ) ) {
|
if ( model->isSortable() && model->isColumnSortable( colIndex ) ) {
|
||||||
if ( -1 != model->keyColumn() && (Int64)colIndex != model->keyColumn() &&
|
|
||||||
columnData( model->keyColumn() ).widget ) {
|
|
||||||
UIImage* image =
|
|
||||||
columnData( model->keyColumn() ).widget->getExtraInnerWidget()->asType<UIImage>();
|
|
||||||
image->setForegroundFillEnabled( false );
|
|
||||||
image->setDrawable( DrawablePtr{} );
|
|
||||||
}
|
|
||||||
SortOrder sortOrder = model->sortOrder() == SortOrder::Ascending ? SortOrder::Descending
|
SortOrder sortOrder = model->sortOrder() == SortOrder::Ascending ? SortOrder::Descending
|
||||||
: SortOrder::Ascending;
|
: SortOrder::Ascending;
|
||||||
UIPushButton* button = columnData( colIndex ).widget;
|
applySortIndicator( colIndex, sortOrder );
|
||||||
UIImage* image = button->getExtraInnerWidget()->asType<UIImage>();
|
|
||||||
std::string tag = button->getElementTag() + "::arrow";
|
|
||||||
image->setElementTag( sortOrder == SortOrder::Ascending ? tag + "-up" : tag + "-down" );
|
|
||||||
image->setForegroundFillEnabled( true );
|
|
||||||
image->reloadStyle();
|
|
||||||
if ( image->getForeground() )
|
|
||||||
image->getForeground()->setAlpha( 255 );
|
|
||||||
if ( image && image->getForeground() == nullptr ) {
|
|
||||||
DrawablePtr icon = mUISceneNode->findIconDrawable(
|
|
||||||
sortOrder == SortOrder::Ascending ? "arrow-down" : "arrow-up", mSortIconSize );
|
|
||||||
if ( icon )
|
|
||||||
image->setDrawable( std::move( icon ) );
|
|
||||||
}
|
|
||||||
model->sort( colIndex, sortOrder );
|
model->sort( colIndex, sortOrder );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+28
-18
@@ -139,28 +139,38 @@ void UIDropDown::alignPopUp( UIWidget* widget ) {
|
|||||||
bool center = mStyleConfig.menuWidthRule == MenuWidthMode::ContentsCentered ||
|
bool center = mStyleConfig.menuWidthRule == MenuWidthMode::ContentsCentered ||
|
||||||
mStyleConfig.menuWidthRule == MenuWidthMode::ExpandIfNeededCentered;
|
mStyleConfig.menuWidthRule == MenuWidthMode::ExpandIfNeededCentered;
|
||||||
|
|
||||||
Float width = widget->getSize().getWidth();
|
// The placement is decided in screen pixels, where the field, the popup and the scene bounds
|
||||||
Float offsetX = center ? eefloor( ( getSize().getWidth() - width ) * 0.5f ) : 0;
|
// are directly comparable. Deriving it from the node's own dp position previously mixed
|
||||||
|
// coordinate spaces (the candidate point was expressed in the parent's space but converted
|
||||||
|
// through the field's own nodeToWorld, shifting the test rectangle by the field's offset), so
|
||||||
|
// the "fits below" check failed for any field away from its parent's origin and the popup was
|
||||||
|
// flipped above the field even when there was no room there.
|
||||||
|
const Rectf field( getScreenRect() );
|
||||||
|
const Sizef popUpSize( widget->getPixelsSize() );
|
||||||
|
const Rectf sceneBounds( getUISceneNode()->getWorldBounds() );
|
||||||
|
|
||||||
Vector2f pos( mDpPos.x + offsetX, mDpPos.y + getSize().getHeight() );
|
Float x = center ? field.Left + eefloor( ( field.getWidth() - popUpSize.getWidth() ) * 0.5f )
|
||||||
Vector2f posCpy( pos );
|
: field.Left;
|
||||||
nodeToWorld( posCpy );
|
|
||||||
|
|
||||||
if ( !getUISceneNode()->getWorldBounds().contains( Rectf( posCpy, widget->getSize() ) ) ) {
|
// Prefer below the field, fall back to above it, and only then clamp: the list is never placed
|
||||||
pos = Vector2f( mDpPos.x + offsetX, mDpPos.y - widget->getSize().getHeight() );
|
// partially off screen when the scene has room for it on either side.
|
||||||
|
Float y = field.Bottom;
|
||||||
|
if ( y + popUpSize.getHeight() > sceneBounds.Bottom ) {
|
||||||
|
Float above = field.Top - popUpSize.getHeight();
|
||||||
|
y = above >= sceneBounds.Top
|
||||||
|
? above
|
||||||
|
: eemax( sceneBounds.Top, sceneBounds.Bottom - popUpSize.getHeight() );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( mStyleConfig.PopUpToRoot ) {
|
// Keep the popup inside the scene horizontally as well; a list wider than its field used to
|
||||||
getParent()->nodeToWorld( pos );
|
// run off the right edge.
|
||||||
pos = PixelDensity::pxToDp( pos );
|
x = eeclamp( x, sceneBounds.Left,
|
||||||
} else {
|
eemax( sceneBounds.Left, sceneBounds.Right - popUpSize.getWidth() ) );
|
||||||
Node* parentNode = getParent();
|
|
||||||
Node* rp = getWindowContainer();
|
// World coordinates are pixels and worldToNode already converts back to dp, which is what
|
||||||
while ( rp != parentNode ) {
|
// setPosition expects; applying the density a second time would shift the popup.
|
||||||
pos += parentNode->getPosition();
|
Vector2f pos( x, y );
|
||||||
parentNode = parentNode->getParent();
|
widget->getParent()->worldToNode( pos );
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
widget->setPosition( pos );
|
widget->setPosition( pos );
|
||||||
show();
|
show();
|
||||||
|
|||||||
Reference in New Issue
Block a user