Added menu-width-mode for UIDropDownList.

This commit is contained in:
Martín Lucas Golini
2025-12-14 18:41:41 -03:00
parent 5e98e1e706
commit 661c453d94
10 changed files with 167 additions and 48 deletions

View File

@@ -1279,6 +1279,22 @@ Sets the maximum visible items for the list shown by a drop down list or combo b
---
### menu-width-mode
Sets how is the dropdown-menu width calculated.
* Applicable to: EE::UI::UIDropDownList (DropDownList), EE::UI::UIComboBox (ComboBox)
* Data Type: [string-list](#string-list-data-type)
* Value List:
* `dropdown`: Same width as the dropdown button.
* `contents`: The length of its contents.
* `contents-center`: The length of its contents but centered against the dropdown button.
* `expand-if-needed`: Same width as the dropdown button unless its contents are larger than the dropdown button, in that case it will expand to its contents.
* `expand-if-needed-center`: Same as `expand-if-needed` but centered against the dropdown button.
* Default value: `dropdown`
---
### min-height
Read [min-height](https://developer.mozilla.org/en-US/docs/Web/CSS/min-height) documentation.

View File

@@ -225,6 +225,7 @@ enum class PropertyId : Uint32 {
LineWrapMode = String::hash( "line-wrap-mode" ),
LineWrapType = String::hash( "line-wrap-type" ),
DisplayOptions = String::hash( "display-options" ),
MenuWidthMode = String::hash( "menu-width-mode" ),
};
enum class PropertyType : Uint32 {

View File

@@ -8,10 +8,23 @@ namespace EE { namespace UI {
class EE_API UIDropDownList : public UITextInput {
public:
enum class MenuWidthMode {
DropDown,
Contents,
ContentsCentered,
ExpandIfNeeded,
ExpandIfNeededCentered
};
static MenuWidthMode menuWidthModeFromString( std::string_view str );
static std::string menuWidthModeToString( MenuWidthMode rule );
class StyleConfig {
public:
Uint32 MaxNumVisibleItems = 10;
bool PopUpToRoot = false;
MenuWidthMode menuWidthRule{ MenuWidthMode::DropDown };
};
static UIDropDownList* NewWithTag( const std::string& tag );
@@ -30,19 +43,19 @@ class EE_API UIDropDownList : public UITextInput {
UIListBox* getListBox() const;
void showList();
UIDropDownList* showList();
bool getPopUpToRoot() const;
void setPopUpToRoot( bool popUpToRoot );
UIDropDownList* setPopUpToRoot( bool popUpToRoot );
Uint32 getMaxNumVisibleItems() const;
void setMaxNumVisibleItems( const Uint32& maxNumVisibleItems );
UIDropDownList* setMaxNumVisibleItems( const Uint32& maxNumVisibleItems );
const StyleConfig& getStyleConfig() const;
void setStyleConfig( const StyleConfig& styleConfig );
UIDropDownList* setStyleConfig( const StyleConfig& styleConfig );
virtual bool applyProperty( const StyleSheetProperty& attribute );
@@ -53,6 +66,10 @@ class EE_API UIDropDownList : public UITextInput {
virtual void loadFromXmlNode( const pugi::xml_node& node );
UIDropDownList* setMenuWidthMode( MenuWidthMode rule );
MenuWidthMode getMenuWidthMode() const;
protected:
friend class UIComboBox;

View File

@@ -116,6 +116,8 @@ class EE_API UIListBox : public UITouchDraggableWidget {
void setItemText( const Uint32& index, const String& newText );
UIListBoxItem* getReferenceItem() const { return mDummyItem; }
protected:
friend class UIListBoxItem;
friend class UIItemContainer<UIListBox>;

View File

@@ -421,6 +421,7 @@ void StyleSheetSpecification::registerDefaultProperties() {
registerProperty( "line-wrap-type", "viewport" ).setType( PropertyType::String );
registerProperty( "display-options", "" ).setType( PropertyType::String );
registerProperty( "menu-width-mode", "" ).setType( PropertyType::String );
// Shorthands
registerShorthand( "margin", { "margin-top", "margin-right", "margin-bottom", "margin-left" },

View File

@@ -179,6 +179,7 @@ bool UIComboBox::applyProperty( const StyleSheetProperty& attribute ) {
case PropertyId::RowHeight:
case PropertyId::VScrollMode:
case PropertyId::HScrollMode:
case PropertyId::MenuWidthMode:
return mDropDownList->applyProperty( attribute );
default:
return UIWidget::applyProperty( attribute );
@@ -229,6 +230,7 @@ std::string UIComboBox::getPropertyString( const PropertyDefinition* propertyDef
case PropertyId::RowHeight:
case PropertyId::VScrollMode:
case PropertyId::HScrollMode:
case PropertyId::MenuWidthMode:
return mDropDownList->getPropertyString( propertyDef, propertyIndex );
default:
return UIWidget::getPropertyString( propertyDef, propertyIndex );
@@ -274,6 +276,7 @@ std::vector<PropertyId> UIComboBox::getPropertiesImplemented() const {
PropertyId::RowHeight,
PropertyId::VScrollMode,
PropertyId::HScrollMode,
PropertyId::MenuWidthMode,
};
props.insert( props.end(), local.begin(), local.end() );
return props;

View File

@@ -10,6 +10,35 @@
namespace EE { namespace UI {
UIDropDownList::MenuWidthMode
UIDropDownList::menuWidthModeFromString( std::string_view str ) {
if ( "contents" == str || "fit-to-contents" == str )
return MenuWidthMode::Contents;
if ( "contents-centered" == str || "fit-to-contents-centered" == str )
return MenuWidthMode::ContentsCentered;
if ( "expand-if-needed" == str || "fit-to-drop-down-expand-if-needed" == str )
return MenuWidthMode::ExpandIfNeeded;
if ( "expand-if-needed-centered" == str || "fit-to-drop-down-expand-if-needed-centered" == str )
return MenuWidthMode::ExpandIfNeededCentered;
return MenuWidthMode::DropDown; // "dropdown"
}
std::string UIDropDownList::menuWidthModeToString( MenuWidthMode rule ) {
switch ( rule ) {
case MenuWidthMode::DropDown:
return "dropdown";
case MenuWidthMode::Contents:
return "contents";
case MenuWidthMode::ContentsCentered:
return "contents-centered";
case MenuWidthMode::ExpandIfNeeded:
return "expand-if-needed";
case MenuWidthMode::ExpandIfNeededCentered:
return "expand-if-needed-centered";
}
return "dropdown";
}
UIDropDownList* UIDropDownList::NewWithTag( const std::string& tag ) {
return eeNew( UIDropDownList, ( tag ) );
}
@@ -138,9 +167,9 @@ Uint32 UIDropDownList::onMouseClick( const Vector2i& Pos, const Uint32& Flags )
return 1;
}
void UIDropDownList::showList() {
UIDropDownList* UIDropDownList::showList() {
if ( NULL == mListBox )
return;
return this;
if ( !mListBox->isVisible() ) {
if ( mListBox->getCount() ) {
@@ -151,6 +180,36 @@ void UIDropDownList::showList() {
Float width =
NULL != mFriendNode ? mFriendNode->getSize().getWidth() : getSize().getWidth();
bool center =
mStyleConfig.menuWidthRule == MenuWidthMode::ContentsCentered ||
mStyleConfig.menuWidthRule ==
MenuWidthMode::ExpandIfNeededCentered;
Float contentsWidth = 0;
if ( mStyleConfig.menuWidthRule == MenuWidthMode::Contents ||
mStyleConfig.menuWidthRule == MenuWidthMode::ContentsCentered ||
mStyleConfig.menuWidthRule == MenuWidthMode::ExpandIfNeeded ||
mStyleConfig.menuWidthRule ==
MenuWidthMode::ExpandIfNeededCentered ) {
contentsWidth = eeceil( PixelDensity::pxToDp(
mListBox->getMaxTextWidth() +
PixelDensity::dpToPx( mListBox->getContainerPadding().getWidth() ) +
mListBox->getReferenceItem()->getPixelsPadding().getWidth() +
mListBox->getVerticalScrollBar()->getPixelsSize().getWidth() ) );
}
if ( mStyleConfig.menuWidthRule == MenuWidthMode::Contents ||
mStyleConfig.menuWidthRule == MenuWidthMode::ContentsCentered ) {
width = contentsWidth;
}
if ( ( mStyleConfig.menuWidthRule == MenuWidthMode::ExpandIfNeeded ||
mStyleConfig.menuWidthRule ==
MenuWidthMode::ExpandIfNeededCentered ) &&
contentsWidth > width ) {
width = contentsWidth;
}
mListBox->setSize(
width, (Int32)( eemin( mListBox->getCount(), mStyleConfig.MaxNumVisibleItems ) *
mListBox->getRowHeight() ) +
@@ -170,13 +229,15 @@ void UIDropDownList::showList() {
mListBox->toFront();
Vector2f pos( mDpPos.x, mDpPos.y + getSize().getHeight() );
Float offsetX = center ? eefloor( ( getSize().getWidth() - width ) * 0.5f ) : 0;
Vector2f pos( mDpPos.x + offsetX, mDpPos.y + getSize().getHeight() );
Vector2f posCpy( pos );
nodeToWorld( posCpy );
if ( !getUISceneNode()->getWorldBounds().contains(
Rectf( posCpy, mListBox->getSize() ) ) ) {
pos = Vector2f( mDpPos.x, mDpPos.y - mListBox->getSize().getHeight() );
pos = Vector2f( mDpPos.x + offsetX, mDpPos.y - mListBox->getSize().getHeight() );
}
if ( mStyleConfig.PopUpToRoot ) {
@@ -200,21 +261,23 @@ void UIDropDownList::showList() {
} else {
hide();
}
return this;
}
bool UIDropDownList::getPopUpToRoot() const {
return mStyleConfig.PopUpToRoot;
}
void UIDropDownList::setPopUpToRoot( bool popUpToRoot ) {
UIDropDownList* UIDropDownList::setPopUpToRoot( bool popUpToRoot ) {
mStyleConfig.PopUpToRoot = popUpToRoot;
return this;
}
Uint32 UIDropDownList::getMaxNumVisibleItems() const {
return mStyleConfig.MaxNumVisibleItems;
}
void UIDropDownList::setMaxNumVisibleItems( const Uint32& maxNumVisibleItems ) {
UIDropDownList* UIDropDownList::setMaxNumVisibleItems( const Uint32& maxNumVisibleItems ) {
if ( maxNumVisibleItems != mStyleConfig.MaxNumVisibleItems ) {
mStyleConfig.MaxNumVisibleItems = maxNumVisibleItems;
@@ -223,17 +286,20 @@ void UIDropDownList::setMaxNumVisibleItems( const Uint32& maxNumVisibleItems ) {
getListBox()->getCount() ) *
mListBox->getRowHeight() );
}
return this;
}
const UIDropDownList::StyleConfig& UIDropDownList::getStyleConfig() const {
return mStyleConfig;
}
void UIDropDownList::setStyleConfig( const StyleConfig& styleConfig ) {
UIDropDownList* UIDropDownList::setStyleConfig( const StyleConfig& styleConfig ) {
mStyleConfig = styleConfig;
setMaxNumVisibleItems( mStyleConfig.MaxNumVisibleItems );
setPopUpToRoot( mStyleConfig.PopUpToRoot );
setMenuWidthMode( mStyleConfig.menuWidthRule );
return this;
}
void UIDropDownList::onWidgetClear( const Event* ) {
@@ -352,6 +418,9 @@ bool UIDropDownList::applyProperty( const StyleSheetProperty& attribute ) {
case PropertyId::MaxVisibleItems:
setMaxNumVisibleItems( attribute.asUint() );
break;
case PropertyId::MenuWidthMode:
setMenuWidthMode( menuWidthModeFromString( attribute.getValue() ) );
break;
case PropertyId::SelectedIndex:
case PropertyId::SelectedText:
case PropertyId::ScrollBarStyle:
@@ -379,6 +448,8 @@ std::string UIDropDownList::getPropertyString( const PropertyDefinition* propert
return mStyleConfig.PopUpToRoot ? "true" : "false";
case PropertyId::MaxVisibleItems:
return String::toString( mStyleConfig.MaxNumVisibleItems );
case PropertyId::MenuWidthMode:
return menuWidthModeToString( mStyleConfig.menuWidthRule );
case PropertyId::SelectedIndex:
case PropertyId::SelectedText:
case PropertyId::ScrollBarStyle:
@@ -398,7 +469,7 @@ std::vector<PropertyId> UIDropDownList::getPropertiesImplemented() const {
auto props = UITextInput::getPropertiesImplemented();
auto local = { PropertyId::PopUpToRoot, PropertyId::MaxVisibleItems, PropertyId::SelectedIndex,
PropertyId::SelectedText, PropertyId::ScrollBarStyle, PropertyId::RowHeight,
PropertyId::VScrollMode, PropertyId::HScrollMode };
PropertyId::VScrollMode, PropertyId::HScrollMode, PropertyId::MenuWidthMode };
props.insert( props.end(), local.begin(), local.end() );
return props;
}
@@ -419,4 +490,13 @@ void UIDropDownList::onClassChange() {
mListBox->setClasses( getClasses() );
}
UIDropDownList* UIDropDownList::setMenuWidthMode( MenuWidthMode rule ) {
mStyleConfig.menuWidthRule = rule;
return this;
}
UIDropDownList::MenuWidthMode UIDropDownList::getMenuWidthMode() const {
return mStyleConfig.menuWidthRule;
}
}} // namespace EE::UI

View File

@@ -132,22 +132,23 @@ void UIListBox::addListBoxItems( std::vector<String> texts ) {
mItems.push_back( NULL );
}
findMaxWidth();
updatePageStep();
updateScroll();
}
Uint32 UIListBox::addListBoxItem( UIListBoxItem* Item ) {
mItems.push_back( Item );
mTexts.push_back( Item->getText() );
Uint32 UIListBox::addListBoxItem( UIListBoxItem* item ) {
mItems.push_back( item );
mTexts.push_back( item->getText() );
if ( Item->getParent() != mContainer )
Item->setParent( mContainer );
if ( item->getParent() != mContainer )
item->setParent( mContainer );
updateScroll();
Uint32 tMaxTextWidth = mMaxTextWidth;
itemUpdateSize( Item );
itemUpdateSize( item );
if ( tMaxTextWidth != mMaxTextWidth ) {
updateListBoxItemsSize();
@@ -249,11 +250,11 @@ Uint32 UIListBox::getListBoxItemIndex( const String& Name ) {
return eeINDEX_NOT_FOUND;
}
Uint32 UIListBox::getListBoxItemIndex( UIListBoxItem* Item ) {
Uint32 UIListBox::getListBoxItemIndex( UIListBoxItem* item ) {
Uint32 size = (Uint32)mItems.size();
for ( Uint32 i = 0; i < size; i++ ) {
if ( Item == mItems[i] )
if ( item == mItems[i] )
return i;
}
@@ -357,9 +358,7 @@ void UIListBox::findMaxWidth() {
return;
Uint32 size = (Uint32)mItems.size();
Int32 width;
Text textCache;
textCache.setStyleConfig( fontStyleConfig );
Float width;
mMaxTextWidth = 0;
@@ -367,11 +366,10 @@ void UIListBox::findMaxWidth() {
if ( NULL != mItems[i] ) {
width = (Int32)mItems[i]->getTextWidth();
} else {
textCache.setString( mTexts[i] );
width = textCache.getTextWidth();
width = Text::getTextWidth( mTexts[i], fontStyleConfig );
}
if ( width > (Int32)mMaxTextWidth )
if ( width > (Float)mMaxTextWidth )
mMaxTextWidth = width;
}
}
@@ -383,27 +381,28 @@ void UIListBox::updateListBoxItemsSize() {
itemUpdateSize( mItems[i] );
}
void UIListBox::itemUpdateSize( UIListBoxItem* Item ) {
if ( NULL != Item ) {
Int32 width = (Int32)Item->getTextWidth();
void UIListBox::itemUpdateSize( UIListBoxItem* item ) {
if ( NULL == item )
return;
if ( width > (Int32)mMaxTextWidth ) {
mMaxTextWidth = width;
}
Float width = eeceil( item->getTextWidth() );
if ( !mHScrollBar->isVisible() ) {
if ( width < mContainer->getSize().getWidth() )
width = mContainer->getSize().getWidth();
if ( ( mItemsNotVisible > 0 && ScrollBarMode::Auto == mVScrollMode ) ||
ScrollBarMode::AlwaysOn == mVScrollMode )
width -= mVScrollBar->getSize().getWidth();
} else {
width = mMaxTextWidth;
}
Item->setSize( width, mRowHeight );
if ( width > (Float)mMaxTextWidth ) {
mMaxTextWidth = width;
}
if ( !mHScrollBar->isVisible() ) {
if ( width < mContainer->getPixelsSize().getWidth() )
width = mContainer->getPixelsSize().getWidth();
if ( ( mItemsNotVisible > 0 && ScrollBarMode::Auto == mVScrollMode ) ||
ScrollBarMode::AlwaysOn == mVScrollMode )
width -= mVScrollBar->getPixelsSize().getWidth();
} else {
width = mMaxTextWidth;
}
item->setPixelsSize( width, PixelDensity::dpToPx( mRowHeight ) );
}
void UIListBox::containerResize() {

View File

@@ -166,7 +166,7 @@ DropDownList.role_ui {
<PushButton id="llm_settings_but" class="llm_button" text="@string(settings, Settings)" tooltip="@string(settings, Settings)" icon="icon(settings, 14dp)" min-width="32dp" />
<PushButton id="llm_more" class="llm_button" tooltip="@string(more_options, More Options)" icon="icon(more-fill, 14dp)" min-width="32dp" />
<hbox lw="0" lw8="1" lh="mp" layout_gravity="center" padding-left="8dp" padding-right="8dp">
<DropDownList class="model_ui" lw="0" lw8="1" selected-index="0"></DropDownList>
<DropDownList class="model_ui" menu-width-mode="expand-if-needed-centered" lw="0" lw8="1" selected-index="0"></DropDownList>
<PushButton id="refresh_model_ui" tooltip="@string(refresh_model_ui, Refresh Local Models)" icon="icon(refresh, 14dp)" />
</hbox>
<SelectButton id="llm_private_chat" class="llm_button" tooltip="@string(private_chat, Toggle Private Chat)" icon="icon(chat-private, 14dp)" min-width="32dp" margin-right="8dp" select-on-click="true" />

View File

@@ -1179,16 +1179,16 @@ void ProjectBuildManager::buildSidePanelTab() {
<TextView text="@string(build_settings, Build Settings)" font-size="15dp" focusable="false" />
<TextView text="@string(build_configuration, Build Configuration)" focusable="false" />
<hbox lw="mp" lh="wc" margin-top="2dp" margin-bottom="4dp">
<DropDownList id="build_list" layout_width="0" lw8="1" layout_height="wrap_content" />
<DropDownList id="build_list" layout_width="0" lw8="1" layout_height="wrap_content" menu-width-mode="expand-if-needed" />
<PushButton id="build_edit" id="build_edit" text="@string(edit_build, Edit Build)" tooltip="@string(edit_build, Edit Build)" text-as-fallback="true" icon="icon(file-edit, 12dp)" margin-left="2dp" />
<PushButton id="build_add" id="build_add" text="@string(add_build, Add Build)" tooltip="@string(add_build, Add Build)" text-as-fallback="true" icon="icon(add, 12dp)" margin-left="2dp" />
</hbox>
<TextView text="@string(build_target, Build Target)" margin-top="8dp" focusable="false" />
<DropDownList lw="mp" id="build_type_list" margin-top="2dp" />
<DropDownList lw="mp" id="build_type_list" margin-top="2dp" menu-width-mode="expand-if-needed" />
<PushButton id="build_button" lw="mp" lh="wc" text="@string(build, Build)" margin-top="8dp" icon="icon(hammer, 12dp)" />
<PushButton id="clean_button" lw="mp" lh="wc" text="@string(clean, Clean)" margin-top="8dp" icon="icon(eraser, 12dp)" />
<TextView text="@string(run_target, Run Target)" margin-top="8dp" focusable="false" />
<DropDownList lw="mp" id="run_config_list" margin-top="2dp" />
<DropDownList lw="mp" id="run_config_list" margin-top="2dp" menu-width-mode="expand-if-needed" />
<PushButton id="run_button" lw="mp" lh="wc" text="@string(run, Run)" margin-top="8dp" icon="icon(play, 12dp)" />
<PushButton id="build_and_run_button" lw="mp" lh="wc" text="@string(build_and_run, Build & Run)" margin-top="8dp" icon="icon(play, 12dp)" />
</vbox>