mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-05-28 17:16:29 +03:00
Redesigned LLM model selection in AI Assistant chat UI (now it's possible to search by filtering its name).
Redesigned UIDropDownList to inherit from UIDropDown which is a base class to handle different types of drop-downs. Added UIDropDownModelList which is the same as UIDropDownList but uses a UIListView by default so it's a model/view based DropDown. Fix crash when changing states of the buttons in the build panel. Increased the default animations speed.
This commit is contained in:
@@ -369,6 +369,12 @@
|
||||
"command": "${project_root}/bin/eepp-ui-markdownview-debug",
|
||||
"name": "eepp-ui-markdownview-debug",
|
||||
"working_dir": "${project_root}/bin"
|
||||
},
|
||||
{
|
||||
"args": "",
|
||||
"command": "${project_root}/bin/eepp-ui-dropdownmodellist-debug",
|
||||
"name": "eepp-ui-dropdownmodellist-debug",
|
||||
"working_dir": "${project_root}/bin"
|
||||
}
|
||||
],
|
||||
"var": {
|
||||
|
||||
@@ -244,7 +244,8 @@ Anchor:hover {
|
||||
|
||||
PushButton,
|
||||
SelectButton,
|
||||
DropDownList {
|
||||
DropDownList,
|
||||
DropDownModelList {
|
||||
padding-left: var(--base-horizontal-padding);
|
||||
padding-right: var(--base-horizontal-padding);
|
||||
padding-top: var(--base-vertical-padding);
|
||||
@@ -259,6 +260,7 @@ DropDownList {
|
||||
}
|
||||
|
||||
DropDownList,
|
||||
DropDownModelList,
|
||||
ComboBox::DropDownList {
|
||||
max-visible-items: 6;
|
||||
}
|
||||
@@ -267,6 +269,8 @@ PushButton:hover,
|
||||
PushButton:focus,
|
||||
DropDownList:hover,
|
||||
DropDownList:focus,
|
||||
DropDownModelList:hover,
|
||||
DropDownModelList:focus,
|
||||
SelectButton:hover,
|
||||
SelectButton:focus,
|
||||
ComboBox:hover,
|
||||
@@ -800,12 +804,14 @@ Window::border::bottom {
|
||||
background-color: var(--separator);
|
||||
}
|
||||
|
||||
DropDownList {
|
||||
DropDownList,
|
||||
DropDownModelList {
|
||||
padding-right: 16dp;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
DropDownList,
|
||||
DropDownModelList,
|
||||
ComboBox::Button {
|
||||
foreground-image: url("data:image/svg,<svg viewBox='0 0 24 24' fill='white'><path d='M12 15.6315L20.9679 10.8838L20.0321 9.11619L12 13.3685L3.96788 9.11619L3.0321 10.8838L12 15.6315Z'></path></svg>");
|
||||
foreground-position-x: right 6dp;
|
||||
@@ -816,6 +822,8 @@ ComboBox::Button {
|
||||
|
||||
DropDownList:hover,
|
||||
DropDownList:focus,
|
||||
DropDownModelList:hover,
|
||||
DropDownModelList:focus,
|
||||
ComboBox::Button:focus,
|
||||
ComboBox::Button:hover {
|
||||
foreground-tint: var(--icon-active);
|
||||
|
||||
@@ -13,6 +13,7 @@ using namespace EE::Math;
|
||||
namespace EE { namespace UI {
|
||||
class UIPushButton;
|
||||
class UILinearLayout;
|
||||
class UIDropDownModelList;
|
||||
}} // namespace EE::UI
|
||||
|
||||
namespace EE { namespace UI { namespace Abstract {
|
||||
@@ -146,6 +147,7 @@ class EE_API UIAbstractTableView : public UIAbstractView {
|
||||
|
||||
protected:
|
||||
friend class EE::UI::UITableHeaderColumn;
|
||||
friend class EE::UI::UIDropDownModelList;
|
||||
|
||||
struct ColumnData {
|
||||
Float minWidth{ 0 };
|
||||
|
||||
89
include/eepp/ui/uidropdown.hpp
Normal file
89
include/eepp/ui/uidropdown.hpp
Normal file
@@ -0,0 +1,89 @@
|
||||
#ifndef EE_UI_UIDROPDOWN_HPP
|
||||
#define EE_UI_UIDROPDOWN_HPP
|
||||
|
||||
#include <eepp/ui/uitextinput.hpp>
|
||||
|
||||
namespace EE { namespace UI {
|
||||
|
||||
class EE_API UIDropDown : public UITextInput {
|
||||
public:
|
||||
enum class MenuWidthMode {
|
||||
DropDown,
|
||||
Contents,
|
||||
ContentsCentered,
|
||||
ExpandIfNeeded,
|
||||
ExpandIfNeededCentered
|
||||
};
|
||||
|
||||
static MenuWidthMode menuWidthModeFromString( std::string_view str );
|
||||
|
||||
static std::string menuWidthModeToString( MenuWidthMode rule );
|
||||
|
||||
struct StyleConfig {
|
||||
Uint32 MaxNumVisibleItems = 10;
|
||||
bool PopUpToRoot = false;
|
||||
MenuWidthMode menuWidthRule{ MenuWidthMode::DropDown };
|
||||
};
|
||||
|
||||
virtual ~UIDropDown();
|
||||
|
||||
virtual Uint32 getType() const;
|
||||
virtual bool isType( const Uint32& type ) const;
|
||||
|
||||
virtual void setTheme( UITheme* Theme );
|
||||
|
||||
virtual UIDropDown* showList();
|
||||
|
||||
bool getPopUpToRoot() const;
|
||||
UIDropDown* setPopUpToRoot( bool popUpToRoot );
|
||||
|
||||
Uint32 getMaxNumVisibleItems() const;
|
||||
virtual UIDropDown* setMaxNumVisibleItems( const Uint32& maxNumVisibleItems );
|
||||
|
||||
const StyleConfig& getStyleConfig() const;
|
||||
UIDropDown* setStyleConfig( const StyleConfig& styleConfig );
|
||||
|
||||
UIDropDown* setMenuWidthMode( MenuWidthMode rule );
|
||||
MenuWidthMode getMenuWidthMode() const;
|
||||
|
||||
virtual bool applyProperty( const StyleSheetProperty& attribute );
|
||||
virtual std::string getPropertyString( const PropertyDefinition* propertyDef,
|
||||
const Uint32& propertyIndex = 0 ) const;
|
||||
virtual std::vector<PropertyId> getPropertiesImplemented() const;
|
||||
|
||||
protected:
|
||||
StyleConfig mStyleConfig;
|
||||
UINode* mFriendNode{ nullptr };
|
||||
|
||||
UIDropDown( const std::string& tag );
|
||||
|
||||
virtual UIWidget* getPopUpWidget() const;
|
||||
|
||||
void onPopUpFocusLoss( const Event* Event );
|
||||
|
||||
virtual void onItemSelected( const Event* Event );
|
||||
virtual void show();
|
||||
virtual void hide();
|
||||
|
||||
virtual Uint32 onMouseOver( const Vector2i& position, const Uint32& flags );
|
||||
virtual Uint32 onMouseLeave( const Vector2i& position, const Uint32& flags );
|
||||
virtual Uint32 onMouseClick( const Vector2i& position, const Uint32& flags );
|
||||
|
||||
virtual void onItemClicked( const Event* Event );
|
||||
virtual void onItemKeyDown( const Event* Event );
|
||||
virtual void onWidgetClear( const Event* Event );
|
||||
virtual Uint32 onKeyDown( const KeyEvent& Event );
|
||||
|
||||
virtual void onSizeChange();
|
||||
virtual void onAutoSize();
|
||||
virtual void onThemeLoaded();
|
||||
|
||||
void setFriendNode( UINode* friendNode );
|
||||
|
||||
Float getPopUpWidth( Float contentsWidth ) const;
|
||||
void alignPopUp( UIWidget* widget );
|
||||
};
|
||||
|
||||
}} // namespace EE::UI
|
||||
|
||||
#endif
|
||||
@@ -1,30 +1,15 @@
|
||||
#ifndef EE_UICUIDROPDOWNLIST_HPP
|
||||
#define EE_UICUIDROPDOWNLIST_HPP
|
||||
|
||||
#include <eepp/ui/uidropdown.hpp>
|
||||
#include <eepp/ui/uilistbox.hpp>
|
||||
#include <eepp/ui/uitextinput.hpp>
|
||||
|
||||
namespace EE { namespace UI {
|
||||
|
||||
class EE_API UIDropDownList : public UITextInput {
|
||||
class EE_API UIDropDownList : public UIDropDown {
|
||||
public:
|
||||
enum class MenuWidthMode {
|
||||
DropDown,
|
||||
Contents,
|
||||
ContentsCentered,
|
||||
ExpandIfNeeded,
|
||||
ExpandIfNeededCentered
|
||||
};
|
||||
|
||||
static MenuWidthMode menuWidthModeFromString( std::string_view str );
|
||||
|
||||
static std::string menuWidthModeToString( MenuWidthMode rule );
|
||||
|
||||
struct StyleConfig {
|
||||
Uint32 MaxNumVisibleItems = 10;
|
||||
bool PopUpToRoot = false;
|
||||
MenuWidthMode menuWidthRule{ MenuWidthMode::DropDown };
|
||||
};
|
||||
using MenuWidthMode = UIDropDown::MenuWidthMode;
|
||||
using StyleConfig = UIDropDown::StyleConfig;
|
||||
|
||||
static UIDropDownList* NewWithTag( const std::string& tag );
|
||||
|
||||
@@ -33,84 +18,39 @@ class EE_API UIDropDownList : public UITextInput {
|
||||
virtual ~UIDropDownList();
|
||||
|
||||
virtual Uint32 getType() const;
|
||||
|
||||
virtual bool isType( const Uint32& type ) const;
|
||||
|
||||
virtual void setTheme( UITheme* Theme );
|
||||
|
||||
UIListBox* getListBox() const;
|
||||
|
||||
UIDropDownList* showList();
|
||||
|
||||
bool getPopUpToRoot() const;
|
||||
|
||||
UIDropDownList* setPopUpToRoot( bool popUpToRoot );
|
||||
|
||||
Uint32 getMaxNumVisibleItems() const;
|
||||
|
||||
UIDropDownList* setMaxNumVisibleItems( const Uint32& maxNumVisibleItems );
|
||||
|
||||
const StyleConfig& getStyleConfig() const;
|
||||
|
||||
UIDropDownList* setStyleConfig( const StyleConfig& styleConfig );
|
||||
virtual UIDropDownList* setMaxNumVisibleItems( const Uint32& maxNumVisibleItems );
|
||||
|
||||
virtual bool applyProperty( const StyleSheetProperty& attribute );
|
||||
|
||||
virtual std::string getPropertyString( const PropertyDefinition* propertyDef,
|
||||
const Uint32& propertyIndex = 0 ) const;
|
||||
|
||||
virtual std::vector<PropertyId> getPropertiesImplemented() const;
|
||||
|
||||
virtual void loadFromXmlNode( const pugi::xml_node& node );
|
||||
|
||||
UIDropDownList* setMenuWidthMode( MenuWidthMode rule );
|
||||
|
||||
MenuWidthMode getMenuWidthMode() const;
|
||||
|
||||
protected:
|
||||
friend class UIComboBox;
|
||||
|
||||
StyleConfig mStyleConfig;
|
||||
UIListBox* mListBox;
|
||||
UINode* mFriendNode;
|
||||
Uint32 mListBoxCloseCb{ 0 };
|
||||
|
||||
UIDropDownList( const std::string& tag = "dropdownlist" );
|
||||
|
||||
void onListBoxFocusLoss( const Event* Event );
|
||||
virtual UIWidget* getPopUpWidget() const;
|
||||
|
||||
virtual void onItemSelected( const Event* Event );
|
||||
|
||||
virtual void show();
|
||||
|
||||
virtual void hide();
|
||||
|
||||
virtual Uint32 onMouseOver( const Vector2i& position, const Uint32& flags );
|
||||
|
||||
virtual Uint32 onMouseLeave( const Vector2i& position, const Uint32& flags );
|
||||
|
||||
virtual Uint32 onMouseUp( const Vector2i& position, const Uint32& flags );
|
||||
|
||||
virtual Uint32 onMouseClick( const Vector2i& position, const Uint32& flags );
|
||||
|
||||
virtual void onItemClicked( const Event* Event );
|
||||
|
||||
virtual void onItemKeyDown( const Event* Event );
|
||||
|
||||
virtual void onWidgetClear( const Event* Event );
|
||||
|
||||
virtual Uint32 onKeyDown( const KeyEvent& Event );
|
||||
|
||||
virtual void onClassChange();
|
||||
|
||||
virtual void onSizeChange();
|
||||
|
||||
virtual void onAutoSize();
|
||||
|
||||
virtual void onThemeLoaded();
|
||||
|
||||
void setFriendNode( UINode* friendNode );
|
||||
|
||||
void destroyListBox();
|
||||
};
|
||||
|
||||
|
||||
68
include/eepp/ui/uidropdownmodellist.hpp
Normal file
68
include/eepp/ui/uidropdownmodellist.hpp
Normal file
@@ -0,0 +1,68 @@
|
||||
#ifndef EE_UI_UIDROPDOWNMODELLIST_HPP
|
||||
#define EE_UI_UIDROPDOWNMODELLIST_HPP
|
||||
|
||||
#include <eepp/ui/abstract/uiabstracttableview.hpp>
|
||||
#include <eepp/ui/uidropdown.hpp>
|
||||
|
||||
namespace EE { namespace UI {
|
||||
|
||||
class EE_API UIDropDownModelList : public UIDropDown {
|
||||
public:
|
||||
using MenuWidthMode = UIDropDown::MenuWidthMode;
|
||||
using StyleConfig = UIDropDown::StyleConfig;
|
||||
|
||||
static UIDropDownModelList* NewWithTag( const std::string& tag );
|
||||
|
||||
static UIDropDownModelList* New();
|
||||
|
||||
virtual ~UIDropDownModelList();
|
||||
|
||||
virtual Uint32 getType() const;
|
||||
virtual bool isType( const Uint32& type ) const;
|
||||
|
||||
UIAbstractTableView* getListView() const;
|
||||
|
||||
void setListView( UIAbstractTableView* listView );
|
||||
|
||||
std::shared_ptr<Model> getModel() const;
|
||||
|
||||
virtual void setModel( std::shared_ptr<Model> model );
|
||||
|
||||
UIDropDownModelList* showList();
|
||||
|
||||
virtual UIDropDownModelList* setMaxNumVisibleItems( const Uint32& maxNumVisibleItems );
|
||||
|
||||
virtual std::string getPropertyString( const PropertyDefinition* propertyDef,
|
||||
const Uint32& propertyIndex = 0 ) const;
|
||||
|
||||
virtual std::vector<PropertyId> getPropertiesImplemented() const;
|
||||
|
||||
protected:
|
||||
UIAbstractTableView* mListView;
|
||||
Uint32 mListViewCloseCb{ 0 };
|
||||
std::shared_ptr<Model> mModel;
|
||||
|
||||
UIDropDownModelList( const std::string& tag = "dropdownmodellist" );
|
||||
|
||||
virtual UIWidget* getPopUpWidget() const;
|
||||
|
||||
virtual void onItemSelected( const Event* Event );
|
||||
|
||||
virtual Uint32 onMouseUp( const Vector2i& position, const Uint32& flags );
|
||||
|
||||
virtual Uint32 onKeyDown( const KeyEvent& Event );
|
||||
|
||||
virtual void onItemClicked( const Event* Event );
|
||||
|
||||
virtual void onClassChange();
|
||||
|
||||
void destroyListView();
|
||||
|
||||
UIWidget* createDefaultListView();
|
||||
|
||||
void updateSelectionIndex();
|
||||
};
|
||||
|
||||
}} // namespace EE::UI
|
||||
|
||||
#endif
|
||||
@@ -65,6 +65,7 @@ enum UINodeType {
|
||||
UI_TYPE_PROGRESSBAR,
|
||||
UI_TYPE_LISTBOX,
|
||||
UI_TYPE_LISTBOXITEM,
|
||||
UI_TYPE_DROPDOWN,
|
||||
UI_TYPE_DROPDOWNLIST,
|
||||
UI_TYPE_MENU_SEPARATOR,
|
||||
UI_TYPE_COMBOBOX,
|
||||
@@ -117,6 +118,7 @@ enum UINodeType {
|
||||
UI_TYPE_HTML_TABLE_FOOTER,
|
||||
UI_TYPE_HTML_TABLE_ROW,
|
||||
UI_TYPE_HTML_TABLE_CELL,
|
||||
UI_TYPE_DROPDOWNMODELLIST,
|
||||
UI_TYPE_MODULES = 10000,
|
||||
UI_TYPE_TERMINAL = 10001,
|
||||
UI_TYPE_USER = 200000,
|
||||
|
||||
@@ -9,6 +9,8 @@ class EE_API UIListView : public UITableView {
|
||||
public:
|
||||
static UIListView* New();
|
||||
|
||||
static UIListView* NewWithTag( const std::string& tag );
|
||||
|
||||
Uint32 getType() const;
|
||||
|
||||
bool isType( const Uint32& type ) const;
|
||||
@@ -16,7 +18,7 @@ class EE_API UIListView : public UITableView {
|
||||
void setTheme( UITheme* Theme );
|
||||
|
||||
protected:
|
||||
UIListView();
|
||||
UIListView( const std::string& tag = "listview" );
|
||||
};
|
||||
|
||||
}} // namespace EE::UI
|
||||
|
||||
@@ -1580,6 +1580,12 @@ solution "eepp"
|
||||
files { "src/examples/ui_application_hello_world/*.cpp" }
|
||||
build_link_configuration( "eepp-ui-application-hello-world", true )
|
||||
|
||||
project "eepp-ui-dropdownmodellist"
|
||||
set_kind()
|
||||
language "C++"
|
||||
files { "src/examples/ui_dropdownmodellist/*.cpp" }
|
||||
build_link_configuration( "eepp-ui-dropdownmodellist", true )
|
||||
|
||||
project "eepp-ui-richtext"
|
||||
set_kind()
|
||||
language "C++"
|
||||
|
||||
@@ -1466,6 +1466,12 @@ workspace "eepp"
|
||||
files { "src/examples/ui_application_hello_world/*.cpp" }
|
||||
build_link_configuration( "eepp-ui-application-hello-world", true )
|
||||
|
||||
project "eepp-ui-dropdownmodellist"
|
||||
set_kind()
|
||||
language "C++"
|
||||
files { "src/examples/ui_dropdownmodellist/*.cpp" }
|
||||
build_link_configuration( "eepp-ui-dropdownmodellist", true )
|
||||
|
||||
project "eepp-ui-richtext"
|
||||
set_kind()
|
||||
language "C++"
|
||||
|
||||
@@ -384,6 +384,7 @@ void addCSS() {
|
||||
{ "RadioButton", "keyword" },
|
||||
{ "ComboBox", "keyword" },
|
||||
{ "DropDownList", "keyword" },
|
||||
{ "DropDownModelList", "keyword" },
|
||||
{ "Image", "keyword" },
|
||||
{ "ListBox", "keyword" },
|
||||
{ "MenuBar", "keyword" },
|
||||
@@ -425,6 +426,7 @@ void addCSS() {
|
||||
{ "PopUpMenu", "keyword" },
|
||||
{ "ImageViewer", "keyword" },
|
||||
{ "AudioPlayer", "keyword" },
|
||||
{ "Table", "keyword" },
|
||||
|
||||
},
|
||||
"",
|
||||
|
||||
339
src/eepp/ui/uidropdown.cpp
Normal file
339
src/eepp/ui/uidropdown.cpp
Normal file
@@ -0,0 +1,339 @@
|
||||
#include <eepp/scene/actions/actions.hpp>
|
||||
#include <eepp/scene/scenemanager.hpp>
|
||||
#include <eepp/scene/scenenode.hpp>
|
||||
#include <eepp/ui/css/propertydefinition.hpp>
|
||||
#include <eepp/ui/uidropdown.hpp>
|
||||
#include <eepp/ui/uiscenenode.hpp>
|
||||
#include <eepp/ui/uithememanager.hpp>
|
||||
|
||||
namespace EE { namespace UI {
|
||||
|
||||
UIDropDown::MenuWidthMode UIDropDown::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 UIDropDown::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";
|
||||
}
|
||||
|
||||
UIDropDown::UIDropDown( const std::string& tag ) : UITextInput( tag ) {
|
||||
mEnabledCreateContextMenu = false;
|
||||
setClipType( ClipType::ContentBox );
|
||||
setFlags( UI_AUTO_SIZE | UI_AUTO_PADDING | UI_SCROLLABLE );
|
||||
unsetFlags( UI_TEXT_SELECTION_ENABLED );
|
||||
setAllowEditing( false );
|
||||
}
|
||||
|
||||
UIDropDown::~UIDropDown() {}
|
||||
|
||||
Uint32 UIDropDown::getType() const {
|
||||
return UI_TYPE_DROPDOWN;
|
||||
}
|
||||
|
||||
bool UIDropDown::isType( const Uint32& type ) const {
|
||||
return UIDropDown::getType() == type ? true : UITextInput::isType( type );
|
||||
}
|
||||
|
||||
void UIDropDown::setTheme( UITheme* Theme ) {
|
||||
UIWidget::setTheme( Theme );
|
||||
setThemeSkin( Theme, "dropdownlist" );
|
||||
onThemeLoaded();
|
||||
}
|
||||
|
||||
void UIDropDown::onSizeChange() {
|
||||
onAutoSize();
|
||||
UITextInput::onSizeChange();
|
||||
}
|
||||
|
||||
void UIDropDown::onThemeLoaded() {
|
||||
autoPadding();
|
||||
onAutoSize();
|
||||
}
|
||||
|
||||
void UIDropDown::setFriendNode( UINode* friendNode ) {
|
||||
mFriendNode = friendNode;
|
||||
}
|
||||
|
||||
void UIDropDown::onAutoSize() {
|
||||
Float max = eemax<Float>( PixelDensity::dpToPxI( getSkinSize().getHeight() ),
|
||||
mTextCache.getLineSpacing() );
|
||||
|
||||
if ( mHeightPolicy == SizePolicy::WrapContent ) {
|
||||
setInternalPixelsHeight( eeceil( max + mPaddingPx.Top + mPaddingPx.Bottom ) );
|
||||
} else if ( ( ( mFlags & UI_AUTO_SIZE ) || 0 == getSize().getHeight() ) && max > 0 ) {
|
||||
setInternalPixelsHeight( eeceil( max ) );
|
||||
}
|
||||
}
|
||||
|
||||
UIWidget* UIDropDown::getPopUpWidget() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Uint32 UIDropDown::onMouseClick( const Vector2i& Pos, const Uint32& Flags ) {
|
||||
if ( ( Flags & EE_BUTTON_LMASK ) && NULL == mFriendNode )
|
||||
showList();
|
||||
|
||||
if ( NULL != mFriendNode ) {
|
||||
UITextInput::onMouseClick( Pos, Flags );
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
UIDropDown* UIDropDown::showList() {
|
||||
return this;
|
||||
}
|
||||
|
||||
Float UIDropDown::getPopUpWidth( Float contentsWidth ) const {
|
||||
Float width = NULL != mFriendNode ? mFriendNode->getSize().getWidth() : getSize().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;
|
||||
}
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
void UIDropDown::alignPopUp( UIWidget* widget ) {
|
||||
if ( !mStyleConfig.PopUpToRoot )
|
||||
widget->setParent( getWindowContainer() );
|
||||
else
|
||||
widget->setParent( getUISceneNode()->getRoot() );
|
||||
|
||||
widget->toFront();
|
||||
|
||||
bool center = mStyleConfig.menuWidthRule == MenuWidthMode::ContentsCentered ||
|
||||
mStyleConfig.menuWidthRule == MenuWidthMode::ExpandIfNeededCentered;
|
||||
|
||||
Float width = widget->getSize().getWidth();
|
||||
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, widget->getSize() ) ) ) {
|
||||
pos = Vector2f( mDpPos.x + offsetX, mDpPos.y - widget->getSize().getHeight() );
|
||||
}
|
||||
|
||||
if ( mStyleConfig.PopUpToRoot ) {
|
||||
getParent()->nodeToWorld( pos );
|
||||
pos = PixelDensity::pxToDp( pos );
|
||||
} else {
|
||||
Node* parentNode = getParent();
|
||||
Node* rp = getWindowContainer();
|
||||
while ( rp != parentNode ) {
|
||||
pos += parentNode->getPosition();
|
||||
parentNode = parentNode->getParent();
|
||||
}
|
||||
}
|
||||
|
||||
widget->setPosition( pos );
|
||||
show();
|
||||
widget->setFocus();
|
||||
}
|
||||
|
||||
bool UIDropDown::getPopUpToRoot() const {
|
||||
return mStyleConfig.PopUpToRoot;
|
||||
}
|
||||
|
||||
UIDropDown* UIDropDown::setPopUpToRoot( bool popUpToRoot ) {
|
||||
mStyleConfig.PopUpToRoot = popUpToRoot;
|
||||
return this;
|
||||
}
|
||||
|
||||
Uint32 UIDropDown::getMaxNumVisibleItems() const {
|
||||
return mStyleConfig.MaxNumVisibleItems;
|
||||
}
|
||||
|
||||
UIDropDown* UIDropDown::setMaxNumVisibleItems( const Uint32& maxNumVisibleItems ) {
|
||||
mStyleConfig.MaxNumVisibleItems = maxNumVisibleItems;
|
||||
return this;
|
||||
}
|
||||
|
||||
const UIDropDown::StyleConfig& UIDropDown::getStyleConfig() const {
|
||||
return mStyleConfig;
|
||||
}
|
||||
|
||||
UIDropDown* UIDropDown::setStyleConfig( const StyleConfig& styleConfig ) {
|
||||
mStyleConfig = styleConfig;
|
||||
|
||||
setMaxNumVisibleItems( mStyleConfig.MaxNumVisibleItems );
|
||||
setPopUpToRoot( mStyleConfig.PopUpToRoot );
|
||||
setMenuWidthMode( mStyleConfig.menuWidthRule );
|
||||
return this;
|
||||
}
|
||||
|
||||
void UIDropDown::onWidgetClear( const Event* ) {
|
||||
setText( "" );
|
||||
sendCommonEvent( Event::OnClear );
|
||||
}
|
||||
|
||||
void UIDropDown::onItemKeyDown( const Event* Event ) {
|
||||
const KeyEvent* KEvent = reinterpret_cast<const KeyEvent*>( Event );
|
||||
|
||||
if ( KEvent->getKeyCode() == KEY_RETURN )
|
||||
onItemClicked( Event );
|
||||
else if ( KEvent->getKeyCode() == KEY_ESCAPE ) {
|
||||
hide();
|
||||
setFocus();
|
||||
}
|
||||
}
|
||||
|
||||
void UIDropDown::onPopUpFocusLoss( const Event* ) {
|
||||
if ( NULL == getEventDispatcher() )
|
||||
return;
|
||||
|
||||
bool frienIsFocus = NULL != mFriendNode && mFriendNode == getEventDispatcher()->getFocusNode();
|
||||
bool isChildFocus = isChild( getEventDispatcher()->getFocusNode() );
|
||||
|
||||
if ( getEventDispatcher()->getFocusNode() != this && !isChildFocus && !frienIsFocus ) {
|
||||
hide();
|
||||
}
|
||||
}
|
||||
|
||||
void UIDropDown::onItemClicked( const Event* ) {
|
||||
hide();
|
||||
setFocus();
|
||||
}
|
||||
|
||||
void UIDropDown::onItemSelected( const Event* ) {}
|
||||
|
||||
void UIDropDown::show() {
|
||||
UIWidget* widget = getPopUpWidget();
|
||||
if ( NULL == widget )
|
||||
return;
|
||||
|
||||
widget->setEnabled( true );
|
||||
widget->setVisible( true );
|
||||
|
||||
if ( NULL != getUISceneNode() &&
|
||||
getUISceneNode()->getUIThemeManager()->getDefaultEffectsEnabled() ) {
|
||||
widget->runAction( Actions::Sequence::New(
|
||||
Actions::Fade::New( 255.f == widget->getAlpha() ? 0.f : widget->getAlpha(), 255.f,
|
||||
getUISceneNode()->getUIThemeManager()->getWidgetsFadeOutTime() ),
|
||||
Actions::Spawn::New( Actions::Enable::New(), Actions::Visible::New( true ) ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
void UIDropDown::hide() {
|
||||
UIWidget* widget = getPopUpWidget();
|
||||
if ( NULL == widget )
|
||||
return;
|
||||
|
||||
if ( NULL != getUISceneNode() &&
|
||||
getUISceneNode()->getUIThemeManager()->getDefaultEffectsEnabled() ) {
|
||||
widget->runAction( Actions::Sequence::New(
|
||||
Actions::FadeOut::New( getUISceneNode()->getUIThemeManager()->getWidgetsFadeOutTime() ),
|
||||
Actions::Spawn::New( Actions::Disable::New(), Actions::Visible::New( false ) ) ) );
|
||||
} else {
|
||||
widget->setEnabled( false );
|
||||
widget->setVisible( false );
|
||||
}
|
||||
}
|
||||
|
||||
Uint32 UIDropDown::onMouseOver( const Vector2i& position, const Uint32& flags ) {
|
||||
if ( getParent()->isType( UI_TYPE_COMBOBOX ) ) {
|
||||
return UITextInput::onMouseOver( position, flags );
|
||||
} else {
|
||||
return UITextView::onMouseOver( position, flags );
|
||||
}
|
||||
}
|
||||
|
||||
Uint32 UIDropDown::onMouseLeave( const Vector2i& position, const Uint32& flags ) {
|
||||
if ( getParent()->isType( UI_TYPE_COMBOBOX ) ) {
|
||||
return UITextInput::onMouseLeave( position, flags );
|
||||
} else {
|
||||
return UITextView::onMouseLeave( position, flags );
|
||||
}
|
||||
}
|
||||
|
||||
Uint32 UIDropDown::onKeyDown( const KeyEvent& Event ) {
|
||||
return UITextInput::onKeyDown( Event );
|
||||
}
|
||||
|
||||
bool UIDropDown::applyProperty( const StyleSheetProperty& attribute ) {
|
||||
if ( !checkPropertyDefinition( attribute ) )
|
||||
return false;
|
||||
|
||||
switch ( attribute.getPropertyDefinition()->getPropertyId() ) {
|
||||
case PropertyId::PopUpToRoot:
|
||||
setPopUpToRoot( attribute.asBool() );
|
||||
break;
|
||||
case PropertyId::MaxVisibleItems:
|
||||
setMaxNumVisibleItems( attribute.asUint() );
|
||||
break;
|
||||
case PropertyId::MenuWidthMode:
|
||||
setMenuWidthMode( menuWidthModeFromString( attribute.getValue() ) );
|
||||
break;
|
||||
default:
|
||||
return UITextInput::applyProperty( attribute );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string UIDropDown::getPropertyString( const PropertyDefinition* propertyDef,
|
||||
const Uint32& propertyIndex ) const {
|
||||
if ( NULL == propertyDef )
|
||||
return "";
|
||||
|
||||
switch ( propertyDef->getPropertyId() ) {
|
||||
case PropertyId::PopUpToRoot:
|
||||
return mStyleConfig.PopUpToRoot ? "true" : "false";
|
||||
case PropertyId::MaxVisibleItems:
|
||||
return String::toString( mStyleConfig.MaxNumVisibleItems );
|
||||
case PropertyId::MenuWidthMode:
|
||||
return menuWidthModeToString( mStyleConfig.menuWidthRule );
|
||||
default:
|
||||
return UITextInput::getPropertyString( propertyDef, propertyIndex );
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
std::vector<PropertyId> UIDropDown::getPropertiesImplemented() const {
|
||||
auto props = UITextInput::getPropertiesImplemented();
|
||||
auto local = { PropertyId::PopUpToRoot, PropertyId::MaxVisibleItems,
|
||||
PropertyId::MenuWidthMode };
|
||||
props.insert( props.end(), local.begin(), local.end() );
|
||||
return props;
|
||||
}
|
||||
|
||||
UIDropDown* UIDropDown::setMenuWidthMode( MenuWidthMode rule ) {
|
||||
mStyleConfig.menuWidthRule = rule;
|
||||
return this;
|
||||
}
|
||||
|
||||
UIDropDown::MenuWidthMode UIDropDown::getMenuWidthMode() const {
|
||||
return mStyleConfig.menuWidthRule;
|
||||
}
|
||||
|
||||
}} // namespace EE::UI
|
||||
@@ -10,34 +10,6 @@
|
||||
|
||||
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 ) );
|
||||
}
|
||||
@@ -46,15 +18,7 @@ UIDropDownList* UIDropDownList::New() {
|
||||
return eeNew( UIDropDownList, () );
|
||||
}
|
||||
|
||||
UIDropDownList::UIDropDownList( const std::string& tag ) :
|
||||
UITextInput( tag ), mListBox( NULL ), mFriendNode( NULL ) {
|
||||
mEnabledCreateContextMenu = false;
|
||||
setClipType( ClipType::ContentBox );
|
||||
setFlags( UI_AUTO_SIZE | UI_AUTO_PADDING | UI_SCROLLABLE );
|
||||
unsetFlags( UI_TEXT_SELECTION_ENABLED );
|
||||
|
||||
setAllowEditing( false );
|
||||
|
||||
UIDropDownList::UIDropDownList( const std::string& tag ) : UIDropDown( tag ), mListBox( NULL ) {
|
||||
applyDefaultTheme();
|
||||
|
||||
mListBox = UIListBox::NewWithTag( mTag + "::listbox" );
|
||||
@@ -65,7 +29,7 @@ UIDropDownList::UIDropDownList( const std::string& tag ) :
|
||||
// This will force to change the parent when shown, and force the CSS style reload.
|
||||
mListBox->setParent( this );
|
||||
|
||||
mListBox->on( Event::OnWidgetFocusLoss, [this]( auto event ) { onListBoxFocusLoss( event ); } );
|
||||
mListBox->on( Event::OnWidgetFocusLoss, [this]( auto event ) { onPopUpFocusLoss( event ); } );
|
||||
mListBox->on( Event::OnItemSelected, [this]( auto event ) { onItemSelected( event ); } );
|
||||
mListBox->on( Event::OnItemClicked, [this]( auto event ) { onItemClicked( event ); } );
|
||||
mListBox->on( Event::OnItemKeyDown, [this]( auto event ) { onItemKeyDown( event ); } );
|
||||
@@ -95,42 +59,11 @@ Uint32 UIDropDownList::getType() const {
|
||||
}
|
||||
|
||||
bool UIDropDownList::isType( const Uint32& type ) const {
|
||||
return UIDropDownList::getType() == type ? true : UITextInput::isType( type );
|
||||
return UIDropDownList::getType() == type ? true : UIDropDown::isType( type );
|
||||
}
|
||||
|
||||
void UIDropDownList::setTheme( UITheme* Theme ) {
|
||||
UIWidget::setTheme( Theme );
|
||||
|
||||
setThemeSkin( Theme, "dropdownlist" );
|
||||
|
||||
onThemeLoaded();
|
||||
}
|
||||
|
||||
void UIDropDownList::onSizeChange() {
|
||||
onAutoSize();
|
||||
|
||||
UITextInput::onSizeChange();
|
||||
}
|
||||
|
||||
void UIDropDownList::onThemeLoaded() {
|
||||
autoPadding();
|
||||
|
||||
onAutoSize();
|
||||
}
|
||||
|
||||
void UIDropDownList::setFriendNode( UINode* friendNode ) {
|
||||
mFriendNode = friendNode;
|
||||
}
|
||||
|
||||
void UIDropDownList::onAutoSize() {
|
||||
Float max = eemax<Float>( PixelDensity::dpToPxI( getSkinSize().getHeight() ),
|
||||
mTextCache.getLineSpacing() );
|
||||
|
||||
if ( mHeightPolicy == SizePolicy::WrapContent ) {
|
||||
setInternalPixelsHeight( eeceil( max + mPaddingPx.Top + mPaddingPx.Bottom ) );
|
||||
} else if ( ( ( mFlags & UI_AUTO_SIZE ) || 0 == getSize().getHeight() ) && max > 0 ) {
|
||||
setInternalPixelsHeight( eeceil( max ) );
|
||||
}
|
||||
UIWidget* UIDropDownList::getPopUpWidget() const {
|
||||
return mListBox;
|
||||
}
|
||||
|
||||
UIListBox* UIDropDownList::getListBox() const {
|
||||
@@ -152,18 +85,14 @@ Uint32 UIDropDownList::onMouseUp( const Vector2i& Pos, const Uint32& Flags ) {
|
||||
}
|
||||
}
|
||||
|
||||
return UITextInput::onMouseUp( Pos, Flags );
|
||||
return UIDropDown::onMouseUp( Pos, Flags );
|
||||
}
|
||||
|
||||
Uint32 UIDropDownList::onMouseClick( const Vector2i& Pos, const Uint32& Flags ) {
|
||||
if ( ( Flags & EE_BUTTON_LMASK ) && NULL == mFriendNode )
|
||||
showList();
|
||||
Uint32 UIDropDownList::onKeyDown( const KeyEvent& Event ) {
|
||||
if ( NULL != mListBox )
|
||||
mListBox->onKeyDown( Event );
|
||||
|
||||
if ( NULL != mFriendNode ) {
|
||||
UITextInput::onMouseClick( Pos, Flags );
|
||||
}
|
||||
|
||||
return 1;
|
||||
return UIDropDown::onKeyDown( Event );
|
||||
}
|
||||
|
||||
UIDropDownList* UIDropDownList::showList() {
|
||||
@@ -173,85 +102,30 @@ UIDropDownList* UIDropDownList::showList() {
|
||||
if ( !mListBox->isVisible() ) {
|
||||
if ( mListBox->getItemsCount() ) {
|
||||
Rectf tPadding = mListBox->getContainerPadding();
|
||||
|
||||
Float sliderValue = mListBox->getVerticalScrollBar()->getValue();
|
||||
|
||||
Float width =
|
||||
NULL != mFriendNode ? mFriendNode->getSize().getWidth() : getSize().getWidth();
|
||||
Float contentsWidth = eeceil( PixelDensity::pxToDp(
|
||||
mListBox->getMaxTextWidth() +
|
||||
PixelDensity::dpToPx( mListBox->getContainerPadding().getWidth() ) +
|
||||
mListBox->getReferenceItem()->getPixelsPadding().getWidth() +
|
||||
mListBox->getVerticalScrollBar()->getPixelsSize().getWidth() ) );
|
||||
|
||||
bool center = mStyleConfig.menuWidthRule == MenuWidthMode::ContentsCentered ||
|
||||
mStyleConfig.menuWidthRule == MenuWidthMode::ExpandIfNeededCentered;
|
||||
Float width = getPopUpWidth( contentsWidth );
|
||||
|
||||
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->getItemsCount(), mStyleConfig.MaxNumVisibleItems ) *
|
||||
mListBox->getRowHeight() ) +
|
||||
tPadding.Top + tPadding.Bottom +
|
||||
( mListBox->getHorizontalScrollBar() &&
|
||||
mListBox->getHorizontalScrollBar()->isVisible() &&
|
||||
PixelDensity::dpToPx( width ) < mListBox->getMaxTextWidth()
|
||||
? mListBox->getHorizontalScrollBar()->getSize().getHeight()
|
||||
: 0.f ) );
|
||||
Float height =
|
||||
(Int32)( eemin( mListBox->getItemsCount(), mStyleConfig.MaxNumVisibleItems ) *
|
||||
mListBox->getRowHeight() ) +
|
||||
tPadding.Top + tPadding.Bottom +
|
||||
( mListBox->getHorizontalScrollBar() &&
|
||||
mListBox->getHorizontalScrollBar()->isVisible() &&
|
||||
PixelDensity::dpToPx( width ) < mListBox->getMaxTextWidth()
|
||||
? mListBox->getHorizontalScrollBar()->getSize().getHeight()
|
||||
: 0.f );
|
||||
|
||||
mListBox->setSize( width, height );
|
||||
mListBox->getVerticalScrollBar()->setValue( sliderValue );
|
||||
|
||||
if ( !mStyleConfig.PopUpToRoot )
|
||||
mListBox->setParent( getWindowContainer() );
|
||||
else
|
||||
mListBox->setParent( getUISceneNode()->getRoot() );
|
||||
|
||||
mListBox->toFront();
|
||||
|
||||
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 + offsetX, mDpPos.y - mListBox->getSize().getHeight() );
|
||||
}
|
||||
|
||||
if ( mStyleConfig.PopUpToRoot ) {
|
||||
getParent()->nodeToWorld( pos );
|
||||
pos = PixelDensity::pxToDp( pos );
|
||||
} else {
|
||||
Node* parentNode = getParent();
|
||||
Node* rp = getWindowContainer();
|
||||
while ( rp != parentNode ) {
|
||||
pos += parentNode->getPosition();
|
||||
parentNode = parentNode->getParent();
|
||||
}
|
||||
}
|
||||
|
||||
mListBox->setPosition( pos );
|
||||
|
||||
show();
|
||||
|
||||
mListBox->setFocus();
|
||||
alignPopUp( mListBox );
|
||||
}
|
||||
} else {
|
||||
hide();
|
||||
@@ -259,19 +133,6 @@ UIDropDownList* UIDropDownList::showList() {
|
||||
return this;
|
||||
}
|
||||
|
||||
bool UIDropDownList::getPopUpToRoot() const {
|
||||
return mStyleConfig.PopUpToRoot;
|
||||
}
|
||||
|
||||
UIDropDownList* UIDropDownList::setPopUpToRoot( bool popUpToRoot ) {
|
||||
mStyleConfig.PopUpToRoot = popUpToRoot;
|
||||
return this;
|
||||
}
|
||||
|
||||
Uint32 UIDropDownList::getMaxNumVisibleItems() const {
|
||||
return mStyleConfig.MaxNumVisibleItems;
|
||||
}
|
||||
|
||||
UIDropDownList* UIDropDownList::setMaxNumVisibleItems( const Uint32& maxNumVisibleItems ) {
|
||||
if ( maxNumVisibleItems != mStyleConfig.MaxNumVisibleItems ) {
|
||||
mStyleConfig.MaxNumVisibleItems = maxNumVisibleItems;
|
||||
@@ -284,52 +145,6 @@ UIDropDownList* UIDropDownList::setMaxNumVisibleItems( const Uint32& maxNumVisib
|
||||
return this;
|
||||
}
|
||||
|
||||
const UIDropDownList::StyleConfig& UIDropDownList::getStyleConfig() const {
|
||||
return mStyleConfig;
|
||||
}
|
||||
|
||||
UIDropDownList* UIDropDownList::setStyleConfig( const StyleConfig& styleConfig ) {
|
||||
mStyleConfig = styleConfig;
|
||||
|
||||
setMaxNumVisibleItems( mStyleConfig.MaxNumVisibleItems );
|
||||
setPopUpToRoot( mStyleConfig.PopUpToRoot );
|
||||
setMenuWidthMode( mStyleConfig.menuWidthRule );
|
||||
return this;
|
||||
}
|
||||
|
||||
void UIDropDownList::onWidgetClear( const Event* ) {
|
||||
setText( "" );
|
||||
sendCommonEvent( Event::OnClear );
|
||||
}
|
||||
|
||||
void UIDropDownList::onItemKeyDown( const Event* Event ) {
|
||||
const KeyEvent* KEvent = reinterpret_cast<const KeyEvent*>( Event );
|
||||
|
||||
if ( KEvent->getKeyCode() == KEY_RETURN )
|
||||
onItemClicked( Event );
|
||||
else if ( KEvent->getKeyCode() == KEY_ESCAPE ) {
|
||||
hide();
|
||||
setFocus();
|
||||
}
|
||||
}
|
||||
|
||||
void UIDropDownList::onListBoxFocusLoss( const Event* ) {
|
||||
if ( NULL == getEventDispatcher() )
|
||||
return;
|
||||
|
||||
bool frienIsFocus = NULL != mFriendNode && mFriendNode == getEventDispatcher()->getFocusNode();
|
||||
bool isChildFocus = isChild( getEventDispatcher()->getFocusNode() );
|
||||
|
||||
if ( getEventDispatcher()->getFocusNode() != this && !isChildFocus && !frienIsFocus ) {
|
||||
hide();
|
||||
}
|
||||
}
|
||||
|
||||
void UIDropDownList::onItemClicked( const Event* ) {
|
||||
hide();
|
||||
setFocus();
|
||||
}
|
||||
|
||||
void UIDropDownList::onItemSelected( const Event* ) {
|
||||
setText( mListBox->getItemSelectedText() );
|
||||
|
||||
@@ -341,60 +156,6 @@ void UIDropDownList::onItemSelected( const Event* ) {
|
||||
sendCommonEvent( Event::OnSelectionChanged );
|
||||
}
|
||||
|
||||
void UIDropDownList::show() {
|
||||
if ( NULL == mListBox )
|
||||
return;
|
||||
|
||||
mListBox->setEnabled( true );
|
||||
mListBox->setVisible( true );
|
||||
|
||||
if ( NULL != getUISceneNode() &&
|
||||
getUISceneNode()->getUIThemeManager()->getDefaultEffectsEnabled() ) {
|
||||
mListBox->runAction( Actions::Sequence::New(
|
||||
Actions::Fade::New( 255.f == mListBox->getAlpha() ? 0.f : mListBox->getAlpha(), 255.f,
|
||||
getUISceneNode()->getUIThemeManager()->getWidgetsFadeOutTime() ),
|
||||
Actions::Spawn::New( Actions::Enable::New(), Actions::Visible::New( true ) ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
void UIDropDownList::hide() {
|
||||
if ( NULL == mListBox )
|
||||
return;
|
||||
|
||||
if ( NULL != getUISceneNode() &&
|
||||
getUISceneNode()->getUIThemeManager()->getDefaultEffectsEnabled() ) {
|
||||
mListBox->runAction( Actions::Sequence::New(
|
||||
Actions::FadeOut::New( getUISceneNode()->getUIThemeManager()->getWidgetsFadeOutTime() ),
|
||||
Actions::Spawn::New( Actions::Disable::New(), Actions::Visible::New( false ) ) ) );
|
||||
} else {
|
||||
mListBox->setEnabled( false );
|
||||
mListBox->setVisible( false );
|
||||
}
|
||||
}
|
||||
|
||||
Uint32 UIDropDownList::onMouseOver( const Vector2i& position, const Uint32& flags ) {
|
||||
if ( getParent()->isType( UI_TYPE_COMBOBOX ) ) {
|
||||
return UITextInput::onMouseOver( position, flags );
|
||||
} else {
|
||||
return UITextView::onMouseOver( position, flags );
|
||||
}
|
||||
}
|
||||
|
||||
Uint32 UIDropDownList::onMouseLeave( const Vector2i& position, const Uint32& flags ) {
|
||||
if ( getParent()->isType( UI_TYPE_COMBOBOX ) ) {
|
||||
return UITextInput::onMouseLeave( position, flags );
|
||||
} else {
|
||||
return UITextView::onMouseLeave( position, flags );
|
||||
}
|
||||
}
|
||||
|
||||
Uint32 UIDropDownList::onKeyDown( const KeyEvent& Event ) {
|
||||
if ( NULL != mListBox )
|
||||
mListBox->onKeyDown( Event );
|
||||
|
||||
return UITextInput::onKeyDown( Event );
|
||||
}
|
||||
|
||||
void UIDropDownList::destroyListBox() {
|
||||
if ( !SceneManager::instance()->isShuttingDown() && NULL != mListBox &&
|
||||
mListBox->getParent() != this ) {
|
||||
@@ -407,15 +168,6 @@ bool UIDropDownList::applyProperty( const StyleSheetProperty& attribute ) {
|
||||
return false;
|
||||
|
||||
switch ( attribute.getPropertyDefinition()->getPropertyId() ) {
|
||||
case PropertyId::PopUpToRoot:
|
||||
setPopUpToRoot( attribute.asBool() );
|
||||
break;
|
||||
case PropertyId::MaxVisibleItems:
|
||||
setMaxNumVisibleItems( attribute.asUint() );
|
||||
break;
|
||||
case PropertyId::MenuWidthMode:
|
||||
setMenuWidthMode( menuWidthModeFromString( attribute.getValue() ) );
|
||||
break;
|
||||
case PropertyId::SelectedIndex:
|
||||
case PropertyId::SelectedText:
|
||||
case PropertyId::ScrollBarStyle:
|
||||
@@ -427,7 +179,7 @@ bool UIDropDownList::applyProperty( const StyleSheetProperty& attribute ) {
|
||||
else
|
||||
return false;
|
||||
default:
|
||||
return UITextInput::applyProperty( attribute );
|
||||
return UIDropDown::applyProperty( attribute );
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -439,12 +191,6 @@ std::string UIDropDownList::getPropertyString( const PropertyDefinition* propert
|
||||
return "";
|
||||
|
||||
switch ( propertyDef->getPropertyId() ) {
|
||||
case PropertyId::PopUpToRoot:
|
||||
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:
|
||||
@@ -454,18 +200,16 @@ std::string UIDropDownList::getPropertyString( const PropertyDefinition* propert
|
||||
if ( NULL != mListBox )
|
||||
return mListBox->getPropertyString( propertyDef, propertyIndex );
|
||||
default:
|
||||
return UITextInput::getPropertyString( propertyDef, propertyIndex );
|
||||
return UIDropDown::getPropertyString( propertyDef, propertyIndex );
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
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::MenuWidthMode };
|
||||
auto props = UIDropDown::getPropertiesImplemented();
|
||||
auto local = { PropertyId::SelectedIndex, PropertyId::SelectedText, PropertyId::ScrollBarStyle,
|
||||
PropertyId::RowHeight, PropertyId::VScrollMode, PropertyId::HScrollMode };
|
||||
props.insert( props.end(), local.begin(), local.end() );
|
||||
return props;
|
||||
}
|
||||
@@ -476,7 +220,7 @@ void UIDropDownList::loadFromXmlNode( const pugi::xml_node& node ) {
|
||||
if ( NULL != mListBox )
|
||||
mListBox->loadItemsFromXmlNode( node );
|
||||
|
||||
UITextInput::loadFromXmlNode( node );
|
||||
UIDropDown::loadFromXmlNode( node );
|
||||
|
||||
endAttributesTransaction();
|
||||
}
|
||||
@@ -486,13 +230,4 @@ 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
|
||||
|
||||
267
src/eepp/ui/uidropdownmodellist.cpp
Normal file
267
src/eepp/ui/uidropdownmodellist.cpp
Normal file
@@ -0,0 +1,267 @@
|
||||
#include <eepp/scene/actions/actions.hpp>
|
||||
#include <eepp/scene/scenemanager.hpp>
|
||||
#include <eepp/scene/scenenode.hpp>
|
||||
#include <eepp/ui/css/propertydefinition.hpp>
|
||||
#include <eepp/ui/uidropdownmodellist.hpp>
|
||||
#include <eepp/ui/uilistview.hpp>
|
||||
#include <eepp/ui/uiscenenode.hpp>
|
||||
#include <eepp/ui/uiscrollbar.hpp>
|
||||
#include <eepp/ui/uithememanager.hpp>
|
||||
|
||||
#define PUGIXML_HEADER_ONLY
|
||||
#include <pugixml/pugixml.hpp>
|
||||
|
||||
namespace EE { namespace UI {
|
||||
|
||||
UIDropDownModelList* UIDropDownModelList::NewWithTag( const std::string& tag ) {
|
||||
return eeNew( UIDropDownModelList, ( tag ) );
|
||||
}
|
||||
|
||||
UIDropDownModelList* UIDropDownModelList::New() {
|
||||
return eeNew( UIDropDownModelList, () );
|
||||
}
|
||||
|
||||
UIDropDownModelList::UIDropDownModelList( const std::string& tag ) :
|
||||
UIDropDown( tag ), mListView( NULL ) {
|
||||
mListView = static_cast<UIAbstractTableView*>( createDefaultListView() );
|
||||
mListView->setEnabled( false );
|
||||
mListView->setVisible( false );
|
||||
mListView->setParent( this );
|
||||
mListView->setSingleClickNavigation( true );
|
||||
|
||||
mListView->on( Event::OnWidgetFocusLoss, [this]( auto event ) { onPopUpFocusLoss( event ); } );
|
||||
mListView->on( Event::OnModelEvent, [this]( auto event ) { onItemSelected( event ); } );
|
||||
mListView->on( Event::KeyDown, [this]( auto event ) { onItemKeyDown( event ); } );
|
||||
mListView->on( Event::OnClear, [this]( auto event ) { onWidgetClear( event ); } );
|
||||
mListViewCloseCb =
|
||||
mListView->on( Event::OnClose, [this]( const Event* ) { mListView = nullptr; } );
|
||||
|
||||
mListView->setOnSelectionChange( [this]() {
|
||||
if ( !mListView->getSelection().isEmpty() ) {
|
||||
updateSelectionIndex();
|
||||
sendCommonEvent( Event::OnSelectionChanged );
|
||||
}
|
||||
} );
|
||||
|
||||
applyDefaultTheme();
|
||||
}
|
||||
|
||||
UIDropDownModelList::~UIDropDownModelList() {
|
||||
if ( mListView != nullptr && mListViewCloseCb )
|
||||
mListView->removeEventListener( mListViewCloseCb );
|
||||
destroyListView();
|
||||
}
|
||||
|
||||
UIWidget* UIDropDownModelList::createDefaultListView() {
|
||||
return UIListView::NewWithTag( mTag + "::listview" );
|
||||
}
|
||||
|
||||
Uint32 UIDropDownModelList::getType() const {
|
||||
return UI_TYPE_DROPDOWNMODELLIST;
|
||||
}
|
||||
|
||||
bool UIDropDownModelList::isType( const Uint32& type ) const {
|
||||
return UIDropDownModelList::getType() == type ? true : UIDropDown::isType( type );
|
||||
}
|
||||
|
||||
UIWidget* UIDropDownModelList::getPopUpWidget() const {
|
||||
return mListView;
|
||||
}
|
||||
|
||||
UIAbstractTableView* UIDropDownModelList::getListView() const {
|
||||
return mListView;
|
||||
}
|
||||
|
||||
void UIDropDownModelList::setListView( UIAbstractTableView* listView ) {
|
||||
if ( listView == mListView )
|
||||
return;
|
||||
|
||||
if ( mListView != nullptr ) {
|
||||
if ( mListViewCloseCb )
|
||||
mListView->removeEventListener( mListViewCloseCb );
|
||||
mListView->close();
|
||||
}
|
||||
|
||||
mListView = listView;
|
||||
mListView->setEnabled( false );
|
||||
mListView->setVisible( false );
|
||||
mListView->setParent( this );
|
||||
|
||||
mListView->on( Event::OnWidgetFocusLoss, [this]( auto event ) { onPopUpFocusLoss( event ); } );
|
||||
mListView->on( Event::OnModelEvent, [this]( auto event ) { onItemSelected( event ); } );
|
||||
mListView->on( Event::KeyDown, [this]( auto event ) { onItemKeyDown( event ); } );
|
||||
mListView->on( Event::OnClear, [this]( auto event ) { onWidgetClear( event ); } );
|
||||
mListViewCloseCb =
|
||||
mListView->on( Event::OnClose, [this]( const Event* ) { mListView = nullptr; } );
|
||||
|
||||
mListView->setOnSelectionChange( [this]() {
|
||||
if ( !mListView->getSelection().isEmpty() ) {
|
||||
sendCommonEvent( Event::OnSelectionChanged );
|
||||
}
|
||||
} );
|
||||
|
||||
if ( mModel ) {
|
||||
mListView->setModel( mModel );
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Model> UIDropDownModelList::getModel() const {
|
||||
return mModel;
|
||||
}
|
||||
|
||||
void UIDropDownModelList::setModel( std::shared_ptr<Model> model ) {
|
||||
mModel = model;
|
||||
if ( mListView ) {
|
||||
mListView->setModel( mModel );
|
||||
}
|
||||
}
|
||||
|
||||
Uint32 UIDropDownModelList::onMouseUp( const Vector2i& Pos, const Uint32& Flags ) {
|
||||
if ( mEnabled && mVisible && isMouseOver() && NULL != mListView && mModel ) {
|
||||
if ( Flags & EE_BUTTONS_WUWD ) {
|
||||
if ( Flags & EE_BUTTON_WUMASK ) {
|
||||
mListView->moveSelection( -1 );
|
||||
updateSelectionIndex();
|
||||
} else if ( Flags & EE_BUTTON_WDMASK ) {
|
||||
if ( !mListView->getSelection().isEmpty() ) {
|
||||
mListView->moveSelection( 1 );
|
||||
updateSelectionIndex();
|
||||
} else if ( mModel->hasChildren() ) {
|
||||
mListView->getSelection().set( mModel->index( 0, 0 ) );
|
||||
updateSelectionIndex();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return UIDropDown::onMouseUp( Pos, Flags );
|
||||
}
|
||||
|
||||
Uint32 UIDropDownModelList::onKeyDown( const KeyEvent& Event ) {
|
||||
if ( NULL != mListView )
|
||||
mListView->onKeyDown( Event );
|
||||
|
||||
return UIDropDown::onKeyDown( Event );
|
||||
}
|
||||
|
||||
UIDropDownModelList* UIDropDownModelList::showList() {
|
||||
if ( NULL == mListView || NULL == mModel )
|
||||
return this;
|
||||
|
||||
if ( !mListView->isVisible() ) {
|
||||
if ( !mModel->hasChildren() )
|
||||
return this;
|
||||
|
||||
Rectf tPadding = mListView->getPadding();
|
||||
|
||||
Float sliderValue = 0;
|
||||
if ( mListView->getVerticalScrollBar() )
|
||||
sliderValue = mListView->getVerticalScrollBar()->getValue();
|
||||
|
||||
Float contentsWidth = eeceil( PixelDensity::pxToDp(
|
||||
mListView->getMaxColumnContentWidth( 0, true ) +
|
||||
PixelDensity::dpToPx( mListView->getPadding().getWidth() ) +
|
||||
( mListView->getVerticalScrollBar()
|
||||
? mListView->getVerticalScrollBar()->getPixelsSize().getWidth()
|
||||
: 0.f ) ) );
|
||||
|
||||
Float width = getPopUpWidth( contentsWidth );
|
||||
|
||||
Float height =
|
||||
(Int32)( eemin( (Uint32)mModel->rowCount(), mStyleConfig.MaxNumVisibleItems ) *
|
||||
mListView->getRowHeight() ) +
|
||||
tPadding.Top + tPadding.Bottom + mListView->getHeaderHeight() +
|
||||
( mListView->getHorizontalScrollBar() &&
|
||||
mListView->getHorizontalScrollBar()->isVisible()
|
||||
? mListView->getHorizontalScrollBar()->getSize().getHeight()
|
||||
: 0.f );
|
||||
|
||||
mListView->setSize( width, height );
|
||||
|
||||
if ( mListView->getVerticalScrollBar() )
|
||||
mListView->getVerticalScrollBar()->setValue( sliderValue );
|
||||
|
||||
alignPopUp( mListView );
|
||||
} else {
|
||||
hide();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
UIDropDownModelList*
|
||||
UIDropDownModelList::setMaxNumVisibleItems( const Uint32& maxNumVisibleItems ) {
|
||||
if ( maxNumVisibleItems != mStyleConfig.MaxNumVisibleItems ) {
|
||||
mStyleConfig.MaxNumVisibleItems = maxNumVisibleItems;
|
||||
|
||||
if ( NULL != mListView && mModel )
|
||||
mListView->setSize( getSize().getWidth(), std::min( mStyleConfig.MaxNumVisibleItems,
|
||||
(Uint32)mModel->rowCount() ) *
|
||||
mListView->getRowHeight() );
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
void UIDropDownModelList::onItemClicked( const Event* ) {
|
||||
updateSelectionIndex();
|
||||
hide();
|
||||
setFocus();
|
||||
}
|
||||
|
||||
void UIDropDownModelList::updateSelectionIndex() {
|
||||
if ( mListView->getSelection().isEmpty() )
|
||||
return;
|
||||
ModelIndex idx = mListView->getSelection().first();
|
||||
if ( idx.isValid() ) {
|
||||
Variant var = mModel->data( idx, ModelRole::Display );
|
||||
if ( var.isValid() && var.isString() )
|
||||
setText( var.toString() );
|
||||
sendCommonEvent( Event::OnItemSelected );
|
||||
sendCommonEvent( Event::OnValueChange );
|
||||
}
|
||||
}
|
||||
|
||||
void UIDropDownModelList::onItemSelected( const Event* event ) {
|
||||
if ( event->getType() == Event::OnModelEvent ) {
|
||||
const ModelEvent* modelEvent = static_cast<const ModelEvent*>( event );
|
||||
if ( modelEvent->getModelEventType() == ModelEventType::Open ) {
|
||||
updateSelectionIndex();
|
||||
hide();
|
||||
setFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UIDropDownModelList::destroyListView() {
|
||||
if ( !SceneManager::instance()->isShuttingDown() && NULL != mListView &&
|
||||
mListView->getParent() != this ) {
|
||||
mListView->setParent( this );
|
||||
}
|
||||
}
|
||||
|
||||
std::string UIDropDownModelList::getPropertyString( const PropertyDefinition* propertyDef,
|
||||
const Uint32& propertyIndex ) const {
|
||||
if ( NULL == propertyDef )
|
||||
return "";
|
||||
|
||||
std::string res = UIDropDown::getPropertyString( propertyDef, propertyIndex );
|
||||
if ( res.empty() && NULL != mListView ) {
|
||||
res = mListView->getPropertyString( propertyDef, propertyIndex );
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<PropertyId> UIDropDownModelList::getPropertiesImplemented() const {
|
||||
auto props = UIDropDown::getPropertiesImplemented();
|
||||
if ( mListView ) {
|
||||
auto listProps = mListView->getPropertiesImplemented();
|
||||
props.insert( props.end(), listProps.begin(), listProps.end() );
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
void UIDropDownModelList::onClassChange() {
|
||||
if ( mListView )
|
||||
mListView->setClasses( getClasses() );
|
||||
}
|
||||
|
||||
}} // namespace EE::UI
|
||||
@@ -6,7 +6,11 @@ UIListView* UIListView::New() {
|
||||
return eeNew( UIListView, () );
|
||||
}
|
||||
|
||||
UIListView::UIListView() : UITableView( "listview" ) {
|
||||
UIListView* UIListView::NewWithTag( const std::string& tag ) {
|
||||
return eeNew( UIListView, () );
|
||||
}
|
||||
|
||||
UIListView::UIListView( const std::string& tag ) : UITableView( tag ) {
|
||||
setHeadersVisible( false );
|
||||
setAutoExpandOnSingleColumn( true );
|
||||
applyDefaultTheme();
|
||||
|
||||
@@ -14,8 +14,8 @@ UIThemeManager::UIThemeManager() :
|
||||
mThemeDefault( NULL ),
|
||||
mAutoApplyDefaultTheme( true ),
|
||||
mEnableDefaultEffects( false ),
|
||||
mFadeInTime( Milliseconds( 100.f ) ),
|
||||
mFadeOutTime( Milliseconds( 100.f ) ),
|
||||
mFadeInTime( Milliseconds( 25.f ) ),
|
||||
mFadeOutTime( Milliseconds( 25.f ) ),
|
||||
mTooltipTimeToShow( Milliseconds( 400 ) ),
|
||||
mTooltipFollowMouse( false ),
|
||||
mCursorSize( 16, 16 ) {}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <eepp/ui/uicombobox.hpp>
|
||||
#include <eepp/ui/uiconsole.hpp>
|
||||
#include <eepp/ui/uidropdownlist.hpp>
|
||||
#include <eepp/ui/uidropdownmodellist.hpp>
|
||||
#include <eepp/ui/uigridlayout.hpp>
|
||||
#include <eepp/ui/uihtmltable.hpp>
|
||||
#include <eepp/ui/uiimage.hpp>
|
||||
@@ -67,6 +68,7 @@ void UIWidgetCreator::createBaseWidgetList() {
|
||||
registeredWidget["radiobutton"] = UIRadioButton::New;
|
||||
registeredWidget["combobox"] = UIComboBox::New;
|
||||
registeredWidget["dropdownlist"] = UIDropDownList::New;
|
||||
registeredWidget["dropdownmodellist"] = UIDropDownModelList::New;
|
||||
registeredWidget["image"] = UIImage::New;
|
||||
registeredWidget["listbox"] = UIListBox::New;
|
||||
registeredWidget["menubar"] = UIMenuBar::New;
|
||||
|
||||
51
src/examples/ui_dropdownmodellist/ui_dropdownmodellist.cpp
Normal file
51
src/examples/ui_dropdownmodellist/ui_dropdownmodellist.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
#include <eepp/ee.hpp>
|
||||
#include <eepp/ui/models/itemlistmodel.hpp>
|
||||
#include <eepp/ui/uidropdownmodellist.hpp>
|
||||
|
||||
using namespace EE::UI;
|
||||
using namespace EE::UI::Models;
|
||||
|
||||
EE_MAIN_FUNC int main( int, char** ) {
|
||||
UIApplication app( { 640, 480, "eepp - UIDropDownModelList Example" } );
|
||||
app.getUI()->loadLayoutFromString( R"xml(
|
||||
<LinearLayout layout_width="match_parent"
|
||||
layout_height="match_parent"
|
||||
orientation="vertical"
|
||||
padding="16dp">
|
||||
<TextView layout_width="match_parent"
|
||||
layout_height="wrap_content"
|
||||
text="Model-Based Dropdown Example"
|
||||
margin_bottom="16dp" />
|
||||
|
||||
<DropDownModelList id="dropdown"
|
||||
layout_width="250dp"
|
||||
layout_height="wrap_content"
|
||||
pop-up-to-root="true"
|
||||
max-visible-items="4" />
|
||||
</LinearLayout>
|
||||
)xml" );
|
||||
|
||||
if ( !app.getWindow()->isOpen() )
|
||||
return EXIT_FAILURE;
|
||||
|
||||
UIDropDownModelList* dropDown = app.getUI()->find<UIDropDownModelList>( "dropdown" );
|
||||
if ( dropDown ) {
|
||||
std::vector<std::string> options = { "Option 1: OpenGL", "Option 2: Vulkan",
|
||||
"Option 3: Direct3D 11", "Option 4: Direct3D 12",
|
||||
"Option 5: Metal", "Option 6: WebGL",
|
||||
"Option 7: Software" };
|
||||
|
||||
auto model = ItemListOwnerModel<std::string>::create( options );
|
||||
dropDown->setModel( model );
|
||||
dropDown->addEventListener( Event::OnItemSelected, []( const Event* event ) {
|
||||
UIDropDownModelList* dropDown = event->getNode()->asType<UIDropDownModelList>();
|
||||
ModelIndex index = dropDown->getListView()->getSelection().first();
|
||||
if ( index.isValid() ) {
|
||||
String text = dropDown->getListView()->getModel()->data( index ).toString();
|
||||
Log::info( "Selected item index: %d, value: %s", (int)index.row(), text.c_str() );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
return app.run();
|
||||
}
|
||||
43
src/tests/unit_tests/uidropdownmodellist.cpp
Normal file
43
src/tests/unit_tests/uidropdownmodellist.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
#include "utest.h"
|
||||
|
||||
#include <eepp/scene/scenemanager.hpp>
|
||||
#include <eepp/system/filesystem.hpp>
|
||||
#include <eepp/system/sys.hpp>
|
||||
#include <eepp/ui/models/itemlistmodel.hpp>
|
||||
#include <eepp/ui/uiapplication.hpp>
|
||||
#include <eepp/ui/uidropdownmodellist.hpp>
|
||||
#include <eepp/ui/uiscenenode.hpp>
|
||||
#include <eepp/window/engine.hpp>
|
||||
|
||||
using namespace EE;
|
||||
using namespace EE::Window;
|
||||
using namespace EE::Scene;
|
||||
using namespace EE::UI;
|
||||
using namespace EE::UI::Models;
|
||||
|
||||
UTEST( UIDropDownModelList, basicFunctionality ) {
|
||||
UIApplication app(
|
||||
WindowSettings( 800, 600, "eepp - UIDropDownModelList Test", WindowStyle::Default,
|
||||
WindowBackend::Default, 32, {}, 1, false, true ),
|
||||
UIApplication::Settings( Sys::getProcessPath() + ".." + FileSystem::getOSSlash(), 1.5 ) );
|
||||
FileSystem::changeWorkingDirectory( Sys::getProcessPath() );
|
||||
|
||||
UISceneNode* sceneNode = app.getUI();
|
||||
|
||||
UIDropDownModelList* dropDown = UIDropDownModelList::New();
|
||||
dropDown->setParent( sceneNode );
|
||||
|
||||
std::vector<std::string> items = { "Item 1", "Item 2", "Item 3" };
|
||||
auto model = ItemListOwnerModel<std::string>::create( items );
|
||||
dropDown->setModel( model );
|
||||
|
||||
// Model should be set
|
||||
EXPECT_TRUE( dropDown->getModel() == model );
|
||||
|
||||
// Items count should match
|
||||
EXPECT_EQ( dropDown->getListView()->getModel()->rowCount(), 3ul );
|
||||
|
||||
// Max visible items
|
||||
dropDown->setMaxNumVisibleItems( 2 );
|
||||
EXPECT_EQ( dropDown->getMaxNumVisibleItems(), 2ul );
|
||||
}
|
||||
@@ -98,7 +98,10 @@ static std::map<std::string, LLMProvider> parseLLMProviders( const nlohmann::jso
|
||||
model.cacheConfiguration = cache;
|
||||
}
|
||||
|
||||
provider.models.push_back( model );
|
||||
model.hash = hashCombine( std::hash<std::string>()( model.name ),
|
||||
std::hash<std::string>()( model.provider ) );
|
||||
|
||||
provider.models.emplace_back( std::move( model ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,107 @@ using namespace EE::Window;
|
||||
|
||||
namespace ecode {
|
||||
|
||||
class LLMModelsModel : public Model {
|
||||
public:
|
||||
enum Columns { Name, Provider, Hash };
|
||||
|
||||
LLMModelsModel( const std::vector<LLMModel>& models, UISceneNode* uiSceneNode = nullptr ) :
|
||||
mModels( models ), mUISceneNode( uiSceneNode ) {
|
||||
mCurModels.reserve( mModels.size() );
|
||||
for ( const auto& model : mModels ) {
|
||||
mCurModels.emplace_back( &model );
|
||||
}
|
||||
}
|
||||
|
||||
virtual size_t rowCount( const ModelIndex& = ModelIndex() ) const override {
|
||||
return mCurModels.size();
|
||||
}
|
||||
|
||||
virtual size_t columnCount( const ModelIndex& = ModelIndex() ) const override { return 2; }
|
||||
|
||||
virtual std::string columnName( const size_t& column ) const override {
|
||||
switch ( column ) {
|
||||
case Columns::Name:
|
||||
return mUISceneNode ? mUISceneNode->i18n( "name", "Name" ) : "Name";
|
||||
case Columns::Provider:
|
||||
return mUISceneNode ? mUISceneNode->i18n( "provider", "Provider" ) : "Provider";
|
||||
case Columns::Hash:
|
||||
return mUISceneNode ? mUISceneNode->i18n( "hash", "Hash" ) : "Hash";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
virtual Variant data( const ModelIndex& index,
|
||||
ModelRole role = ModelRole::Display ) const override {
|
||||
if ( role != ModelRole::Display )
|
||||
return {};
|
||||
|
||||
if ( index.row() < 0 || static_cast<size_t>( index.row() ) >= mCurModels.size() )
|
||||
return {};
|
||||
|
||||
const auto& model = *mCurModels[index.row()];
|
||||
|
||||
switch ( index.column() ) {
|
||||
case Columns::Name: {
|
||||
if ( model.displayName.has_value() && !model.displayName->empty() ) {
|
||||
return Variant( model.displayName->c_str() );
|
||||
}
|
||||
return Variant( model.name.c_str() );
|
||||
}
|
||||
case Columns::Provider: {
|
||||
return Variant( model.provider.c_str() );
|
||||
}
|
||||
case Columns::Hash: {
|
||||
return Variant( static_cast<Uint64>( model.hash ) );
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
ModelIndex getFromHash( Uint64 hash ) {
|
||||
auto it = std::find_if( mCurModels.begin(), mCurModels.end(),
|
||||
[hash]( const LLMModel* model ) { return hash == model->hash; } );
|
||||
return it != mCurModels.end() ? index( std::distance( mCurModels.begin(), it ) )
|
||||
: index( 0 );
|
||||
}
|
||||
|
||||
void setFilter( const std::string& filter ) {
|
||||
if ( mCurFilter == filter )
|
||||
return;
|
||||
mCurFilter = filter;
|
||||
mCurModels.clear();
|
||||
|
||||
for ( const auto& model : mModels ) {
|
||||
if ( filter.empty() ) {
|
||||
mCurModels.emplace_back( &model );
|
||||
continue;
|
||||
}
|
||||
|
||||
bool matchesName = String::icontains( model.name, filter );
|
||||
bool matchesDisplayName =
|
||||
model.displayName.has_value() && String::icontains( *model.displayName, filter );
|
||||
bool matchesProvider = String::icontains( model.provider, filter );
|
||||
|
||||
if ( matchesName || matchesDisplayName || matchesProvider ) {
|
||||
mCurModels.emplace_back( &model );
|
||||
}
|
||||
}
|
||||
|
||||
invalidate();
|
||||
}
|
||||
|
||||
const std::vector<const LLMModel*>& getCurModels() const { return mCurModels; }
|
||||
|
||||
void refresh() { setFilter( mCurFilter ); }
|
||||
|
||||
protected:
|
||||
const std::vector<LLMModel>& mModels;
|
||||
std::vector<const LLMModel*> mCurModels;
|
||||
std::string mCurFilter;
|
||||
UISceneNode* mUISceneNode;
|
||||
};
|
||||
|
||||
static const char* DEFAULT_PROVIDER = "google";
|
||||
static const char* DEFAULT_MODEL = "gemini-2.5-flash";
|
||||
|
||||
@@ -71,13 +172,7 @@ LLMChat::Role LLMChat::stringToRole( UIPushButton* userBut ) {
|
||||
|
||||
static const char* DEFAULT_LAYOUT = R"xml(
|
||||
<style>
|
||||
.llm_chatui DropDownList {
|
||||
border-color: transparent;
|
||||
background-color: var(--tab-back);
|
||||
}
|
||||
.llm_chatui DropDownList:hover {
|
||||
border-color: var(--primary);
|
||||
}
|
||||
<![CDATA[
|
||||
.llm_conversation {
|
||||
margin-bottom: 8dp;
|
||||
}
|
||||
@@ -137,14 +232,17 @@ DropDownList.role_ui {
|
||||
.llm_chatui .image {
|
||||
tint: var(--font);
|
||||
}
|
||||
.llm_chat_attach {
|
||||
.llm_chat_attach,
|
||||
.llm_chat_select_model {
|
||||
padding: 8dp 8dp 38dp 8dp;
|
||||
}
|
||||
.llm_chat_locate_input {
|
||||
.llm_chat_locate_input,
|
||||
.llm_chat_select_model_input {
|
||||
margin-bottom: 2dp;
|
||||
padding: 0 0 0 4dp;
|
||||
}
|
||||
.llm_chat_attach_locate {
|
||||
.llm_chat_attach_locate,
|
||||
.llm_chat_model_locate {
|
||||
border-radius: 8dp;
|
||||
margin-bottom: 4dp;
|
||||
}
|
||||
@@ -155,6 +253,27 @@ DropDownList.role_ui {
|
||||
background-image: icon(spy-line, 16dp);
|
||||
background-position: top 8dp right 8dp;
|
||||
}
|
||||
.llm_chatui DropDownList,
|
||||
.model_ui {
|
||||
border-color: transparent;
|
||||
background-color: var(--tab-back);
|
||||
}
|
||||
.llm_chatui DropDownList:hover,
|
||||
.model_ui:hover {
|
||||
border-color: var(--primary);
|
||||
}
|
||||
.model_ui {
|
||||
padding-right: 16dp;
|
||||
text-align: left;
|
||||
text-overflow: ellipsis;
|
||||
expand-text: true;
|
||||
foreground-image: url("data:image/svg,<svg viewBox='0 0 24 24' fill='white'><path d='M12 15.6315L20.9679 10.8838L20.0321 9.11619L12 13.3685L3.96788 9.11619L3.0321 10.8838L12 15.6315Z'></path></svg>");
|
||||
foreground-position-x: right 6dp;
|
||||
foreground-position-y: center 1dp;
|
||||
foreground-size: 12dp 16dp;
|
||||
foreground-tint: var(--icon);
|
||||
}
|
||||
]]>
|
||||
</style>
|
||||
<Splitter lw="mp" lh="mp" orientation="vertical" splitter-partition="75%" padding="4dp">
|
||||
<RelativeLayout lw="mp">
|
||||
@@ -172,17 +291,17 @@ DropDownList.role_ui {
|
||||
<TableView lw="mp" lh="0dp" lw8="1" class="llm_chat_attach_locate" />
|
||||
<TextInput class="llm_chat_locate_input" lw="mp" lh="18dp" hint='@string(type_to_locate, "Type to Locate")' />
|
||||
</vboxce>
|
||||
<vboxce class="llm_chat_select_model" lw="mp" lh="mp" visible="false">
|
||||
<TableView lw="mp" lh="0dp" lw8="1" class="llm_chat_model_locate" />
|
||||
<TextInput class="llm_chat_select_model_input" lw="mp" lh="18dp" hint='@string(select_a_model_ellipsis, "Select a model...")' />
|
||||
</vboxce>
|
||||
<hbox lw="mp" lh="wc" layout_gravity="bottom|left" layout_margin="8dp" clip="false">
|
||||
<PushButton id="llm_user" class="llm_button" text="@string(user, User)" tooltip="@string(change_role, Change Role)" min-width="60dp" margin-right="4dp" />
|
||||
<PushButton id="llm_attach" class="llm_button" text="@string(add_context, Add Context)" tooltip="@string(add_context, Add Context)" icon="icon(attach, 14dp)" min-width="32dp" margin-right="4dp" />
|
||||
<PushButton id="llm_chat_history" class="llm_button" text="@string(chat_history, Chat History)" tooltip="@string(chat_history, Chat History)" icon="icon(chat-history, 14dp)" min-width="32dp" margin-right="4dp" />
|
||||
<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="4dp" padding-right="4dp">
|
||||
<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>
|
||||
<PushButton class="model_ui" lw="0" lw8="1" lh="mp" margin-left="4dp" margin-right="4dp" tooltip="@string(select_model, Select Model)" />
|
||||
<PushButton id="llm_settings_but" class="llm_button" text="@string(settings, Settings)" tooltip="@string(settings, Settings)" icon="icon(settings, 14dp)" min-width="32dp" margin-right="4dp" />
|
||||
<!-- <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" /> -->
|
||||
<PushButton id="llm_add_chat" class="llm_button" text="@string(add, Add)" tooltip="@string(add_message, Add Message)" icon="icon(add, 15dp)" min-width="32dp" margin-right="4dp" />
|
||||
<PushButton id="llm_run" class="llm_button primary" text="@string(run, Run)" tooltip="@string(add_message_and_run_prompt, Add Message and Run Prompt)" icon="icon(play-filled, 14dp)" />
|
||||
<PushButton id="llm_stop" class="llm_button primary" text="@string(stop, Stop)" icon="icon(stop, 12dp)" min-width="32dp" visible="false" enabled="false" />
|
||||
@@ -219,10 +338,7 @@ LLMChatUI::LLMChatUI( PluginManager* manager ) :
|
||||
->asType<UISplitter>();
|
||||
|
||||
mChatsList = findByClass( "llm_chats" );
|
||||
mModelDDL = findByClass<UIDropDownList>( "model_ui" );
|
||||
|
||||
// mRefreshModels = find<UIPushButton>( "refresh_model_ui" );
|
||||
// mRefreshModels->onClick( [this]( auto ) { execute( "ai-refresh-local-models" ); } );
|
||||
mModelBtn = findByClass<UIPushButton>( "model_ui" );
|
||||
|
||||
mChatMore = find<UIPushButton>( "llm_more" );
|
||||
mChatMore->onClick( [this]( auto ) { execute( "ai-show-menu" ); } );
|
||||
@@ -276,10 +392,6 @@ LLMChatUI::LLMChatUI( PluginManager* manager ) :
|
||||
mChatUserRole = find<UIPushButton>( "llm_user" );
|
||||
mChatUserRole->onClick( [this]( auto ) { execute( "ai-chat-toggle-role" ); } );
|
||||
|
||||
/* mChatPrivate = find<UISelectButton>( "llm_private_chat" );
|
||||
mChatPrivate->on( Event::OnValueChange,
|
||||
[this]( auto ) { mChatIsPrivate = mChatPrivate->isSelected(); } ); */
|
||||
|
||||
auto setCmd = [this]( const std::string& name, const CommandCallback& cb ) {
|
||||
setCommand( name, cb );
|
||||
mChatInput->getDocument().setCommand( name, cb );
|
||||
@@ -387,9 +499,26 @@ LLMChatUI::LLMChatUI( PluginManager* manager ) :
|
||||
mLocateInput->getDocument().selectAll();
|
||||
} );
|
||||
|
||||
setCmd( "ai-select-model", [this] {
|
||||
if ( !mLocateModelBarLayout->isVisible() ) {
|
||||
if ( mLocateModelTable->getModel() ) {
|
||||
mLocateModelInput->setText( "" );
|
||||
static_cast<LLMModelsModel*>( mLocateModelTable->getModel() )->setFilter( "" );
|
||||
}
|
||||
|
||||
showSelectModel();
|
||||
|
||||
mLocateModelTable->runOnMainThread( [this] {
|
||||
auto model = static_cast<LLMModelsModel*>( mLocateModelTable->getModel() );
|
||||
if ( model )
|
||||
mLocateModelTable->setSelection( model->getFromHash( mCurModel.hash ) );
|
||||
} );
|
||||
} else
|
||||
hideSelectModel();
|
||||
} );
|
||||
|
||||
setCmd( "ai-toggle-private-chat", [this] {
|
||||
mChatIsPrivate = !mChatIsPrivate;
|
||||
/* mChatPrivate->toggleSelection(); */
|
||||
|
||||
if ( mChatIsPrivate )
|
||||
mChatInput->addClass( "incognito" );
|
||||
@@ -533,7 +662,7 @@ LLMChatUI::LLMChatUI( PluginManager* manager ) :
|
||||
} );
|
||||
} );
|
||||
|
||||
setCmd( "ai-refresh-local-models", [this] { fillApiModels( mModelDDL ); } );
|
||||
setCmd( "ai-refresh-local-models", [this] { fillApiModels(); } );
|
||||
|
||||
mChatHistory = find<UIPushButton>( "llm_chat_history" );
|
||||
mChatHistory->onClick( [this]( auto ) { showChatHistory(); } );
|
||||
@@ -541,10 +670,13 @@ LLMChatUI::LLMChatUI( PluginManager* manager ) :
|
||||
mChatAttach = find<UIPushButton>( "llm_attach" );
|
||||
mChatAttach->onClick( [this]( auto ) { execute( "ai-show-add-context-menu" ); } );
|
||||
|
||||
mModelBtn->onClick( [this]( auto ) { execute( "ai-select-model" ); } );
|
||||
|
||||
if ( getPlugin() == nullptr )
|
||||
return;
|
||||
|
||||
initAttachFile();
|
||||
initSelectModel();
|
||||
|
||||
auto providers = getPlugin()->getProviders();
|
||||
setProviders( std::move( providers ) );
|
||||
@@ -566,7 +698,7 @@ LLMChatUI::LLMChatUI( PluginManager* manager ) :
|
||||
}
|
||||
}
|
||||
|
||||
fillModelDropDownList( mModelDDL );
|
||||
fillModelDropDownList();
|
||||
|
||||
const auto appendShortcutToTooltip = [this]( UIPushButton* but, const std::string& cmd ) {
|
||||
auto kb = getKeyBindings().getCommandKeybindString( cmd );
|
||||
@@ -583,10 +715,9 @@ LLMChatUI::LLMChatUI( PluginManager* manager ) :
|
||||
appendShortcutToTooltip( mChatStop, "ai-prompt" );
|
||||
appendShortcutToTooltip( mChatAdd, "ai-add-chat" );
|
||||
appendShortcutToTooltip( mChatSettings, "ai-settings" );
|
||||
// appendShortcutToTooltip( mChatPrivate, "ai-toggle-private-chat" );
|
||||
appendShortcutToTooltip( mChatMore, "ai-show-menu" );
|
||||
appendShortcutToTooltip( mChatUserRole, "ai-chat-toggle-role" );
|
||||
// appendShortcutToTooltip( mRefreshModels, "ai-refresh-local-models" );
|
||||
appendShortcutToTooltip( mModelBtn, "ai-select-model" );
|
||||
|
||||
addKb( mChatInput, "mod+keypad enter", "ai-prompt", true, false );
|
||||
addKb( mChatInput, "mod+shift+keypad enter", "ai-add-chat", true, false );
|
||||
@@ -643,6 +774,7 @@ void LLMChatUI::bindCmds( UICodeEditor* editor, bool bindToChatUI ) {
|
||||
addKb( editor, "mod+shift+l", "ai-refresh-local-models", bindToChatUI );
|
||||
addKb( editor, "mod+shift+a", "ai-attach-file", bindToChatUI );
|
||||
addKb( editor, "mod+shift+z", "ai-link-file", bindToChatUI );
|
||||
addKb( editor, "mod+shift+x", "ai-select-model", bindToChatUI );
|
||||
|
||||
if ( bindToChatUI )
|
||||
addKb( editor, "mod+shift+return", "ai-add-chat", bindToChatUI );
|
||||
@@ -663,6 +795,14 @@ std::optional<LLMModel> LLMChatUI::getModel( const std::string& provider,
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<LLMModel> LLMChatUI::getModel( Uint64 hash ) {
|
||||
auto modelIt = std::find_if( mModels.begin(), mModels.end(),
|
||||
[hash]( const LLMModel& model ) { return hash == model.hash; } );
|
||||
if ( modelIt != mModels.end() )
|
||||
return *modelIt;
|
||||
return {};
|
||||
}
|
||||
|
||||
void LLMChatUI::showChatHistory() {
|
||||
auto plugin = getPlugin();
|
||||
if ( plugin == nullptr )
|
||||
@@ -832,8 +972,10 @@ void LLMChatUI::showChatHistory() {
|
||||
} );
|
||||
}
|
||||
|
||||
void LLMChatUI::fillApiModels( UIDropDownList* modelDDL ) {
|
||||
void LLMChatUI::fillApiModels() {
|
||||
mPendingModelsToLoad = 0;
|
||||
mNewModels.clear();
|
||||
|
||||
for ( auto& [name, data] : mProviders ) {
|
||||
if ( !data.enabled || !data.fetchModelsUrl )
|
||||
continue;
|
||||
@@ -869,6 +1011,8 @@ void LLMChatUI::fillApiModels( UIDropDownList* modelDDL ) {
|
||||
model.displayName = el.value( "display_name", "" );
|
||||
|
||||
model.isEphemeral = true;
|
||||
model.hash = hashCombine( std::hash<std::string>()( model.name ),
|
||||
std::hash<std::string>()( model.provider ) );
|
||||
|
||||
if ( model.name.empty() )
|
||||
continue;
|
||||
@@ -877,39 +1021,25 @@ void LLMChatUI::fillApiModels( UIDropDownList* modelDDL ) {
|
||||
model.maxOutputTokens = el.value( "max_context_length", 0 );
|
||||
|
||||
data.models.emplace_back( model );
|
||||
mNewModels.push_back( model );
|
||||
}
|
||||
|
||||
mPendingModelsToLoad++;
|
||||
|
||||
std::string pname = name;
|
||||
modelDDL->runOnMainThread( [pname = std::move( pname ), modelDDL, this] {
|
||||
String providerName( pname );
|
||||
std::vector<String> removeValues;
|
||||
size_t count = modelDDL->getListBox()->getItemsCount();
|
||||
for ( size_t i = 0; i < count; i++ ) {
|
||||
const String& txt = modelDDL->getListBox()->getItemText( i );
|
||||
if ( txt.contains( providerName ) )
|
||||
removeValues.emplace_back( txt );
|
||||
}
|
||||
|
||||
for ( const auto& val : removeValues )
|
||||
modelDDL->getListBox()->removeListBoxItem( val );
|
||||
|
||||
const auto& models = mProviders[pname].models;
|
||||
std::vector<String> newModels;
|
||||
for ( const auto& model : models ) {
|
||||
if ( !model.isEphemeral )
|
||||
continue;
|
||||
newModels.emplace_back( String::format( "%s (%s)", model.name, pname ) );
|
||||
mModelsMap[newModels[newModels.size() - 1].getHash()] = model;
|
||||
}
|
||||
|
||||
modelDDL->getListBox()->addListBoxItems( newModels );
|
||||
|
||||
runOnMainThread( [this] {
|
||||
mPendingModelsToLoad--;
|
||||
if ( mPendingModelsToLoad == 0 ) {
|
||||
mModels.erase(
|
||||
std::remove_if( mModels.begin(), mModels.end(),
|
||||
[]( const LLMModel& model ) { return model.isEphemeral; } ),
|
||||
mModels.end() );
|
||||
|
||||
if ( mPendingModelsToLoad == 0 )
|
||||
mModels.insert( mModels.end(), mNewModels.begin(), mNewModels.end() );
|
||||
|
||||
if ( mLocateModelTable && mLocateModelTable->getModel() )
|
||||
loadSelectModel();
|
||||
onInit();
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
@@ -926,46 +1056,28 @@ String LLMChatUI::getModelDisplayName( const LLMModel& model ) const {
|
||||
data.displayName ? *data.displayName : String::capitalize( data.name ) );
|
||||
}
|
||||
|
||||
bool LLMChatUI::selectModel( UIDropDownList* modelDDL, const LLMModel& model ) {
|
||||
auto modelName = getModelDisplayName( model );
|
||||
auto index = modelDDL->getListBox()->getItemIndex( modelName );
|
||||
if ( index != eeINDEX_NOT_FOUND ) {
|
||||
modelDDL->getListBox()->setSelected( index );
|
||||
bool LLMChatUI::selectModel( std::optional<LLMModel> model ) {
|
||||
if ( model ) {
|
||||
mModelBtn->setText( getModelDisplayName( *model ) );
|
||||
mCurModel = *model;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void LLMChatUI::fillModelDropDownList( UIDropDownList* modelDDL ) {
|
||||
std::vector<String> models;
|
||||
std::size_t selectedIndex = 0;
|
||||
void LLMChatUI::fillModelDropDownList() {
|
||||
mModels.clear();
|
||||
std::size_t reserve = 0;
|
||||
for ( const auto& [_, data] : mProviders )
|
||||
reserve += data.models.size();
|
||||
mModels.reserve( reserve + 8 /* extra space for local models */ );
|
||||
for ( const auto& [name, data] : mProviders ) {
|
||||
if ( !data.enabled )
|
||||
continue;
|
||||
|
||||
for ( const auto& model : data.models ) {
|
||||
String modelName( String::format(
|
||||
"%s (%s)", model.displayName ? *model.displayName : model.name,
|
||||
data.displayName ? *data.displayName : String::capitalize( data.name ) ) );
|
||||
mModelsMap[modelName.getHash()] = model;
|
||||
if ( model.provider == mCurModel.provider && model.name == mCurModel.name )
|
||||
selectedIndex = models.size();
|
||||
models.push_back( std::move( modelName ) );
|
||||
}
|
||||
for ( const auto& model : data.models )
|
||||
mModels.push_back( model );
|
||||
}
|
||||
modelDDL->getListBox()->clear();
|
||||
modelDDL->getListBox()->addListBoxItems( std::move( models ) );
|
||||
modelDDL->getListBox()->setSelected( selectedIndex );
|
||||
modelDDL->on( Event::OnValueChange, [this, modelDDL]( auto ) {
|
||||
auto selectedModel =
|
||||
mModelsMap.find( modelDDL->getListBox()->getItemSelectedText().getHash() );
|
||||
if ( selectedModel != mModelsMap.end() ) {
|
||||
mCurModel = selectedModel->second;
|
||||
}
|
||||
} );
|
||||
|
||||
modelDDL->getUISceneNode()->getThreadPool()->run(
|
||||
[this, modelDDL] { fillApiModels( modelDDL ); } );
|
||||
getUISceneNode()->getThreadPool()->run( [this] { fillApiModels(); } );
|
||||
}
|
||||
|
||||
void LLMChatUI::resizeToFit( UICodeEditor* editor ) {
|
||||
@@ -1077,8 +1189,8 @@ std::string LLMChatUI::unserialize( const nlohmann::json& payload ) {
|
||||
if ( mCurModel.name.empty() )
|
||||
return payload.value( "input", "" );
|
||||
|
||||
if ( !selectModel( mModelDDL, mCurModel ) )
|
||||
fillModelDropDownList( mModelDDL );
|
||||
if ( !selectModel( mCurModel ) )
|
||||
fillModelDropDownList();
|
||||
|
||||
if ( payload.contains( "chat" ) && payload["chat"].is_object() ) {
|
||||
const auto& chat = payload["chat"];
|
||||
@@ -1488,10 +1600,10 @@ const LLMModel& LLMChatUI::getCheapestModelFromCurrentProvider() const {
|
||||
}
|
||||
|
||||
void LLMChatUI::onInit() {
|
||||
if ( !mModelDDL )
|
||||
if ( !mModelBtn )
|
||||
return;
|
||||
if ( getModelDisplayName( mCurModel ) != mModelDDL->getListBox()->getItemSelectedText() )
|
||||
selectModel( mModelDDL, mCurModel );
|
||||
if ( getModelDisplayName( mCurModel ) != mModelBtn->getText() )
|
||||
selectModel( mCurModel );
|
||||
}
|
||||
|
||||
void LLMChatUI::updateTabTitle() {
|
||||
@@ -1549,9 +1661,12 @@ void LLMChatUI::updateLocateBarColumns() {
|
||||
mLocateTable->setColumnWidth( 1, width - mLocateTable->getColumnWidth( 0 ) );
|
||||
}
|
||||
|
||||
// File picker
|
||||
|
||||
void LLMChatUI::showAttachFile() {
|
||||
if ( getPlugin() == nullptr )
|
||||
return;
|
||||
hideSelectModel();
|
||||
auto text = mLocateInput->getText();
|
||||
auto ctx = getPlugin()->getPluginContext();
|
||||
if ( !ctx->isDirTreeReady() ) {
|
||||
@@ -1687,4 +1802,90 @@ void LLMChatUI::initAttachFile() {
|
||||
} );
|
||||
}
|
||||
|
||||
// Model Picker
|
||||
|
||||
void LLMChatUI::updateLocateModelBarColumns() {
|
||||
Float width = eeceil( mLocateModelTable->getPixelsSize().getWidth() );
|
||||
width -= mLocateModelTable->getVerticalScrollBar()->getPixelsSize().getWidth();
|
||||
mLocateModelTable->setColumnsVisible( { 0, 1 } );
|
||||
mLocateModelTable->setColumnWidth( 0, eeceil( width * 0.8 ) );
|
||||
mLocateModelTable->setColumnWidth( 1, width - mLocateModelTable->getColumnWidth( 0 ) );
|
||||
}
|
||||
|
||||
void LLMChatUI::loadSelectModel() {
|
||||
auto ctx = getPlugin()->getPluginContext();
|
||||
mLocateModelTable->setModel(
|
||||
std::make_shared<LLMModelsModel>( mModels, ctx->getUISceneNode() ) );
|
||||
|
||||
static_cast<LLMModelsModel*>( mLocateModelTable->getModel() )
|
||||
->setFilter( mLocateModelInput->getText() );
|
||||
}
|
||||
|
||||
void LLMChatUI::showSelectModel() {
|
||||
if ( getPlugin() == nullptr )
|
||||
return;
|
||||
hideAttachFile();
|
||||
|
||||
if ( nullptr == mLocateModelTable->getModel() )
|
||||
loadSelectModel();
|
||||
|
||||
static_cast<LLMModelsModel*>( mLocateModelTable->getModel() )
|
||||
->setFilter( mLocateModelInput->getText() );
|
||||
|
||||
mLocateModelBarLayout->setVisible( true );
|
||||
mLocateModelInput->setFocus();
|
||||
updateLocateModelBarColumns();
|
||||
}
|
||||
|
||||
void LLMChatUI::hideSelectModel() {
|
||||
mLocateModelBarLayout->setVisible( false );
|
||||
}
|
||||
|
||||
void LLMChatUI::initSelectModel() {
|
||||
mLocateModelBarLayout = findByClass<UIVLinearLayoutCommandExecuter>( "llm_chat_select_model" );
|
||||
mLocateModelInput = findByClass<UITextInput>( "llm_chat_select_model_input" );
|
||||
mLocateModelTable = findByClass<UITableView>( "llm_chat_model_locate" );
|
||||
mLocateModelTable->setHeadersVisible( false );
|
||||
|
||||
mLocateModelTable->on( Event::OnSizeChange,
|
||||
[this]( const Event* ) { updateLocateModelBarColumns(); } );
|
||||
|
||||
mLocateModelInput->on( Event::OnTextChanged, [this]( const Event* ) {
|
||||
showSelectModel();
|
||||
updateLocateModelBarColumns();
|
||||
} );
|
||||
mLocateModelInput->on( Event::OnPressEnter, [this]( const Event* ) {
|
||||
KeyEvent keyEvent( mLocateModelTable, Event::KeyDown, KEY_RETURN, SCANCODE_UNKNOWN, 0, 0 );
|
||||
mLocateModelTable->forceKeyDown( keyEvent );
|
||||
} );
|
||||
mLocateModelInput->on( Event::KeyDown, [this]( const Event* event ) {
|
||||
const KeyEvent* keyEvent = static_cast<const KeyEvent*>( event );
|
||||
mLocateModelTable->forceKeyDown( *keyEvent );
|
||||
} );
|
||||
mLocateModelBarLayout->setCommand( "close-locatebar", [this] {
|
||||
hideSelectModel();
|
||||
if ( mChatInput )
|
||||
mChatInput->setFocus();
|
||||
} );
|
||||
mLocateModelBarLayout->getKeyBindings().addKeybindsString( {
|
||||
{ "escape", "close-locatebar" },
|
||||
} );
|
||||
mLocateModelTable->on( Event::KeyDown, [this]( const Event* event ) {
|
||||
const KeyEvent* keyEvent = static_cast<const KeyEvent*>( event );
|
||||
if ( keyEvent->getKeyCode() == KEY_ESCAPE )
|
||||
mLocateModelBarLayout->execute( "close-locatebar" );
|
||||
} );
|
||||
mLocateModelTable->on( Event::OnModelEvent, [this]( const Event* event ) {
|
||||
const ModelEvent* modelEvent = static_cast<const ModelEvent*>( event );
|
||||
if ( modelEvent->getModelEventType() == ModelEventType::Open ) {
|
||||
Variant vHash( modelEvent->getModel()->data(
|
||||
modelEvent->getModel()->index( modelEvent->getModelIndex().row(),
|
||||
LLMModelsModel::Hash ),
|
||||
ModelRole::Display ) );
|
||||
selectModel( getModel( vHash.asUint64() ) );
|
||||
mLocateModelBarLayout->execute( "close-locatebar" );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
} // namespace ecode
|
||||
|
||||
@@ -110,20 +110,28 @@ class LLMChatUI : public UILinearLayout, public WidgetCommandExecuter {
|
||||
UIPushButton* mChatAttach{ nullptr };
|
||||
UISelectButton* mChatPrivate{ nullptr };
|
||||
UIScrollView* mChatScrollView{ nullptr };
|
||||
UIDropDownList* mModelDDL{ nullptr };
|
||||
UIPushButton* mModelBtn{ nullptr };
|
||||
|
||||
// Locate file
|
||||
UIVLinearLayoutCommandExecuter* mLocateBarLayout{ nullptr };
|
||||
UITextInput* mLocateInput{ nullptr };
|
||||
UITableView* mLocateTable{ nullptr };
|
||||
|
||||
// Select model
|
||||
UIVLinearLayoutCommandExecuter* mLocateModelBarLayout{ nullptr };
|
||||
UITextInput* mLocateModelInput{ nullptr };
|
||||
UITableView* mLocateModelTable{ nullptr };
|
||||
|
||||
std::unique_ptr<LLMChatCompletionRequest> mRequest;
|
||||
std::unique_ptr<LLMChatCompletionRequest> mSummaryRequest;
|
||||
LLMProviders mProviders;
|
||||
LLMModel mCurModel;
|
||||
std::unordered_map<String::HashType, LLMModel> mModelsMap;
|
||||
std::vector<LLMModel> mModels;
|
||||
int mPendingModelsToLoad{ 0 };
|
||||
bool mChatIsPrivate{ false };
|
||||
bool mChatLocked{ false };
|
||||
bool mLinkMode{ false };
|
||||
std::vector<LLMModel> mNewModels;
|
||||
|
||||
LLMModel findModel( const std::string& provider, const std::string& model );
|
||||
|
||||
@@ -151,13 +159,13 @@ class LLMChatUI : public UILinearLayout, public WidgetCommandExecuter {
|
||||
|
||||
UIWidget* addChatUI( LLMChat::Role role );
|
||||
|
||||
void fillApiModels( UIDropDownList* modelDDL );
|
||||
void fillApiModels();
|
||||
|
||||
String getModelDisplayName( const LLMModel& model ) const;
|
||||
|
||||
bool selectModel( UIDropDownList* modelDDL, const LLMModel& model );
|
||||
bool selectModel( std::optional<LLMModel> model );
|
||||
|
||||
void fillModelDropDownList( UIDropDownList* modelDDL );
|
||||
void fillModelDropDownList();
|
||||
|
||||
void resizeToFit( UICodeEditor* editor );
|
||||
|
||||
@@ -173,6 +181,8 @@ class LLMChatUI : public UILinearLayout, public WidgetCommandExecuter {
|
||||
|
||||
std::optional<LLMModel> getModel( const std::string& provider, const std::string& modelName );
|
||||
|
||||
std::optional<LLMModel> getModel( Uint64 hash );
|
||||
|
||||
void saveChat();
|
||||
|
||||
void onInit();
|
||||
@@ -188,12 +198,22 @@ class LLMChatUI : public UILinearLayout, public WidgetCommandExecuter {
|
||||
|
||||
void initAttachFile();
|
||||
|
||||
void initSelectModel();
|
||||
|
||||
void updateLocateBarColumns();
|
||||
|
||||
void showAttachFile();
|
||||
|
||||
void hideAttachFile();
|
||||
|
||||
void updateLocateModelBarColumns();
|
||||
|
||||
void loadSelectModel();
|
||||
|
||||
void showSelectModel();
|
||||
|
||||
void hideSelectModel();
|
||||
|
||||
void insertFileToDocument( std::string path, std::shared_ptr<TextDocument> cdoc );
|
||||
|
||||
void replaceFileLinksToContents( std::string& text );
|
||||
|
||||
@@ -14,6 +14,7 @@ struct LLMCacheConfiguration {
|
||||
};
|
||||
|
||||
struct LLMModel {
|
||||
std::size_t hash{ 0 };
|
||||
std::string name;
|
||||
std::string provider;
|
||||
std::optional<std::string> displayName;
|
||||
|
||||
@@ -217,16 +217,20 @@ void StatusBuildOutputController::runBuild( const std::string& buildName,
|
||||
}
|
||||
|
||||
if ( enableBuildButton && buildButton )
|
||||
buildButton->setEnabled( true );
|
||||
buildButton->ensureMainThread( [buildButton] { buildButton->setEnabled( true ); } );
|
||||
|
||||
if ( enableCleanButton && cleanButton )
|
||||
cleanButton->runOnMainThread( [cleanButton] { cleanButton->setEnabled( true ); } );
|
||||
cleanButton->ensureMainThread( [cleanButton] { cleanButton->setEnabled( true ); } );
|
||||
|
||||
if ( buildAndRunButton )
|
||||
buildAndRunButton->setEnabled( true );
|
||||
if ( buildAndRunButton ) {
|
||||
buildAndRunButton->ensureMainThread(
|
||||
[buildAndRunButton] { buildAndRunButton->setEnabled( true ); } );
|
||||
}
|
||||
|
||||
mBuildButton->setEnabled( true );
|
||||
mStopButton->setEnabled( false );
|
||||
mBuildButton->ensureMainThread( [this] {
|
||||
mBuildButton->setEnabled( true );
|
||||
mStopButton->setEnabled( false );
|
||||
} );
|
||||
};
|
||||
|
||||
auto res = pbm->build(
|
||||
|
||||
Reference in New Issue
Block a user