mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-10-02 03:00:23 +03:00
Merge branch 'develop' into feature/sdl3
This commit is contained in:
@@ -369,6 +369,12 @@
|
|||||||
"command": "${project_root}/bin/eepp-ui-markdownview-debug",
|
"command": "${project_root}/bin/eepp-ui-markdownview-debug",
|
||||||
"name": "eepp-ui-markdownview-debug",
|
"name": "eepp-ui-markdownview-debug",
|
||||||
"working_dir": "${project_root}/bin"
|
"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": {
|
"var": {
|
||||||
|
|||||||
@@ -469,6 +469,10 @@
|
|||||||
{
|
{
|
||||||
"name": "arcee-ai/trinity-large-preview:free",
|
"name": "arcee-ai/trinity-large-preview:free",
|
||||||
"display_name": "Arcee AI: Trinity Large Preview (free)"
|
"display_name": "Arcee AI: Trinity Large Preview (free)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nvidia/nemotron-3-super-120b-a12b:free",
|
||||||
|
"display_name": "NVIDIA: Nemotron 3 Super (free)"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -244,7 +244,8 @@ Anchor:hover {
|
|||||||
|
|
||||||
PushButton,
|
PushButton,
|
||||||
SelectButton,
|
SelectButton,
|
||||||
DropDownList {
|
DropDownList,
|
||||||
|
DropDownModelList {
|
||||||
padding-left: var(--base-horizontal-padding);
|
padding-left: var(--base-horizontal-padding);
|
||||||
padding-right: var(--base-horizontal-padding);
|
padding-right: var(--base-horizontal-padding);
|
||||||
padding-top: var(--base-vertical-padding);
|
padding-top: var(--base-vertical-padding);
|
||||||
@@ -259,6 +260,7 @@ DropDownList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
DropDownList,
|
DropDownList,
|
||||||
|
DropDownModelList,
|
||||||
ComboBox::DropDownList {
|
ComboBox::DropDownList {
|
||||||
max-visible-items: 6;
|
max-visible-items: 6;
|
||||||
}
|
}
|
||||||
@@ -267,6 +269,8 @@ PushButton:hover,
|
|||||||
PushButton:focus,
|
PushButton:focus,
|
||||||
DropDownList:hover,
|
DropDownList:hover,
|
||||||
DropDownList:focus,
|
DropDownList:focus,
|
||||||
|
DropDownModelList:hover,
|
||||||
|
DropDownModelList:focus,
|
||||||
SelectButton:hover,
|
SelectButton:hover,
|
||||||
SelectButton:focus,
|
SelectButton:focus,
|
||||||
ComboBox:hover,
|
ComboBox:hover,
|
||||||
@@ -800,12 +804,14 @@ Window::border::bottom {
|
|||||||
background-color: var(--separator);
|
background-color: var(--separator);
|
||||||
}
|
}
|
||||||
|
|
||||||
DropDownList {
|
DropDownList,
|
||||||
|
DropDownModelList {
|
||||||
padding-right: 16dp;
|
padding-right: 16dp;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
DropDownList,
|
DropDownList,
|
||||||
|
DropDownModelList,
|
||||||
ComboBox::Button {
|
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-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-x: right 6dp;
|
||||||
@@ -816,6 +822,8 @@ ComboBox::Button {
|
|||||||
|
|
||||||
DropDownList:hover,
|
DropDownList:hover,
|
||||||
DropDownList:focus,
|
DropDownList:focus,
|
||||||
|
DropDownModelList:hover,
|
||||||
|
DropDownModelList:focus,
|
||||||
ComboBox::Button:focus,
|
ComboBox::Button:focus,
|
||||||
ComboBox::Button:hover {
|
ComboBox::Button:hover {
|
||||||
foreground-tint: var(--icon-active);
|
foreground-tint: var(--icon-active);
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
#ifndef EE_CONTAINERS_HPP
|
#ifndef EE_CONTAINERS_HPP
|
||||||
#define EE_CONTAINERS_HPP
|
#define EE_CONTAINERS_HPP
|
||||||
|
|
||||||
#ifdef EEPP_NO_THIRDPARTY_CONTAINERS
|
#include <eepp/config.hpp>
|
||||||
|
|
||||||
|
#if defined( EEPP_NO_THIRDPARTY_CONTAINERS ) || ( defined( EE_DEBUG ) && defined( EE_COMPILER_MSVC ) )
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#else
|
#else
|
||||||
@@ -11,7 +13,7 @@
|
|||||||
|
|
||||||
namespace EE {
|
namespace EE {
|
||||||
|
|
||||||
#ifdef EEPP_NO_THIRDPARTY_CONTAINERS
|
#if defined( EEPP_NO_THIRDPARTY_CONTAINERS ) || ( defined( EE_DEBUG ) && defined( EE_COMPILER_MSVC ) )
|
||||||
|
|
||||||
template <typename Key, typename Value> using UnorderedMap = std::unordered_map<Key, Value>;
|
template <typename Key, typename Value> using UnorderedMap = std::unordered_map<Key, Value>;
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ using namespace EE::Math;
|
|||||||
namespace EE { namespace UI {
|
namespace EE { namespace UI {
|
||||||
class UIPushButton;
|
class UIPushButton;
|
||||||
class UILinearLayout;
|
class UILinearLayout;
|
||||||
|
class UIDropDownModelList;
|
||||||
}} // namespace EE::UI
|
}} // namespace EE::UI
|
||||||
|
|
||||||
namespace EE { namespace UI { namespace Abstract {
|
namespace EE { namespace UI { namespace Abstract {
|
||||||
@@ -146,6 +147,7 @@ class EE_API UIAbstractTableView : public UIAbstractView {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend class EE::UI::UITableHeaderColumn;
|
friend class EE::UI::UITableHeaderColumn;
|
||||||
|
friend class EE::UI::UIDropDownModelList;
|
||||||
|
|
||||||
struct ColumnData {
|
struct ColumnData {
|
||||||
Float minWidth{ 0 };
|
Float minWidth{ 0 };
|
||||||
|
|||||||
@@ -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
|
#ifndef EE_UICUIDROPDOWNLIST_HPP
|
||||||
#define EE_UICUIDROPDOWNLIST_HPP
|
#define EE_UICUIDROPDOWNLIST_HPP
|
||||||
|
|
||||||
|
#include <eepp/ui/uidropdown.hpp>
|
||||||
#include <eepp/ui/uilistbox.hpp>
|
#include <eepp/ui/uilistbox.hpp>
|
||||||
#include <eepp/ui/uitextinput.hpp>
|
|
||||||
|
|
||||||
namespace EE { namespace UI {
|
namespace EE { namespace UI {
|
||||||
|
|
||||||
class EE_API UIDropDownList : public UITextInput {
|
class EE_API UIDropDownList : public UIDropDown {
|
||||||
public:
|
public:
|
||||||
enum class MenuWidthMode {
|
using MenuWidthMode = UIDropDown::MenuWidthMode;
|
||||||
DropDown,
|
using StyleConfig = UIDropDown::StyleConfig;
|
||||||
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 };
|
|
||||||
};
|
|
||||||
|
|
||||||
static UIDropDownList* NewWithTag( const std::string& tag );
|
static UIDropDownList* NewWithTag( const std::string& tag );
|
||||||
|
|
||||||
@@ -33,84 +18,39 @@ class EE_API UIDropDownList : public UITextInput {
|
|||||||
virtual ~UIDropDownList();
|
virtual ~UIDropDownList();
|
||||||
|
|
||||||
virtual Uint32 getType() const;
|
virtual Uint32 getType() const;
|
||||||
|
|
||||||
virtual bool isType( const Uint32& type ) const;
|
virtual bool isType( const Uint32& type ) const;
|
||||||
|
|
||||||
virtual void setTheme( UITheme* Theme );
|
|
||||||
|
|
||||||
UIListBox* getListBox() const;
|
UIListBox* getListBox() const;
|
||||||
|
|
||||||
UIDropDownList* showList();
|
UIDropDownList* showList();
|
||||||
|
|
||||||
bool getPopUpToRoot() const;
|
virtual UIDropDownList* setMaxNumVisibleItems( const Uint32& maxNumVisibleItems );
|
||||||
|
|
||||||
UIDropDownList* setPopUpToRoot( bool popUpToRoot );
|
|
||||||
|
|
||||||
Uint32 getMaxNumVisibleItems() const;
|
|
||||||
|
|
||||||
UIDropDownList* setMaxNumVisibleItems( const Uint32& maxNumVisibleItems );
|
|
||||||
|
|
||||||
const StyleConfig& getStyleConfig() const;
|
|
||||||
|
|
||||||
UIDropDownList* setStyleConfig( const StyleConfig& styleConfig );
|
|
||||||
|
|
||||||
virtual bool applyProperty( const StyleSheetProperty& attribute );
|
virtual bool applyProperty( const StyleSheetProperty& attribute );
|
||||||
|
|
||||||
virtual std::string getPropertyString( const PropertyDefinition* propertyDef,
|
virtual std::string getPropertyString( const PropertyDefinition* propertyDef,
|
||||||
const Uint32& propertyIndex = 0 ) const;
|
const Uint32& propertyIndex = 0 ) const;
|
||||||
|
|
||||||
virtual std::vector<PropertyId> getPropertiesImplemented() const;
|
virtual std::vector<PropertyId> getPropertiesImplemented() const;
|
||||||
|
|
||||||
virtual void loadFromXmlNode( const pugi::xml_node& node );
|
virtual void loadFromXmlNode( const pugi::xml_node& node );
|
||||||
|
|
||||||
UIDropDownList* setMenuWidthMode( MenuWidthMode rule );
|
|
||||||
|
|
||||||
MenuWidthMode getMenuWidthMode() const;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend class UIComboBox;
|
friend class UIComboBox;
|
||||||
|
|
||||||
StyleConfig mStyleConfig;
|
|
||||||
UIListBox* mListBox;
|
UIListBox* mListBox;
|
||||||
UINode* mFriendNode;
|
|
||||||
Uint32 mListBoxCloseCb{ 0 };
|
Uint32 mListBoxCloseCb{ 0 };
|
||||||
|
|
||||||
UIDropDownList( const std::string& tag = "dropdownlist" );
|
UIDropDownList( const std::string& tag = "dropdownlist" );
|
||||||
|
|
||||||
void onListBoxFocusLoss( const Event* Event );
|
virtual UIWidget* getPopUpWidget() const;
|
||||||
|
|
||||||
virtual void onItemSelected( 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 onMouseUp( 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 Uint32 onKeyDown( const KeyEvent& Event );
|
||||||
|
|
||||||
virtual void onClassChange();
|
virtual void onClassChange();
|
||||||
|
|
||||||
virtual void onSizeChange();
|
|
||||||
|
|
||||||
virtual void onAutoSize();
|
|
||||||
|
|
||||||
virtual void onThemeLoaded();
|
|
||||||
|
|
||||||
void setFriendNode( UINode* friendNode );
|
|
||||||
|
|
||||||
void destroyListBox();
|
void destroyListBox();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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_PROGRESSBAR,
|
||||||
UI_TYPE_LISTBOX,
|
UI_TYPE_LISTBOX,
|
||||||
UI_TYPE_LISTBOXITEM,
|
UI_TYPE_LISTBOXITEM,
|
||||||
|
UI_TYPE_DROPDOWN,
|
||||||
UI_TYPE_DROPDOWNLIST,
|
UI_TYPE_DROPDOWNLIST,
|
||||||
UI_TYPE_MENU_SEPARATOR,
|
UI_TYPE_MENU_SEPARATOR,
|
||||||
UI_TYPE_COMBOBOX,
|
UI_TYPE_COMBOBOX,
|
||||||
@@ -117,6 +118,7 @@ enum UINodeType {
|
|||||||
UI_TYPE_HTML_TABLE_FOOTER,
|
UI_TYPE_HTML_TABLE_FOOTER,
|
||||||
UI_TYPE_HTML_TABLE_ROW,
|
UI_TYPE_HTML_TABLE_ROW,
|
||||||
UI_TYPE_HTML_TABLE_CELL,
|
UI_TYPE_HTML_TABLE_CELL,
|
||||||
|
UI_TYPE_DROPDOWNMODELLIST,
|
||||||
UI_TYPE_MODULES = 10000,
|
UI_TYPE_MODULES = 10000,
|
||||||
UI_TYPE_TERMINAL = 10001,
|
UI_TYPE_TERMINAL = 10001,
|
||||||
UI_TYPE_USER = 200000,
|
UI_TYPE_USER = 200000,
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ class EE_API UIListView : public UITableView {
|
|||||||
public:
|
public:
|
||||||
static UIListView* New();
|
static UIListView* New();
|
||||||
|
|
||||||
|
static UIListView* NewWithTag( const std::string& tag );
|
||||||
|
|
||||||
Uint32 getType() const;
|
Uint32 getType() const;
|
||||||
|
|
||||||
bool isType( const Uint32& type ) const;
|
bool isType( const Uint32& type ) const;
|
||||||
@@ -16,7 +18,7 @@ class EE_API UIListView : public UITableView {
|
|||||||
void setTheme( UITheme* Theme );
|
void setTheme( UITheme* Theme );
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
UIListView();
|
UIListView( const std::string& tag = "listview" );
|
||||||
};
|
};
|
||||||
|
|
||||||
}} // namespace EE::UI
|
}} // namespace EE::UI
|
||||||
|
|||||||
@@ -1618,6 +1618,12 @@ solution "eepp"
|
|||||||
files { "src/examples/ui_application_hello_world/*.cpp" }
|
files { "src/examples/ui_application_hello_world/*.cpp" }
|
||||||
build_link_configuration( "eepp-ui-application-hello-world", true )
|
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"
|
project "eepp-ui-richtext"
|
||||||
set_kind()
|
set_kind()
|
||||||
language "C++"
|
language "C++"
|
||||||
@@ -1770,6 +1776,7 @@ solution "eepp"
|
|||||||
linkoptions { "../../bin/assets/icon/ecode.res" }
|
linkoptions { "../../bin/assets/icon/ecode.res" }
|
||||||
end
|
end
|
||||||
buildoptions{ "-Wa,-mbig-obj" }
|
buildoptions{ "-Wa,-mbig-obj" }
|
||||||
|
linkoptions { "-Wl,--export-all-symbols" }
|
||||||
end
|
end
|
||||||
build_link_configuration( "ecode", false )
|
build_link_configuration( "ecode", false )
|
||||||
configuration { "release", "windows" }
|
configuration { "release", "windows" }
|
||||||
|
|||||||
@@ -1507,6 +1507,12 @@ workspace "eepp"
|
|||||||
files { "src/examples/ui_application_hello_world/*.cpp" }
|
files { "src/examples/ui_application_hello_world/*.cpp" }
|
||||||
build_link_configuration( "eepp-ui-application-hello-world", true )
|
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"
|
project "eepp-ui-richtext"
|
||||||
set_kind()
|
set_kind()
|
||||||
language "C++"
|
language "C++"
|
||||||
@@ -1628,6 +1634,7 @@ workspace "eepp"
|
|||||||
build_link_configuration( "ecode", false )
|
build_link_configuration( "ecode", false )
|
||||||
filter { "system:windows", "action:not vs*" }
|
filter { "system:windows", "action:not vs*" }
|
||||||
buildoptions{ "-Wa,-mbig-obj" }
|
buildoptions{ "-Wa,-mbig-obj" }
|
||||||
|
linkoptions { "-Wl,--export-all-symbols" }
|
||||||
filter { "system:windows", "action:vs*" }
|
filter { "system:windows", "action:vs*" }
|
||||||
files { "bin/assets/icon/ecode.rc", "bin/assets/icon/ecode.ico" }
|
files { "bin/assets/icon/ecode.rc", "bin/assets/icon/ecode.ico" }
|
||||||
vpaths { ['Resources/*'] = { "ecode.rc", "ecode.ico" } }
|
vpaths { ['Resources/*'] = { "ecode.rc", "ecode.ico" } }
|
||||||
|
|||||||
@@ -115,8 +115,11 @@ void FileInfo::getInfo() {
|
|||||||
struct stat st;
|
struct stat st;
|
||||||
int res = stat( mFilepath.c_str(), &st );
|
int res = stat( mFilepath.c_str(), &st );
|
||||||
#else
|
#else
|
||||||
struct _stat st;
|
std::string_view fp( mFilepath );
|
||||||
int res = _wstat( String::fromUtf8( mFilepath ).toWideString().c_str(), &st );
|
if ( fp.size() > 3 && ( fp.back() == '/' || fp.back() == '\\' ) )
|
||||||
|
fp.remove_suffix( 1 );
|
||||||
|
struct __stat64 st;
|
||||||
|
int res = _wstat64( String( fp ).toWideString().c_str(), &st );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if ( 0 == res ) {
|
if ( 0 == res ) {
|
||||||
@@ -261,8 +264,11 @@ bool FileInfo::exists() const {
|
|||||||
struct stat st;
|
struct stat st;
|
||||||
int res = stat( mFilepath.c_str(), &st );
|
int res = stat( mFilepath.c_str(), &st );
|
||||||
#else
|
#else
|
||||||
struct _stat st;
|
std::string_view fp( mFilepath );
|
||||||
int res = _wstat( String::fromUtf8( mFilepath ).toWideString().c_str(), &st );
|
if ( fp.size() > 3 && ( fp.back() == '/' || fp.back() == '\\' ) )
|
||||||
|
fp.remove_suffix( 1 );
|
||||||
|
struct __stat64 st;
|
||||||
|
int res = _wstat64( String( fp ).toWideString().c_str(), &st );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if ( isDirectory() )
|
if ( isDirectory() )
|
||||||
|
|||||||
@@ -211,8 +211,11 @@ bool FileSystem::fileHide( const std::string& filepath ) {
|
|||||||
|
|
||||||
Uint32 FileSystem::fileGetModificationDate( const std::string& filepath ) {
|
Uint32 FileSystem::fileGetModificationDate( const std::string& filepath ) {
|
||||||
#if EE_PLATFORM == EE_PLATFORM_WIN
|
#if EE_PLATFORM == EE_PLATFORM_WIN
|
||||||
struct _stat st;
|
std::string_view fp( filepath );
|
||||||
int res = _wstat( String( filepath ).toWideString().c_str(), &st );
|
if ( fp.size() > 3 && ( fp.back() == '/' || fp.back() == '\\' ) )
|
||||||
|
fp.remove_suffix( 1 );
|
||||||
|
struct __stat64 st;
|
||||||
|
int res = _wstat64( String( fp ).toWideString().c_str(), &st );
|
||||||
#else
|
#else
|
||||||
struct stat st;
|
struct stat st;
|
||||||
int res = stat( filepath.c_str(), &st );
|
int res = stat( filepath.c_str(), &st );
|
||||||
@@ -540,8 +543,11 @@ std::vector<std::string> FileSystem::filesGetInPath( const std::string& path,
|
|||||||
|
|
||||||
Uint64 FileSystem::fileSize( const std::string& Filepath ) {
|
Uint64 FileSystem::fileSize( const std::string& Filepath ) {
|
||||||
#if EE_PLATFORM == EE_PLATFORM_WIN
|
#if EE_PLATFORM == EE_PLATFORM_WIN
|
||||||
struct _stat st;
|
std::string_view fp( Filepath );
|
||||||
int res = _wstat( String( Filepath ).toWideString().c_str(), &st );
|
if ( fp.size() > 3 && ( fp.back() == '/' || fp.back() == '\\' ) )
|
||||||
|
fp.remove_suffix( 1 );
|
||||||
|
struct __stat64 st;
|
||||||
|
int res = _wstat64( String( fp ).toWideString().c_str(), &st );
|
||||||
#else
|
#else
|
||||||
struct stat st;
|
struct stat st;
|
||||||
int res = stat( Filepath.c_str(), &st );
|
int res = stat( Filepath.c_str(), &st );
|
||||||
@@ -555,8 +561,11 @@ Uint64 FileSystem::fileSize( const std::string& Filepath ) {
|
|||||||
|
|
||||||
bool FileSystem::fileExists( const std::string& Filepath ) {
|
bool FileSystem::fileExists( const std::string& Filepath ) {
|
||||||
#if EE_PLATFORM == EE_PLATFORM_WIN
|
#if EE_PLATFORM == EE_PLATFORM_WIN
|
||||||
struct _stat st;
|
std::string_view fp( Filepath );
|
||||||
return ( _wstat( String( Filepath ).toWideString().c_str(), &st ) == 0 );
|
if ( fp.size() > 3 && ( fp.back() == '/' || fp.back() == '\\' ) )
|
||||||
|
fp.remove_suffix( 1 );
|
||||||
|
struct __stat64 st;
|
||||||
|
return ( _wstat64( String( fp ).toWideString().c_str(), &st ) == 0 );
|
||||||
#else
|
#else
|
||||||
struct stat st;
|
struct stat st;
|
||||||
return ( stat( Filepath.c_str(), &st ) == 0 );
|
return ( stat( Filepath.c_str(), &st ) == 0 );
|
||||||
|
|||||||
@@ -643,7 +643,7 @@ static std::string sGetProcessPath() {
|
|||||||
_splitpath_s( dllstrName.c_str(), szDrive, _MAX_DRIVE, szDir, _MAX_DIR, szFilename, _MAX_DIR,
|
_splitpath_s( dllstrName.c_str(), szDrive, _MAX_DRIVE, szDir, _MAX_DIR, szFilename, _MAX_DIR,
|
||||||
szExt, _MAX_DIR );
|
szExt, _MAX_DIR );
|
||||||
#else
|
#else
|
||||||
_splitpath( szDllName, szDrive, szDir, szFilename, szExt );
|
_splitpath( dllstrName.c_str(), szDrive, szDir, szFilename, szExt );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return std::string( szDrive ) + std::string( szDir );
|
return std::string( szDrive ) + std::string( szDir );
|
||||||
|
|||||||
@@ -384,6 +384,7 @@ void addCSS() {
|
|||||||
{ "RadioButton", "keyword" },
|
{ "RadioButton", "keyword" },
|
||||||
{ "ComboBox", "keyword" },
|
{ "ComboBox", "keyword" },
|
||||||
{ "DropDownList", "keyword" },
|
{ "DropDownList", "keyword" },
|
||||||
|
{ "DropDownModelList", "keyword" },
|
||||||
{ "Image", "keyword" },
|
{ "Image", "keyword" },
|
||||||
{ "ListBox", "keyword" },
|
{ "ListBox", "keyword" },
|
||||||
{ "MenuBar", "keyword" },
|
{ "MenuBar", "keyword" },
|
||||||
@@ -425,6 +426,7 @@ void addCSS() {
|
|||||||
{ "PopUpMenu", "keyword" },
|
{ "PopUpMenu", "keyword" },
|
||||||
{ "ImageViewer", "keyword" },
|
{ "ImageViewer", "keyword" },
|
||||||
{ "AudioPlayer", "keyword" },
|
{ "AudioPlayer", "keyword" },
|
||||||
|
{ "Table", "keyword" },
|
||||||
|
|
||||||
},
|
},
|
||||||
"",
|
"",
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ void addJSON() {
|
|||||||
"%.jsonc$",
|
"%.jsonc$",
|
||||||
"%.ipynb$",
|
"%.ipynb$",
|
||||||
"%.webmanifest$",
|
"%.webmanifest$",
|
||||||
|
"%.cps$",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
{ { "//.-\n" }, "comment" },
|
{ { "//.-\n" }, "comment" },
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ void SyntaxHighlighter::setMaxTokenizationLength( const Int64& maxTokenizationLe
|
|||||||
|
|
||||||
void SyntaxHighlighter::tokenizeAsync( std::shared_ptr<ThreadPool> pool,
|
void SyntaxHighlighter::tokenizeAsync( std::shared_ptr<ThreadPool> pool,
|
||||||
const std::function<void()>& onDone ) {
|
const std::function<void()>& onDone ) {
|
||||||
if ( mTokenizeAsync && false )
|
if ( mTokenizeAsync )
|
||||||
return;
|
return;
|
||||||
mTokenizeAsync = true;
|
mTokenizeAsync = true;
|
||||||
pool->run( [this, onDone] {
|
pool->run( [this, onDone] {
|
||||||
|
|||||||
@@ -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
|
||||||
+33
-298
@@ -10,34 +10,6 @@
|
|||||||
|
|
||||||
namespace EE { namespace UI {
|
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 ) {
|
UIDropDownList* UIDropDownList::NewWithTag( const std::string& tag ) {
|
||||||
return eeNew( UIDropDownList, ( tag ) );
|
return eeNew( UIDropDownList, ( tag ) );
|
||||||
}
|
}
|
||||||
@@ -46,15 +18,7 @@ UIDropDownList* UIDropDownList::New() {
|
|||||||
return eeNew( UIDropDownList, () );
|
return eeNew( UIDropDownList, () );
|
||||||
}
|
}
|
||||||
|
|
||||||
UIDropDownList::UIDropDownList( const std::string& tag ) :
|
UIDropDownList::UIDropDownList( const std::string& tag ) : UIDropDown( tag ), mListBox( NULL ) {
|
||||||
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 );
|
|
||||||
|
|
||||||
applyDefaultTheme();
|
applyDefaultTheme();
|
||||||
|
|
||||||
mListBox = UIListBox::NewWithTag( mTag + "::listbox" );
|
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.
|
// This will force to change the parent when shown, and force the CSS style reload.
|
||||||
mListBox->setParent( this );
|
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::OnItemSelected, [this]( auto event ) { onItemSelected( event ); } );
|
||||||
mListBox->on( Event::OnItemClicked, [this]( auto event ) { onItemClicked( event ); } );
|
mListBox->on( Event::OnItemClicked, [this]( auto event ) { onItemClicked( event ); } );
|
||||||
mListBox->on( Event::OnItemKeyDown, [this]( auto event ) { onItemKeyDown( 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 {
|
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* UIDropDownList::getPopUpWidget() const {
|
||||||
UIWidget::setTheme( Theme );
|
return mListBox;
|
||||||
|
|
||||||
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 ) );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UIListBox* UIDropDownList::getListBox() const {
|
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 ) {
|
Uint32 UIDropDownList::onKeyDown( const KeyEvent& Event ) {
|
||||||
if ( ( Flags & EE_BUTTON_LMASK ) && NULL == mFriendNode )
|
if ( NULL != mListBox )
|
||||||
showList();
|
mListBox->onKeyDown( Event );
|
||||||
|
|
||||||
if ( NULL != mFriendNode ) {
|
return UIDropDown::onKeyDown( Event );
|
||||||
UITextInput::onMouseClick( Pos, Flags );
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UIDropDownList* UIDropDownList::showList() {
|
UIDropDownList* UIDropDownList::showList() {
|
||||||
@@ -173,85 +102,30 @@ UIDropDownList* UIDropDownList::showList() {
|
|||||||
if ( !mListBox->isVisible() ) {
|
if ( !mListBox->isVisible() ) {
|
||||||
if ( mListBox->getItemsCount() ) {
|
if ( mListBox->getItemsCount() ) {
|
||||||
Rectf tPadding = mListBox->getContainerPadding();
|
Rectf tPadding = mListBox->getContainerPadding();
|
||||||
|
|
||||||
Float sliderValue = mListBox->getVerticalScrollBar()->getValue();
|
Float sliderValue = mListBox->getVerticalScrollBar()->getValue();
|
||||||
|
|
||||||
Float width =
|
Float contentsWidth = eeceil( PixelDensity::pxToDp(
|
||||||
NULL != mFriendNode ? mFriendNode->getSize().getWidth() : getSize().getWidth();
|
mListBox->getMaxTextWidth() +
|
||||||
|
PixelDensity::dpToPx( mListBox->getContainerPadding().getWidth() ) +
|
||||||
|
mListBox->getReferenceItem()->getPixelsPadding().getWidth() +
|
||||||
|
mListBox->getVerticalScrollBar()->getPixelsSize().getWidth() ) );
|
||||||
|
|
||||||
bool center = mStyleConfig.menuWidthRule == MenuWidthMode::ContentsCentered ||
|
Float width = getPopUpWidth( contentsWidth );
|
||||||
mStyleConfig.menuWidthRule == MenuWidthMode::ExpandIfNeededCentered;
|
|
||||||
|
|
||||||
Float contentsWidth = 0;
|
Float height =
|
||||||
if ( mStyleConfig.menuWidthRule == MenuWidthMode::Contents ||
|
(Int32)( eemin( mListBox->getItemsCount(), mStyleConfig.MaxNumVisibleItems ) *
|
||||||
mStyleConfig.menuWidthRule == MenuWidthMode::ContentsCentered ||
|
mListBox->getRowHeight() ) +
|
||||||
mStyleConfig.menuWidthRule == MenuWidthMode::ExpandIfNeeded ||
|
tPadding.Top + tPadding.Bottom +
|
||||||
mStyleConfig.menuWidthRule == MenuWidthMode::ExpandIfNeededCentered ) {
|
( mListBox->getHorizontalScrollBar() &&
|
||||||
contentsWidth = eeceil( PixelDensity::pxToDp(
|
mListBox->getHorizontalScrollBar()->isVisible() &&
|
||||||
mListBox->getMaxTextWidth() +
|
PixelDensity::dpToPx( width ) < mListBox->getMaxTextWidth()
|
||||||
PixelDensity::dpToPx( mListBox->getContainerPadding().getWidth() ) +
|
? mListBox->getHorizontalScrollBar()->getSize().getHeight()
|
||||||
mListBox->getReferenceItem()->getPixelsPadding().getWidth() +
|
: 0.f );
|
||||||
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 ) );
|
|
||||||
|
|
||||||
|
mListBox->setSize( width, height );
|
||||||
mListBox->getVerticalScrollBar()->setValue( sliderValue );
|
mListBox->getVerticalScrollBar()->setValue( sliderValue );
|
||||||
|
|
||||||
if ( !mStyleConfig.PopUpToRoot )
|
alignPopUp( mListBox );
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
hide();
|
hide();
|
||||||
@@ -259,19 +133,6 @@ UIDropDownList* UIDropDownList::showList() {
|
|||||||
return this;
|
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 ) {
|
UIDropDownList* UIDropDownList::setMaxNumVisibleItems( const Uint32& maxNumVisibleItems ) {
|
||||||
if ( maxNumVisibleItems != mStyleConfig.MaxNumVisibleItems ) {
|
if ( maxNumVisibleItems != mStyleConfig.MaxNumVisibleItems ) {
|
||||||
mStyleConfig.MaxNumVisibleItems = maxNumVisibleItems;
|
mStyleConfig.MaxNumVisibleItems = maxNumVisibleItems;
|
||||||
@@ -284,52 +145,6 @@ UIDropDownList* UIDropDownList::setMaxNumVisibleItems( const Uint32& maxNumVisib
|
|||||||
return this;
|
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* ) {
|
void UIDropDownList::onItemSelected( const Event* ) {
|
||||||
setText( mListBox->getItemSelectedText() );
|
setText( mListBox->getItemSelectedText() );
|
||||||
|
|
||||||
@@ -341,60 +156,6 @@ void UIDropDownList::onItemSelected( const Event* ) {
|
|||||||
sendCommonEvent( Event::OnSelectionChanged );
|
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() {
|
void UIDropDownList::destroyListBox() {
|
||||||
if ( !SceneManager::instance()->isShuttingDown() && NULL != mListBox &&
|
if ( !SceneManager::instance()->isShuttingDown() && NULL != mListBox &&
|
||||||
mListBox->getParent() != this ) {
|
mListBox->getParent() != this ) {
|
||||||
@@ -407,15 +168,6 @@ bool UIDropDownList::applyProperty( const StyleSheetProperty& attribute ) {
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
switch ( attribute.getPropertyDefinition()->getPropertyId() ) {
|
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::SelectedIndex:
|
||||||
case PropertyId::SelectedText:
|
case PropertyId::SelectedText:
|
||||||
case PropertyId::ScrollBarStyle:
|
case PropertyId::ScrollBarStyle:
|
||||||
@@ -427,7 +179,7 @@ bool UIDropDownList::applyProperty( const StyleSheetProperty& attribute ) {
|
|||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
default:
|
default:
|
||||||
return UITextInput::applyProperty( attribute );
|
return UIDropDown::applyProperty( attribute );
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -439,12 +191,6 @@ std::string UIDropDownList::getPropertyString( const PropertyDefinition* propert
|
|||||||
return "";
|
return "";
|
||||||
|
|
||||||
switch ( propertyDef->getPropertyId() ) {
|
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::SelectedIndex:
|
||||||
case PropertyId::SelectedText:
|
case PropertyId::SelectedText:
|
||||||
case PropertyId::ScrollBarStyle:
|
case PropertyId::ScrollBarStyle:
|
||||||
@@ -454,18 +200,16 @@ std::string UIDropDownList::getPropertyString( const PropertyDefinition* propert
|
|||||||
if ( NULL != mListBox )
|
if ( NULL != mListBox )
|
||||||
return mListBox->getPropertyString( propertyDef, propertyIndex );
|
return mListBox->getPropertyString( propertyDef, propertyIndex );
|
||||||
default:
|
default:
|
||||||
return UITextInput::getPropertyString( propertyDef, propertyIndex );
|
return UIDropDown::getPropertyString( propertyDef, propertyIndex );
|
||||||
}
|
}
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<PropertyId> UIDropDownList::getPropertiesImplemented() const {
|
std::vector<PropertyId> UIDropDownList::getPropertiesImplemented() const {
|
||||||
auto props = UITextInput::getPropertiesImplemented();
|
auto props = UIDropDown::getPropertiesImplemented();
|
||||||
auto local = {
|
auto local = { PropertyId::SelectedIndex, PropertyId::SelectedText, PropertyId::ScrollBarStyle,
|
||||||
PropertyId::PopUpToRoot, PropertyId::MaxVisibleItems, PropertyId::SelectedIndex,
|
PropertyId::RowHeight, PropertyId::VScrollMode, PropertyId::HScrollMode };
|
||||||
PropertyId::SelectedText, PropertyId::ScrollBarStyle, PropertyId::RowHeight,
|
|
||||||
PropertyId::VScrollMode, PropertyId::HScrollMode, PropertyId::MenuWidthMode };
|
|
||||||
props.insert( props.end(), local.begin(), local.end() );
|
props.insert( props.end(), local.begin(), local.end() );
|
||||||
return props;
|
return props;
|
||||||
}
|
}
|
||||||
@@ -476,7 +220,7 @@ void UIDropDownList::loadFromXmlNode( const pugi::xml_node& node ) {
|
|||||||
if ( NULL != mListBox )
|
if ( NULL != mListBox )
|
||||||
mListBox->loadItemsFromXmlNode( node );
|
mListBox->loadItemsFromXmlNode( node );
|
||||||
|
|
||||||
UITextInput::loadFromXmlNode( node );
|
UIDropDown::loadFromXmlNode( node );
|
||||||
|
|
||||||
endAttributesTransaction();
|
endAttributesTransaction();
|
||||||
}
|
}
|
||||||
@@ -486,13 +230,4 @@ void UIDropDownList::onClassChange() {
|
|||||||
mListBox->setClasses( getClasses() );
|
mListBox->setClasses( getClasses() );
|
||||||
}
|
}
|
||||||
|
|
||||||
UIDropDownList* UIDropDownList::setMenuWidthMode( MenuWidthMode rule ) {
|
|
||||||
mStyleConfig.menuWidthRule = rule;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
UIDropDownList::MenuWidthMode UIDropDownList::getMenuWidthMode() const {
|
|
||||||
return mStyleConfig.menuWidthRule;
|
|
||||||
}
|
|
||||||
|
|
||||||
}} // namespace EE::UI
|
}} // namespace EE::UI
|
||||||
|
|||||||
@@ -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, () );
|
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 );
|
setHeadersVisible( false );
|
||||||
setAutoExpandOnSingleColumn( true );
|
setAutoExpandOnSingleColumn( true );
|
||||||
applyDefaultTheme();
|
applyDefaultTheme();
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ UIThemeManager::UIThemeManager() :
|
|||||||
mThemeDefault( NULL ),
|
mThemeDefault( NULL ),
|
||||||
mAutoApplyDefaultTheme( true ),
|
mAutoApplyDefaultTheme( true ),
|
||||||
mEnableDefaultEffects( false ),
|
mEnableDefaultEffects( false ),
|
||||||
mFadeInTime( Milliseconds( 100.f ) ),
|
mFadeInTime( Milliseconds( 25.f ) ),
|
||||||
mFadeOutTime( Milliseconds( 100.f ) ),
|
mFadeOutTime( Milliseconds( 25.f ) ),
|
||||||
mTooltipTimeToShow( Milliseconds( 400 ) ),
|
mTooltipTimeToShow( Milliseconds( 400 ) ),
|
||||||
mTooltipFollowMouse( false ),
|
mTooltipFollowMouse( false ),
|
||||||
mCursorSize( 16, 16 ) {}
|
mCursorSize( 16, 16 ) {}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <eepp/ui/uicombobox.hpp>
|
#include <eepp/ui/uicombobox.hpp>
|
||||||
#include <eepp/ui/uiconsole.hpp>
|
#include <eepp/ui/uiconsole.hpp>
|
||||||
#include <eepp/ui/uidropdownlist.hpp>
|
#include <eepp/ui/uidropdownlist.hpp>
|
||||||
|
#include <eepp/ui/uidropdownmodellist.hpp>
|
||||||
#include <eepp/ui/uigridlayout.hpp>
|
#include <eepp/ui/uigridlayout.hpp>
|
||||||
#include <eepp/ui/uihtmltable.hpp>
|
#include <eepp/ui/uihtmltable.hpp>
|
||||||
#include <eepp/ui/uiimage.hpp>
|
#include <eepp/ui/uiimage.hpp>
|
||||||
@@ -67,6 +68,7 @@ void UIWidgetCreator::createBaseWidgetList() {
|
|||||||
registeredWidget["radiobutton"] = UIRadioButton::New;
|
registeredWidget["radiobutton"] = UIRadioButton::New;
|
||||||
registeredWidget["combobox"] = UIComboBox::New;
|
registeredWidget["combobox"] = UIComboBox::New;
|
||||||
registeredWidget["dropdownlist"] = UIDropDownList::New;
|
registeredWidget["dropdownlist"] = UIDropDownList::New;
|
||||||
|
registeredWidget["dropdownmodellist"] = UIDropDownModelList::New;
|
||||||
registeredWidget["image"] = UIImage::New;
|
registeredWidget["image"] = UIImage::New;
|
||||||
registeredWidget["listbox"] = UIListBox::New;
|
registeredWidget["listbox"] = UIListBox::New;
|
||||||
registeredWidget["menubar"] = UIMenuBar::New;
|
registeredWidget["menubar"] = UIMenuBar::New;
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
@@ -248,42 +248,6 @@ static std::wstring stringToWideString( const std::string& str ) {
|
|||||||
|
|
||||||
#define HANDLE_WIN_ERR( err ) HRESULT_FROM_WIN32( err ), PrintWinApiError( err )
|
#define HANDLE_WIN_ERR( err ) HRESULT_FROM_WIN32( err ), PrintWinApiError( err )
|
||||||
|
|
||||||
static HRESULT InitializeStartupInfoAttachedToPseudoConsole( STARTUPINFOEXW* pStartupInfo,
|
|
||||||
HPCON hpc ) {
|
|
||||||
// Prepare Startup Information structure
|
|
||||||
STARTUPINFOEXW si;
|
|
||||||
ZeroMemory( &si, sizeof( si ) );
|
|
||||||
si.StartupInfo.cb = sizeof( STARTUPINFOEXW );
|
|
||||||
|
|
||||||
// Discover the size required for the list
|
|
||||||
SIZE_T bytesRequired;
|
|
||||||
InitializeProcThreadAttributeList( NULL, 1, 0, &bytesRequired );
|
|
||||||
|
|
||||||
// Allocate memory to represent the list
|
|
||||||
si.lpAttributeList =
|
|
||||||
(PPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc( GetProcessHeap(), 0, bytesRequired );
|
|
||||||
if ( !si.lpAttributeList ) {
|
|
||||||
return E_OUTOFMEMORY;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize the list memory location
|
|
||||||
if ( !InitializeProcThreadAttributeList( si.lpAttributeList, 1, 0, &bytesRequired ) ) {
|
|
||||||
HeapFree( GetProcessHeap(), 0, si.lpAttributeList );
|
|
||||||
return HRESULT_FROM_WIN32( GetLastError() );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the pseudoconsole information into the list
|
|
||||||
if ( !UpdateProcThreadAttribute( si.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE,
|
|
||||||
hpc, sizeof( hpc ), NULL, NULL ) ) {
|
|
||||||
HeapFree( GetProcessHeap(), 0, si.lpAttributeList );
|
|
||||||
return HRESULT_FROM_WIN32( GetLastError() );
|
|
||||||
}
|
|
||||||
|
|
||||||
*pStartupInfo = si;
|
|
||||||
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
Process::Process( AutoHandle&& hProcess, void* lpAttributeList, int pid ) :
|
Process::Process( AutoHandle&& hProcess, void* lpAttributeList, int pid ) :
|
||||||
mStatus( ProcessStatus::RUNNING ),
|
mStatus( ProcessStatus::RUNNING ),
|
||||||
mExitCode( 1 ),
|
mExitCode( 1 ),
|
||||||
@@ -393,11 +357,13 @@ std::unique_ptr<Process> Process::createWithPipe( const std::string& program,
|
|||||||
oss << ' ' << arg;
|
oss << ' ' << arg;
|
||||||
}
|
}
|
||||||
std::wstring commandLine = stringToWideString( oss.str() );
|
std::wstring commandLine = stringToWideString( oss.str() );
|
||||||
// TODO: If workingDirectory is relative, make it absolute (relative to the
|
|
||||||
// current process working directory)
|
std::vector<wchar_t> cmdLineMutable(commandLine.begin(), commandLine.end());
|
||||||
|
cmdLineMutable.push_back(0);
|
||||||
|
|
||||||
std::wstring workingDir = stringToWideString( workingDirectory );
|
std::wstring workingDir = stringToWideString( workingDirectory );
|
||||||
|
|
||||||
if ( CreateProcessW( NULL, (LPWSTR)commandLine.c_str(), NULL, NULL, TRUE, 0, NULL,
|
if ( CreateProcessW( NULL, (LPWSTR)cmdLineMutable.data(), NULL, NULL, TRUE, 0, NULL,
|
||||||
workingDir.empty() ? NULL : workingDir.c_str(), &siStartInfo,
|
workingDir.empty() ? NULL : workingDir.c_str(), &siStartInfo,
|
||||||
&piProcInfo ) ) {
|
&piProcInfo ) ) {
|
||||||
std::unique_ptr<Pipe> pipe = std::unique_ptr<Pipe>(
|
std::unique_ptr<Pipe> pipe = std::unique_ptr<Pipe>(
|
||||||
@@ -434,6 +400,12 @@ Process::createWithPseudoTerminal( const std::string& program, const std::vector
|
|||||||
AutoHandle hThread{};
|
AutoHandle hThread{};
|
||||||
|
|
||||||
STARTUPINFOEXW startupInfo{};
|
STARTUPINFOEXW startupInfo{};
|
||||||
|
startupInfo.StartupInfo.cb = sizeof( STARTUPINFOEXW );
|
||||||
|
startupInfo.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
|
||||||
|
startupInfo.StartupInfo.hStdInput = INVALID_HANDLE_VALUE;
|
||||||
|
startupInfo.StartupInfo.hStdOutput = INVALID_HANDLE_VALUE;
|
||||||
|
startupInfo.StartupInfo.hStdError = INVALID_HANDLE_VALUE;
|
||||||
|
|
||||||
PROCESS_INFORMATION piClient{};
|
PROCESS_INFORMATION piClient{};
|
||||||
|
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
@@ -442,19 +414,32 @@ Process::createWithPseudoTerminal( const std::string& program, const std::vector
|
|||||||
oss << ' ' << arg;
|
oss << ' ' << arg;
|
||||||
}
|
}
|
||||||
std::wstring commandLine = stringToWideString( oss.str() );
|
std::wstring commandLine = stringToWideString( oss.str() );
|
||||||
// TODO: If workingDirectory is relative, make it absolute (relative to the
|
|
||||||
// current process working directory)
|
|
||||||
std::wstring workingDir = stringToWideString( workingDirectory );
|
std::wstring workingDir = stringToWideString( workingDirectory );
|
||||||
|
|
||||||
if ( ( hr = InitializeStartupInfoAttachedToPseudoConsole( &startupInfo,
|
SIZE_T bytesRequired = 0;
|
||||||
pseudoTerminal.mPHPC ) ) != S_OK ) {
|
InitializeProcThreadAttributeList( NULL, 1, 0, &bytesRequired );
|
||||||
Log::error( "InitializeStartupInfoAttachedToPseudoConsole failed." );
|
|
||||||
PrintErrorResult( hr );
|
startupInfo.lpAttributeList =
|
||||||
|
(PPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc( GetProcessHeap(), 0, bytesRequired );
|
||||||
|
|
||||||
|
if ( !startupInfo.lpAttributeList ) {
|
||||||
|
Log::error( "HeapAlloc failed for attribute list." );
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !InitializeProcThreadAttributeList( startupInfo.lpAttributeList, 1, 0, &bytesRequired ) ) {
|
||||||
|
Log::error( "InitializeProcThreadAttributeList failed." );
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !UpdateProcThreadAttribute( startupInfo.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE,
|
||||||
|
pseudoTerminal.mPHPC, sizeof( HPCON ), NULL, NULL ) ) {
|
||||||
|
Log::error( "UpdateProcThreadAttribute failed." );
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr = CreateProcessW( NULL, // No module name - use Command Line
|
hr = CreateProcessW( NULL, // No module name - use Command Line
|
||||||
(wchar_t*)commandLine.c_str(), // Command Line
|
commandLine.data(), // Must be a mutable buffer to prevent ERROR_INSUFFICIENT_BUFFER
|
||||||
NULL, // Process handle not inheritable
|
NULL, // Process handle not inheritable
|
||||||
NULL, // Thread handle not inheritable
|
NULL, // Thread handle not inheritable
|
||||||
FALSE, // Inherit handles
|
FALSE, // Inherit handles
|
||||||
@@ -480,6 +465,10 @@ Process::createWithPseudoTerminal( const std::string& program, const std::vector
|
|||||||
return std::unique_ptr<Process>(
|
return std::unique_ptr<Process>(
|
||||||
new Process( std::move( hProcess ), startupInfo.lpAttributeList, piClient.dwProcessId ) );
|
new Process( std::move( hProcess ), startupInfo.lpAttributeList, piClient.dwProcessId ) );
|
||||||
fail:
|
fail:
|
||||||
|
if ( startupInfo.lpAttributeList ) {
|
||||||
|
DeleteProcThreadAttributeList( startupInfo.lpAttributeList );
|
||||||
|
HeapFree( GetProcessHeap(), 0, startupInfo.lpAttributeList );
|
||||||
|
}
|
||||||
return std::unique_ptr<Process>();
|
return std::unique_ptr<Process>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -489,4 +478,4 @@ int Process::pid() {
|
|||||||
|
|
||||||
}} // namespace eterm::System
|
}} // namespace eterm::System
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -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 );
|
||||||
|
}
|
||||||
+28
-21
@@ -332,6 +332,10 @@
|
|||||||
|
|
||||||
#if defined(BACKWARD_SYSTEM_WINDOWS)
|
#if defined(BACKWARD_SYSTEM_WINDOWS)
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
#include <cxxabi.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
@@ -612,14 +616,20 @@ template <typename TAG> struct demangler_impl {
|
|||||||
static std::string demangle(const char *funcname) { return funcname; }
|
static std::string demangle(const char *funcname) { return funcname; }
|
||||||
};
|
};
|
||||||
|
|
||||||
#if defined(BACKWARD_SYSTEM_LINUX) || defined(BACKWARD_SYSTEM_DARWIN)
|
#if defined(BACKWARD_SYSTEM_LINUX) || defined(BACKWARD_SYSTEM_DARWIN) || (defined(BACKWARD_SYSTEM_WINDOWS) && defined(__GNUC__))
|
||||||
|
|
||||||
template <> struct demangler_impl<system_tag::current_tag> {
|
template <> struct demangler_impl<system_tag::current_tag> {
|
||||||
demangler_impl() : _demangle_buffer_length(0) {}
|
demangler_impl() : _demangle_buffer_length(0) {}
|
||||||
|
|
||||||
std::string demangle(const char *funcname) {
|
std::string demangle(const char *funcname) {
|
||||||
using namespace details;
|
using namespace details;
|
||||||
char *result = abi::__cxa_demangle(funcname, _demangle_buffer.get(),
|
const char* target = funcname;
|
||||||
|
std::string mangled;
|
||||||
|
if (target && target[0] == 'Z') {
|
||||||
|
mangled = "_" + std::string(target);
|
||||||
|
target = mangled.c_str();
|
||||||
|
}
|
||||||
|
char *result = abi::__cxa_demangle(target, _demangle_buffer.get(),
|
||||||
&_demangle_buffer_length, nullptr);
|
&_demangle_buffer_length, nullptr);
|
||||||
if (result) {
|
if (result) {
|
||||||
_demangle_buffer.update(result);
|
_demangle_buffer.update(result);
|
||||||
@@ -3674,41 +3684,38 @@ public:
|
|||||||
ResolvedTrace resolve(ResolvedTrace t) override {
|
ResolvedTrace resolve(ResolvedTrace t) override {
|
||||||
HANDLE process = GetCurrentProcess();
|
HANDLE process = GetCurrentProcess();
|
||||||
|
|
||||||
char name[256];
|
|
||||||
|
|
||||||
memset(&sym, 0, sizeof(sym));
|
memset(&sym, 0, sizeof(sym));
|
||||||
sym.sym.SizeOfStruct = sizeof(SYMBOL_INFO);
|
sym.sym.SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||||
sym.sym.MaxNameLen = max_sym_len;
|
sym.sym.MaxNameLen = max_sym_len;
|
||||||
|
|
||||||
if (!SymFromAddr(process, (ULONG64)t.addr, &displacement, &sym.sym)) {
|
if (!SymFromAddr(process, (ULONG64)t.addr, &displacement, &sym.sym)) {
|
||||||
// TODO: error handling everywhere
|
t.object_function = "??";
|
||||||
char* lpMsgBuf;
|
return t;
|
||||||
DWORD dw = GetLastError();
|
}
|
||||||
|
std::string name = demangle(sym.sym.Name);
|
||||||
if (FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
if (name == sym.sym.Name) {
|
||||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
char undecorated[256];
|
||||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
if (UnDecorateSymbolName(sym.sym.Name, undecorated, 256, UNDNAME_COMPLETE)) {
|
||||||
NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
name = undecorated;
|
||||||
(char*)&lpMsgBuf, 0, NULL)) {
|
}
|
||||||
std::fprintf(stderr, "%s\n", lpMsgBuf);
|
|
||||||
LocalFree(lpMsgBuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
// abort();
|
|
||||||
}
|
}
|
||||||
UnDecorateSymbolName(sym.sym.Name, (PSTR)name, 256, UNDNAME_COMPLETE);
|
|
||||||
|
|
||||||
DWORD offset = 0;
|
DWORD offset = 0;
|
||||||
IMAGEHLP_LINE line;
|
IMAGEHLP_LINE line;
|
||||||
if (SymGetLineFromAddr(process, (ULONG64)t.addr, &offset, &line)) {
|
if (SymGetLineFromAddr(process, (ULONG64)t.addr, &offset, &line)) {
|
||||||
t.object_filename = line.FileName;
|
|
||||||
t.source.filename = line.FileName;
|
t.source.filename = line.FileName;
|
||||||
t.source.line = line.LineNumber;
|
t.source.line = line.LineNumber;
|
||||||
t.source.col = offset;
|
t.source.col = offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IMAGEHLP_MODULE64 module;
|
||||||
|
memset(&module, 0, sizeof(module));
|
||||||
|
module.SizeOfStruct = sizeof(module);
|
||||||
|
if (SymGetModuleInfo64(process, (ULONG64)t.addr, &module)) {
|
||||||
|
t.object_filename = module.ModuleName;
|
||||||
|
}
|
||||||
|
|
||||||
t.source.function = name;
|
t.source.function = name;
|
||||||
t.object_filename = "";
|
|
||||||
t.object_function = name;
|
t.object_function = name;
|
||||||
|
|
||||||
return t;
|
return t;
|
||||||
|
|||||||
@@ -732,6 +732,15 @@ void AppConfig::loadDocuments( UICodeEditorSplitter* editorSplitter, json j,
|
|||||||
|
|
||||||
editorLoadedCounter( app );
|
editorLoadedCounter( app );
|
||||||
} else {
|
} else {
|
||||||
|
auto isSnapshotFile =
|
||||||
|
std::find_if( sessionSnapshotFiles.begin(), sessionSnapshotFiles.end(),
|
||||||
|
[&path]( const SessionSnapshotFile& file ) {
|
||||||
|
return file.cachePath == path;
|
||||||
|
} );
|
||||||
|
|
||||||
|
if ( isSnapshotFile != sessionSnapshotFiles.end() )
|
||||||
|
return; // snapshot files are loaded later
|
||||||
|
|
||||||
editorSplitter->loadAsyncFileFromPathInNewTab(
|
editorSplitter->loadAsyncFileFromPathInNewTab(
|
||||||
path,
|
path,
|
||||||
[this, curTabWidget, selection, totalToLoad, currentPage, app, path,
|
[this, curTabWidget, selection, totalToLoad, currentPage, app, path,
|
||||||
|
|||||||
@@ -4566,7 +4566,11 @@ void App::init( InitParameters& params ) {
|
|||||||
} else if ( mConfig.term.warnBeforeClosingTab && widget->isType( UI_TYPE_TERMINAL ) ) {
|
} else if ( mConfig.term.warnBeforeClosingTab && widget->isType( UI_TYPE_TERMINAL ) ) {
|
||||||
UITerminal* term = widget->asType<UITerminal>();
|
UITerminal* term = widget->asType<UITerminal>();
|
||||||
ProcessID pid = term->getTerm()->getTerminal()->getProcess()->pid();
|
ProcessID pid = term->getTerm()->getTerminal()->getProcess()->pid();
|
||||||
|
std::string msgBoxId = String::format( "msgbox_%p", this );
|
||||||
if ( Sys::processHasChildren( pid ) ) {
|
if ( Sys::processHasChildren( pid ) ) {
|
||||||
|
if ( nullptr != getUISceneNode()->find( msgBoxId ) )
|
||||||
|
return false;
|
||||||
|
|
||||||
UIMessageBox* msgBox = UIMessageBox::New(
|
UIMessageBox* msgBox = UIMessageBox::New(
|
||||||
UIMessageBox::OK_CANCEL,
|
UIMessageBox::OK_CANCEL,
|
||||||
i18n( "terminal_close_warn", "Are you sure you want to close this "
|
i18n( "terminal_close_warn", "Are you sure you want to close this "
|
||||||
@@ -4584,6 +4588,7 @@ void App::init( InitParameters& params ) {
|
|||||||
msgBox->setTitle( "ecode" );
|
msgBox->setTitle( "ecode" );
|
||||||
msgBox->center();
|
msgBox->center();
|
||||||
msgBox->showWhenReady();
|
msgBox->showWhenReady();
|
||||||
|
msgBox->setId( msgBoxId );
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,7 +98,10 @@ static std::map<std::string, LLMProvider> parseLLMProviders( const nlohmann::jso
|
|||||||
model.cacheConfiguration = cache;
|
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 {
|
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_PROVIDER = "google";
|
||||||
static const char* DEFAULT_MODEL = "gemini-2.5-flash";
|
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(
|
static const char* DEFAULT_LAYOUT = R"xml(
|
||||||
<style>
|
<style>
|
||||||
.llm_chatui DropDownList {
|
<![CDATA[
|
||||||
border-color: transparent;
|
|
||||||
background-color: var(--tab-back);
|
|
||||||
}
|
|
||||||
.llm_chatui DropDownList:hover {
|
|
||||||
border-color: var(--primary);
|
|
||||||
}
|
|
||||||
.llm_conversation {
|
.llm_conversation {
|
||||||
margin-bottom: 8dp;
|
margin-bottom: 8dp;
|
||||||
}
|
}
|
||||||
@@ -137,14 +232,17 @@ DropDownList.role_ui {
|
|||||||
.llm_chatui .image {
|
.llm_chatui .image {
|
||||||
tint: var(--font);
|
tint: var(--font);
|
||||||
}
|
}
|
||||||
.llm_chat_attach {
|
.llm_chat_attach,
|
||||||
|
.llm_chat_select_model {
|
||||||
padding: 8dp 8dp 38dp 8dp;
|
padding: 8dp 8dp 38dp 8dp;
|
||||||
}
|
}
|
||||||
.llm_chat_locate_input {
|
.llm_chat_locate_input,
|
||||||
|
.llm_chat_select_model_input {
|
||||||
margin-bottom: 2dp;
|
margin-bottom: 2dp;
|
||||||
padding: 0 0 0 4dp;
|
padding: 0 0 0 4dp;
|
||||||
}
|
}
|
||||||
.llm_chat_attach_locate {
|
.llm_chat_attach_locate,
|
||||||
|
.llm_chat_model_locate {
|
||||||
border-radius: 8dp;
|
border-radius: 8dp;
|
||||||
margin-bottom: 4dp;
|
margin-bottom: 4dp;
|
||||||
}
|
}
|
||||||
@@ -155,6 +253,27 @@ DropDownList.role_ui {
|
|||||||
background-image: icon(spy-line, 16dp);
|
background-image: icon(spy-line, 16dp);
|
||||||
background-position: top 8dp right 8dp;
|
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>
|
</style>
|
||||||
<Splitter lw="mp" lh="mp" orientation="vertical" splitter-partition="75%" padding="4dp">
|
<Splitter lw="mp" lh="mp" orientation="vertical" splitter-partition="75%" padding="4dp">
|
||||||
<RelativeLayout lw="mp">
|
<RelativeLayout lw="mp">
|
||||||
@@ -172,17 +291,17 @@ DropDownList.role_ui {
|
|||||||
<TableView lw="mp" lh="0dp" lw8="1" class="llm_chat_attach_locate" />
|
<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")' />
|
<TextInput class="llm_chat_locate_input" lw="mp" lh="18dp" hint='@string(type_to_locate, "Type to Locate")' />
|
||||||
</vboxce>
|
</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">
|
<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_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_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_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" />
|
<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">
|
<PushButton class="model_ui" lw="0" lw8="1" lh="mp" margin-left="4dp" margin-right="4dp" tooltip="@string(select_model, Select Model)" />
|
||||||
<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 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" />
|
<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_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_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" />
|
<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>();
|
->asType<UISplitter>();
|
||||||
|
|
||||||
mChatsList = findByClass( "llm_chats" );
|
mChatsList = findByClass( "llm_chats" );
|
||||||
mModelDDL = findByClass<UIDropDownList>( "model_ui" );
|
mModelBtn = findByClass<UIPushButton>( "model_ui" );
|
||||||
|
|
||||||
// mRefreshModels = find<UIPushButton>( "refresh_model_ui" );
|
|
||||||
// mRefreshModels->onClick( [this]( auto ) { execute( "ai-refresh-local-models" ); } );
|
|
||||||
|
|
||||||
mChatMore = find<UIPushButton>( "llm_more" );
|
mChatMore = find<UIPushButton>( "llm_more" );
|
||||||
mChatMore->onClick( [this]( auto ) { execute( "ai-show-menu" ); } );
|
mChatMore->onClick( [this]( auto ) { execute( "ai-show-menu" ); } );
|
||||||
@@ -276,10 +392,6 @@ LLMChatUI::LLMChatUI( PluginManager* manager ) :
|
|||||||
mChatUserRole = find<UIPushButton>( "llm_user" );
|
mChatUserRole = find<UIPushButton>( "llm_user" );
|
||||||
mChatUserRole->onClick( [this]( auto ) { execute( "ai-chat-toggle-role" ); } );
|
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 ) {
|
auto setCmd = [this]( const std::string& name, const CommandCallback& cb ) {
|
||||||
setCommand( name, cb );
|
setCommand( name, cb );
|
||||||
mChatInput->getDocument().setCommand( name, cb );
|
mChatInput->getDocument().setCommand( name, cb );
|
||||||
@@ -387,9 +499,26 @@ LLMChatUI::LLMChatUI( PluginManager* manager ) :
|
|||||||
mLocateInput->getDocument().selectAll();
|
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] {
|
setCmd( "ai-toggle-private-chat", [this] {
|
||||||
mChatIsPrivate = !mChatIsPrivate;
|
mChatIsPrivate = !mChatIsPrivate;
|
||||||
/* mChatPrivate->toggleSelection(); */
|
|
||||||
|
|
||||||
if ( mChatIsPrivate )
|
if ( mChatIsPrivate )
|
||||||
mChatInput->addClass( "incognito" );
|
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 = find<UIPushButton>( "llm_chat_history" );
|
||||||
mChatHistory->onClick( [this]( auto ) { showChatHistory(); } );
|
mChatHistory->onClick( [this]( auto ) { showChatHistory(); } );
|
||||||
@@ -541,10 +670,13 @@ LLMChatUI::LLMChatUI( PluginManager* manager ) :
|
|||||||
mChatAttach = find<UIPushButton>( "llm_attach" );
|
mChatAttach = find<UIPushButton>( "llm_attach" );
|
||||||
mChatAttach->onClick( [this]( auto ) { execute( "ai-show-add-context-menu" ); } );
|
mChatAttach->onClick( [this]( auto ) { execute( "ai-show-add-context-menu" ); } );
|
||||||
|
|
||||||
|
mModelBtn->onClick( [this]( auto ) { execute( "ai-select-model" ); } );
|
||||||
|
|
||||||
if ( getPlugin() == nullptr )
|
if ( getPlugin() == nullptr )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
initAttachFile();
|
initAttachFile();
|
||||||
|
initSelectModel();
|
||||||
|
|
||||||
auto providers = getPlugin()->getProviders();
|
auto providers = getPlugin()->getProviders();
|
||||||
setProviders( std::move( providers ) );
|
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 ) {
|
const auto appendShortcutToTooltip = [this]( UIPushButton* but, const std::string& cmd ) {
|
||||||
auto kb = getKeyBindings().getCommandKeybindString( cmd );
|
auto kb = getKeyBindings().getCommandKeybindString( cmd );
|
||||||
@@ -583,10 +715,9 @@ LLMChatUI::LLMChatUI( PluginManager* manager ) :
|
|||||||
appendShortcutToTooltip( mChatStop, "ai-prompt" );
|
appendShortcutToTooltip( mChatStop, "ai-prompt" );
|
||||||
appendShortcutToTooltip( mChatAdd, "ai-add-chat" );
|
appendShortcutToTooltip( mChatAdd, "ai-add-chat" );
|
||||||
appendShortcutToTooltip( mChatSettings, "ai-settings" );
|
appendShortcutToTooltip( mChatSettings, "ai-settings" );
|
||||||
// appendShortcutToTooltip( mChatPrivate, "ai-toggle-private-chat" );
|
|
||||||
appendShortcutToTooltip( mChatMore, "ai-show-menu" );
|
appendShortcutToTooltip( mChatMore, "ai-show-menu" );
|
||||||
appendShortcutToTooltip( mChatUserRole, "ai-chat-toggle-role" );
|
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+keypad enter", "ai-prompt", true, false );
|
||||||
addKb( mChatInput, "mod+shift+keypad enter", "ai-add-chat", 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+l", "ai-refresh-local-models", bindToChatUI );
|
||||||
addKb( editor, "mod+shift+a", "ai-attach-file", bindToChatUI );
|
addKb( editor, "mod+shift+a", "ai-attach-file", bindToChatUI );
|
||||||
addKb( editor, "mod+shift+z", "ai-link-file", bindToChatUI );
|
addKb( editor, "mod+shift+z", "ai-link-file", bindToChatUI );
|
||||||
|
addKb( editor, "mod+shift+x", "ai-select-model", bindToChatUI );
|
||||||
|
|
||||||
if ( bindToChatUI )
|
if ( bindToChatUI )
|
||||||
addKb( editor, "mod+shift+return", "ai-add-chat", bindToChatUI );
|
addKb( editor, "mod+shift+return", "ai-add-chat", bindToChatUI );
|
||||||
@@ -663,6 +795,14 @@ std::optional<LLMModel> LLMChatUI::getModel( const std::string& provider,
|
|||||||
return {};
|
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() {
|
void LLMChatUI::showChatHistory() {
|
||||||
auto plugin = getPlugin();
|
auto plugin = getPlugin();
|
||||||
if ( plugin == nullptr )
|
if ( plugin == nullptr )
|
||||||
@@ -832,8 +972,10 @@ void LLMChatUI::showChatHistory() {
|
|||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLMChatUI::fillApiModels( UIDropDownList* modelDDL ) {
|
void LLMChatUI::fillApiModels() {
|
||||||
mPendingModelsToLoad = 0;
|
mPendingModelsToLoad = 0;
|
||||||
|
mNewModels.clear();
|
||||||
|
|
||||||
for ( auto& [name, data] : mProviders ) {
|
for ( auto& [name, data] : mProviders ) {
|
||||||
if ( !data.enabled || !data.fetchModelsUrl )
|
if ( !data.enabled || !data.fetchModelsUrl )
|
||||||
continue;
|
continue;
|
||||||
@@ -869,6 +1011,8 @@ void LLMChatUI::fillApiModels( UIDropDownList* modelDDL ) {
|
|||||||
model.displayName = el.value( "display_name", "" );
|
model.displayName = el.value( "display_name", "" );
|
||||||
|
|
||||||
model.isEphemeral = true;
|
model.isEphemeral = true;
|
||||||
|
model.hash = hashCombine( std::hash<std::string>()( model.name ),
|
||||||
|
std::hash<std::string>()( model.provider ) );
|
||||||
|
|
||||||
if ( model.name.empty() )
|
if ( model.name.empty() )
|
||||||
continue;
|
continue;
|
||||||
@@ -877,39 +1021,25 @@ void LLMChatUI::fillApiModels( UIDropDownList* modelDDL ) {
|
|||||||
model.maxOutputTokens = el.value( "max_context_length", 0 );
|
model.maxOutputTokens = el.value( "max_context_length", 0 );
|
||||||
|
|
||||||
data.models.emplace_back( model );
|
data.models.emplace_back( model );
|
||||||
|
mNewModels.push_back( model );
|
||||||
}
|
}
|
||||||
|
|
||||||
mPendingModelsToLoad++;
|
mPendingModelsToLoad++;
|
||||||
|
|
||||||
std::string pname = name;
|
runOnMainThread( [this] {
|
||||||
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 );
|
|
||||||
|
|
||||||
mPendingModelsToLoad--;
|
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();
|
onInit();
|
||||||
|
}
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -926,46 +1056,28 @@ String LLMChatUI::getModelDisplayName( const LLMModel& model ) const {
|
|||||||
data.displayName ? *data.displayName : String::capitalize( data.name ) );
|
data.displayName ? *data.displayName : String::capitalize( data.name ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LLMChatUI::selectModel( UIDropDownList* modelDDL, const LLMModel& model ) {
|
bool LLMChatUI::selectModel( std::optional<LLMModel> model ) {
|
||||||
auto modelName = getModelDisplayName( model );
|
if ( model ) {
|
||||||
auto index = modelDDL->getListBox()->getItemIndex( modelName );
|
mModelBtn->setText( getModelDisplayName( *model ) );
|
||||||
if ( index != eeINDEX_NOT_FOUND ) {
|
mCurModel = *model;
|
||||||
modelDDL->getListBox()->setSelected( index );
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLMChatUI::fillModelDropDownList( UIDropDownList* modelDDL ) {
|
void LLMChatUI::fillModelDropDownList() {
|
||||||
std::vector<String> models;
|
mModels.clear();
|
||||||
std::size_t selectedIndex = 0;
|
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 ) {
|
for ( const auto& [name, data] : mProviders ) {
|
||||||
if ( !data.enabled )
|
if ( !data.enabled )
|
||||||
continue;
|
continue;
|
||||||
|
for ( const auto& model : data.models )
|
||||||
for ( const auto& model : data.models ) {
|
mModels.push_back( model );
|
||||||
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 ) );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
modelDDL->getListBox()->clear();
|
getUISceneNode()->getThreadPool()->run( [this] { fillApiModels(); } );
|
||||||
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 ); } );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLMChatUI::resizeToFit( UICodeEditor* editor ) {
|
void LLMChatUI::resizeToFit( UICodeEditor* editor ) {
|
||||||
@@ -1077,8 +1189,8 @@ std::string LLMChatUI::unserialize( const nlohmann::json& payload ) {
|
|||||||
if ( mCurModel.name.empty() )
|
if ( mCurModel.name.empty() )
|
||||||
return payload.value( "input", "" );
|
return payload.value( "input", "" );
|
||||||
|
|
||||||
if ( !selectModel( mModelDDL, mCurModel ) )
|
if ( !selectModel( mCurModel ) )
|
||||||
fillModelDropDownList( mModelDDL );
|
fillModelDropDownList();
|
||||||
|
|
||||||
if ( payload.contains( "chat" ) && payload["chat"].is_object() ) {
|
if ( payload.contains( "chat" ) && payload["chat"].is_object() ) {
|
||||||
const auto& chat = payload["chat"];
|
const auto& chat = payload["chat"];
|
||||||
@@ -1488,10 +1600,10 @@ const LLMModel& LLMChatUI::getCheapestModelFromCurrentProvider() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LLMChatUI::onInit() {
|
void LLMChatUI::onInit() {
|
||||||
if ( !mModelDDL )
|
if ( !mModelBtn )
|
||||||
return;
|
return;
|
||||||
if ( getModelDisplayName( mCurModel ) != mModelDDL->getListBox()->getItemSelectedText() )
|
if ( getModelDisplayName( mCurModel ) != mModelBtn->getText() )
|
||||||
selectModel( mModelDDL, mCurModel );
|
selectModel( mCurModel );
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLMChatUI::updateTabTitle() {
|
void LLMChatUI::updateTabTitle() {
|
||||||
@@ -1549,9 +1661,12 @@ void LLMChatUI::updateLocateBarColumns() {
|
|||||||
mLocateTable->setColumnWidth( 1, width - mLocateTable->getColumnWidth( 0 ) );
|
mLocateTable->setColumnWidth( 1, width - mLocateTable->getColumnWidth( 0 ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// File picker
|
||||||
|
|
||||||
void LLMChatUI::showAttachFile() {
|
void LLMChatUI::showAttachFile() {
|
||||||
if ( getPlugin() == nullptr )
|
if ( getPlugin() == nullptr )
|
||||||
return;
|
return;
|
||||||
|
hideSelectModel();
|
||||||
auto text = mLocateInput->getText();
|
auto text = mLocateInput->getText();
|
||||||
auto ctx = getPlugin()->getPluginContext();
|
auto ctx = getPlugin()->getPluginContext();
|
||||||
if ( !ctx->isDirTreeReady() ) {
|
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
|
} // namespace ecode
|
||||||
|
|||||||
@@ -110,20 +110,28 @@ class LLMChatUI : public UILinearLayout, public WidgetCommandExecuter {
|
|||||||
UIPushButton* mChatAttach{ nullptr };
|
UIPushButton* mChatAttach{ nullptr };
|
||||||
UISelectButton* mChatPrivate{ nullptr };
|
UISelectButton* mChatPrivate{ nullptr };
|
||||||
UIScrollView* mChatScrollView{ nullptr };
|
UIScrollView* mChatScrollView{ nullptr };
|
||||||
UIDropDownList* mModelDDL{ nullptr };
|
UIPushButton* mModelBtn{ nullptr };
|
||||||
|
|
||||||
|
// Locate file
|
||||||
UIVLinearLayoutCommandExecuter* mLocateBarLayout{ nullptr };
|
UIVLinearLayoutCommandExecuter* mLocateBarLayout{ nullptr };
|
||||||
UITextInput* mLocateInput{ nullptr };
|
UITextInput* mLocateInput{ nullptr };
|
||||||
UITableView* mLocateTable{ nullptr };
|
UITableView* mLocateTable{ nullptr };
|
||||||
|
|
||||||
|
// Select model
|
||||||
|
UIVLinearLayoutCommandExecuter* mLocateModelBarLayout{ nullptr };
|
||||||
|
UITextInput* mLocateModelInput{ nullptr };
|
||||||
|
UITableView* mLocateModelTable{ nullptr };
|
||||||
|
|
||||||
std::unique_ptr<LLMChatCompletionRequest> mRequest;
|
std::unique_ptr<LLMChatCompletionRequest> mRequest;
|
||||||
std::unique_ptr<LLMChatCompletionRequest> mSummaryRequest;
|
std::unique_ptr<LLMChatCompletionRequest> mSummaryRequest;
|
||||||
LLMProviders mProviders;
|
LLMProviders mProviders;
|
||||||
LLMModel mCurModel;
|
LLMModel mCurModel;
|
||||||
std::unordered_map<String::HashType, LLMModel> mModelsMap;
|
std::vector<LLMModel> mModels;
|
||||||
int mPendingModelsToLoad{ 0 };
|
int mPendingModelsToLoad{ 0 };
|
||||||
bool mChatIsPrivate{ false };
|
bool mChatIsPrivate{ false };
|
||||||
bool mChatLocked{ false };
|
bool mChatLocked{ false };
|
||||||
bool mLinkMode{ false };
|
bool mLinkMode{ false };
|
||||||
|
std::vector<LLMModel> mNewModels;
|
||||||
|
|
||||||
LLMModel findModel( const std::string& provider, const std::string& model );
|
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 );
|
UIWidget* addChatUI( LLMChat::Role role );
|
||||||
|
|
||||||
void fillApiModels( UIDropDownList* modelDDL );
|
void fillApiModels();
|
||||||
|
|
||||||
String getModelDisplayName( const LLMModel& model ) const;
|
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 );
|
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( const std::string& provider, const std::string& modelName );
|
||||||
|
|
||||||
|
std::optional<LLMModel> getModel( Uint64 hash );
|
||||||
|
|
||||||
void saveChat();
|
void saveChat();
|
||||||
|
|
||||||
void onInit();
|
void onInit();
|
||||||
@@ -188,12 +198,22 @@ class LLMChatUI : public UILinearLayout, public WidgetCommandExecuter {
|
|||||||
|
|
||||||
void initAttachFile();
|
void initAttachFile();
|
||||||
|
|
||||||
|
void initSelectModel();
|
||||||
|
|
||||||
void updateLocateBarColumns();
|
void updateLocateBarColumns();
|
||||||
|
|
||||||
void showAttachFile();
|
void showAttachFile();
|
||||||
|
|
||||||
void hideAttachFile();
|
void hideAttachFile();
|
||||||
|
|
||||||
|
void updateLocateModelBarColumns();
|
||||||
|
|
||||||
|
void loadSelectModel();
|
||||||
|
|
||||||
|
void showSelectModel();
|
||||||
|
|
||||||
|
void hideSelectModel();
|
||||||
|
|
||||||
void insertFileToDocument( std::string path, std::shared_ptr<TextDocument> cdoc );
|
void insertFileToDocument( std::string path, std::shared_ptr<TextDocument> cdoc );
|
||||||
|
|
||||||
void replaceFileLinksToContents( std::string& text );
|
void replaceFileLinksToContents( std::string& text );
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ struct LLMCacheConfiguration {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct LLMModel {
|
struct LLMModel {
|
||||||
|
std::size_t hash{ 0 };
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string provider;
|
std::string provider;
|
||||||
std::optional<std::string> displayName;
|
std::optional<std::string> displayName;
|
||||||
|
|||||||
@@ -217,16 +217,20 @@ void StatusBuildOutputController::runBuild( const std::string& buildName,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ( enableBuildButton && buildButton )
|
if ( enableBuildButton && buildButton )
|
||||||
buildButton->setEnabled( true );
|
buildButton->ensureMainThread( [buildButton] { buildButton->setEnabled( true ); } );
|
||||||
|
|
||||||
if ( enableCleanButton && cleanButton )
|
if ( enableCleanButton && cleanButton )
|
||||||
cleanButton->runOnMainThread( [cleanButton] { cleanButton->setEnabled( true ); } );
|
cleanButton->ensureMainThread( [cleanButton] { cleanButton->setEnabled( true ); } );
|
||||||
|
|
||||||
if ( buildAndRunButton )
|
if ( buildAndRunButton ) {
|
||||||
buildAndRunButton->setEnabled( true );
|
buildAndRunButton->ensureMainThread(
|
||||||
|
[buildAndRunButton] { buildAndRunButton->setEnabled( true ); } );
|
||||||
|
}
|
||||||
|
|
||||||
mBuildButton->setEnabled( true );
|
mBuildButton->ensureMainThread( [this] {
|
||||||
mStopButton->setEnabled( false );
|
mBuildButton->setEnabled( true );
|
||||||
|
mStopButton->setEnabled( false );
|
||||||
|
} );
|
||||||
};
|
};
|
||||||
|
|
||||||
auto res = pbm->build(
|
auto res = pbm->build(
|
||||||
|
|||||||
@@ -148,6 +148,8 @@ UITreeViewFS::UITreeViewFS() : UITreeView(), mKeyBindings( getInput() ) {
|
|||||||
mCommands["paste"] = [this] {
|
mCommands["paste"] = [this] {
|
||||||
if ( mWasCut )
|
if ( mWasCut )
|
||||||
moveFile( mSrcCopy, getSelectionPath() );
|
moveFile( mSrcCopy, getSelectionPath() );
|
||||||
|
else
|
||||||
|
copyFile( mSrcCopy, getSelectionPath() );
|
||||||
};
|
};
|
||||||
|
|
||||||
mKeyBindings.addKeybinds( getDefaultKeybindings() );
|
mKeyBindings.addKeybinds( getDefaultKeybindings() );
|
||||||
|
|||||||
@@ -317,7 +317,8 @@ UniversalLocator::UniversalLocator( UICodeEditorSplitter* editorSplitter, UIScen
|
|||||||
mApp->getSettingsMenu()->updateCurrentFileType();
|
mApp->getSettingsMenu()->updateCurrentFileType();
|
||||||
mLocateBarLayout->execute( "close-locatebar" );
|
mLocateBarLayout->execute( "close-locatebar" );
|
||||||
}
|
}
|
||||||
} } );
|
},
|
||||||
|
nullptr, false } );
|
||||||
|
|
||||||
mApp->getPluginManager()->subscribeMessages(
|
mApp->getPluginManager()->subscribeMessages(
|
||||||
"universallocator", [this]( const PluginMessage& msg ) -> PluginRequestHandle {
|
"universallocator", [this]( const PluginMessage& msg ) -> PluginRequestHandle {
|
||||||
|
|||||||
Reference in New Issue
Block a user