From 1e0324be3aee1de197320ee2aa153ca446c330b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Sat, 4 Jul 2020 03:04:44 -0300 Subject: [PATCH 1/7] WIP. --- include/eepp/ui/abstract/model.hpp | 91 + .../eepp/ui/abstract/modeleditingdelegate.hpp | 48 + include/eepp/ui/abstract/modelindex.hpp | 42 + include/eepp/ui/abstract/modelselection.hpp | 70 + include/eepp/ui/uiabstractview.hpp | 74 + premake4.lua | 4 +- premake5.lua | 1710 ++++++++--------- projects/android-project/app/jni/eepp.mk | 2 +- projects/linux/ee.creator.user | 2 +- projects/linux/ee.files | 13 + src/eepp/ui/abstract/model.cpp | 42 + src/eepp/ui/abstract/modelselection.cpp | 62 + src/eepp/ui/uiabstractview.cpp | 59 + 13 files changed, 1360 insertions(+), 859 deletions(-) create mode 100644 include/eepp/ui/abstract/model.hpp create mode 100644 include/eepp/ui/abstract/modeleditingdelegate.hpp create mode 100644 include/eepp/ui/abstract/modelindex.hpp create mode 100644 include/eepp/ui/abstract/modelselection.hpp create mode 100644 include/eepp/ui/uiabstractview.hpp create mode 100644 src/eepp/ui/abstract/model.cpp create mode 100644 src/eepp/ui/abstract/modelselection.cpp create mode 100644 src/eepp/ui/uiabstractview.cpp diff --git a/include/eepp/ui/abstract/model.hpp b/include/eepp/ui/abstract/model.hpp new file mode 100644 index 000000000..33a814d44 --- /dev/null +++ b/include/eepp/ui/abstract/model.hpp @@ -0,0 +1,91 @@ +#ifndef EE_UI_MODEL_MODEL_HPP +#define EE_UI_MODEL_MODEL_HPP + +#include +#include +#include +#include + +namespace EE { namespace UI { +class UIAbstractView; +}} // namespace EE::UI + +namespace EE { namespace UI { namespace Abstract { + +enum class SortOrder { None, Ascending, Descending }; + +class Variant; + +class EE_API Model { + public: + enum UpdateFlag { + DontInvalidateIndexes = 0, + InvalidateAllIndexes = 1 << 0, + }; + + enum class Role { + Display, + Sort, + Custom, + ForegroundColor, + BackgroundColor, + Icon, + Font, + DragData, + TextAlignment, + }; + + virtual ~Model(){}; + + virtual int rowCount( const ModelIndex& = ModelIndex() ) const = 0; + virtual int columnCount( const ModelIndex& = ModelIndex() ) const = 0; + virtual std::string columnName( int ) const { return {}; } + virtual Variant data( const ModelIndex&, Role = Role::Display ) const = 0; + virtual void update() = 0; + virtual ModelIndex parentIndex( const ModelIndex& ) const { return {}; } + virtual ModelIndex index( int row, int column = 0, const ModelIndex& = ModelIndex() ) const { + return createIndex( row, column ); + } + virtual ModelIndex sibling( int row, int column, const ModelIndex& parent ) const; + virtual void setData( const ModelIndex&, const Variant& ) {} + virtual int treeColumn() const { return 0; } + virtual bool acceptsDrag( const ModelIndex&, const std::string& dataType ); + + virtual bool isColumnSortable( int /*columnIndex*/ ) const { return true; } + + virtual std::string dragDataType() const { return {}; } + + bool isValid( const ModelIndex& index ) const { + auto parentIndex = this->parentIndex( index ); + return index.row() >= 0 && index.row() < rowCount( parentIndex ) && index.column() >= 0 && + index.column() < columnCount( parentIndex ); + } + + virtual int keyColumn() const { return -1; } + virtual SortOrder sortOrder() const { return SortOrder::None; } + virtual void setKeyColumnAndSortOrder( int, SortOrder ) {} + + void registerView( UIAbstractView* ); + void unregisterView( UIAbstractView* ); + + std::function onUpdate; + + protected: + Model(){}; + + void forEachView( std::function ); + void didUpdate( unsigned flags = UpdateFlag::InvalidateAllIndexes ); + + ModelIndex createIndex( int row, int column, const void* data = nullptr ) const; + + private: + std::unordered_set mViews; +}; + +inline ModelIndex ModelIndex::parent() const { + return mModel ? mModel->parentIndex( *this ) : ModelIndex(); +} + +}}} // namespace EE::UI::Abstract + +#endif // EE_UI_MODEL_MODEL_HPP diff --git a/include/eepp/ui/abstract/modeleditingdelegate.hpp b/include/eepp/ui/abstract/modeleditingdelegate.hpp new file mode 100644 index 000000000..dbf2ec4dd --- /dev/null +++ b/include/eepp/ui/abstract/modeleditingdelegate.hpp @@ -0,0 +1,48 @@ +#ifndef EE_UI_MODELEDITINGDELEGATE_HPP +#define EE_UI_MODELEDITINGDELEGATE_HPP + +#include +#include +#include + +namespace EE { namespace UI { namespace Abstract { + +class EE_API ModelEditingDelegate { + public: + virtual ~ModelEditingDelegate() {} + + void bind( std::shared_ptr model, const ModelIndex& index ) { + if ( mModel.get() == model.get() && mIndex == index ) + return; + mModel = model; + mIndex = index; + mWidget = createWidget(); + } + + UIWidget* getWidget() { return mWidget; } + UIWidget* getWidget() const { return mWidget; } + + std::function onCommit; + + virtual Variant value() const = 0; + virtual void setValue( const Variant& ) = 0; + virtual void willBeginEditing() {} + + protected: + ModelEditingDelegate() {} + + virtual UIWidget* createWidget() = 0; + + void commit() { + if ( onCommit ) + onCommit(); + } + + std::shared_ptr mModel; + ModelIndex mIndex; + UIWidget* mWidget; +}; + +}}} // namespace EE::UI::Abstract + +#endif // MODELEDITINGDELEGATE_HPP diff --git a/include/eepp/ui/abstract/modelindex.hpp b/include/eepp/ui/abstract/modelindex.hpp new file mode 100644 index 000000000..14b86a4b5 --- /dev/null +++ b/include/eepp/ui/abstract/modelindex.hpp @@ -0,0 +1,42 @@ +#ifndef EE_UI_MODEL_MODELINDEX_HPP +#define EE_UI_MODEL_MODELINDEX_HPP + +#include + +namespace EE { namespace UI { namespace Abstract { + +class Model; + +class EE_API ModelIndex { + public: + ModelIndex() {} + + bool isValid() const { return mRow != -1 && mColumn != -1; } + + const Int64& row() const { return mRow; } + const Int64& column() const { return mColumn; } + void* data() const { return mData; } + ModelIndex parent() const; + bool hasParent() const { return parent().isValid(); } + + bool operator==( const ModelIndex& other ) const { + return mModel == other.mModel && mRow == other.mRow && mColumn == other.mColumn && + mData == other.mData; + } + + bool operator!=( const ModelIndex& other ) const { return !( *this == other ); } + + protected: + friend class Model; + const Model* mModel{nullptr}; + Int64 mRow{-1}; + Int64 mColumn{-1}; + void* mData{nullptr}; + + ModelIndex( const Model& model, int row, int column, void* internalData ) : + mModel( &model ), mRow( row ), mColumn( column ), mData( internalData ) {} +}; + +}}} // namespace EE::UI::Model + +#endif // EE_UI_MODEL_MODELINDEX_HPP diff --git a/include/eepp/ui/abstract/modelselection.hpp b/include/eepp/ui/abstract/modelselection.hpp new file mode 100644 index 000000000..5b87d55f5 --- /dev/null +++ b/include/eepp/ui/abstract/modelselection.hpp @@ -0,0 +1,70 @@ +#ifndef EE_UI_MODEL_MODELSELECTION_HPP +#define EE_UI_MODEL_MODELSELECTION_HPP + +#include +#include +#include +#include + +namespace EE { namespace UI { +class UIAbstractView; +}} // namespace EE::UI + +namespace EE { namespace UI { namespace Abstract { + +class EE_API ModelSelection { + public: + ModelSelection( UIAbstractView* view ) : mView( view ) {} + + int size() const { return mIndexes.size(); } + bool isEmpty() const { return mIndexes.empty(); } + bool contains( const ModelIndex& index ) const { + return std::find( mIndexes.begin(), mIndexes.end(), index ) != mIndexes.end(); + } + bool containsRow( int row ) const { + for ( auto& index : mIndexes ) { + if ( index.row() == row ) + return true; + } + return false; + } + + void set( const ModelIndex& ); + void add( const ModelIndex& ); + void toggle( const ModelIndex& ); + bool remove( const ModelIndex& ); + void clear(); + + template void forEachIndex( Callback callback ) { + for ( auto& index : indexes() ) + callback( index ); + } + + template void forEachIndex( Callback callback ) const { + for ( auto& index : indexes() ) + callback( index ); + } + + std::vector indexes() const { + std::vector selectedIndexes; + for ( auto& index : mIndexes ) + selectedIndexes.push_back( index ); + return selectedIndexes; + } + + ModelIndex first() const { + if ( mIndexes.empty() ) + return {}; + return *mIndexes.begin(); + } + + void removeMatching( std::function ); + + protected: + UIAbstractView* mView; + std::vector mIndexes; +}; + +}}} // namespace EE::UI::Abstract + +#endif // EE_UI_MODEL_MODELSELECTION_HPP diff --git a/include/eepp/ui/uiabstractview.hpp b/include/eepp/ui/uiabstractview.hpp new file mode 100644 index 000000000..b13bbcbaa --- /dev/null +++ b/include/eepp/ui/uiabstractview.hpp @@ -0,0 +1,74 @@ +#ifndef EE_UI_UIABSTRACTVIEW_HPP +#define EE_UI_UIABSTRACTVIEW_HPP + +#include +#include +#include +#include +#include + +using namespace EE::UI::Abstract; + +namespace EE { namespace UI { + +class EE_API UIAbstractView : public UIWidget { + public: + void set_model( std::shared_ptr ); + Model* model() { return m_model.get(); } + const Model* model() const { return m_model.get(); } + + ModelSelection& selection() { return m_selection; } + const ModelSelection& selection() const { return m_selection; } + virtual void select_all() = 0; + + bool is_editable() const { return m_editable; } + void set_editable( bool editable ) { m_editable = editable; } + + virtual void didUpdateModel( unsigned flags ); + virtual void didUpdateSelection(); + + virtual Vector2i content_rect( const ModelIndex& ) const { return {}; } + virtual ModelIndex index_at_event_position( const Vector2i& ) const = 0; + + void set_activates_on_selection( bool b ) { m_activates_on_selection = b; } + bool activates_on_selection() const { return m_activates_on_selection; } + + std::function on_selection_change; + std::function on_activation; + std::function on_selection; + std::function on_drop; + + //std::function( const ModelIndex& )> aid_create_editing_delegate; + + void notifySelectionChange(); + + protected: + UIAbstractView(); + + virtual ~UIAbstractView(); + + virtual void did_scroll(); + void set_hovered_index( const ModelIndex& ); + void activate( const ModelIndex& ); + void activate_selected(); + + bool m_editable{false}; + ModelIndex m_edit_index; + UIWidget* m_edit_widget; + Vector2i m_edit_widget_content_rect; + + Vector2i m_left_mousedown_position; + bool m_might_drag{false}; + + ModelIndex m_hovered_index; + + private: + std::shared_ptr m_model; + std::unique_ptr m_editing_delegate; + ModelSelection m_selection; + bool m_activates_on_selection{false}; +}; + +}} // namespace EE::UI + +#endif // EE_UI_UIABSTRACTVIEW_HPP diff --git a/premake4.lua b/premake4.lua index 595a8ad55..c54c0060e 100644 --- a/premake4.lua +++ b/premake4.lua @@ -433,7 +433,7 @@ function build_link_configuration( package_name, use_ee_icon ) end fix_shared_lib_linking_path( package_name, "libeepp-debug" ) - + if not os.is_real("emscripten") then targetname ( package_name .. "-debug" .. extension ) else @@ -748,7 +748,7 @@ function build_eepp( build_name ) "src/eepp/scene/*.cpp", "src/eepp/scene/actions/*.cpp", "src/eepp/ui/*.cpp", - "src/eepp/ui/actions/*.cpp", + "src/eepp/ui/abstract/*.cpp", "src/eepp/ui/css/*.cpp", "src/eepp/ui/doc/*.cpp", "src/eepp/ui/tools/*.cpp", diff --git a/premake5.lua b/premake5.lua index cebee0a06..484a0abfa 100644 --- a/premake5.lua +++ b/premake5.lua @@ -1,855 +1,855 @@ -newoption { trigger = "with-openssl", description = "Enables OpenSSL support ( and disables mbedtls backend )." } -newoption { trigger = "with-dynamic-freetype", description = "Dynamic link against freetype." } -newoption { trigger = "with-static-eepp", description = "Force to build the demos and tests with eepp compiled statically" } -newoption { trigger = "with-static-backend", description = "It will try to compile the library with a static backend (only for gcc and mingw).\n\t\t\t\tThe backend should be placed in libs/your_platform/libYourBackend.a" } -newoption { trigger = "with-gles2", description = "Compile with GLES2 support" } -newoption { trigger = "with-gles1", description = "Compile with GLES1 support" } -newoption { trigger = "with-mojoal", description = "Compile with mojoAL as OpenAL implementation instead of using openal-soft (requires SDL2 backend)" } -newoption { trigger = "use-frameworks", description = "In macOS it will try to link the external libraries from its frameworks. For example, instead of linking against SDL2 it will link against SDL2.framework." } -newoption { trigger = "windows-vc-build", description = "This is used to build the framework in Visual Studio downloading its external dependencies and making them available to the VS project without having to install them manually." } -newoption { - trigger = "with-backend", - description = "Select the backend to use for window and input handling.\n\t\t\tIf no backend is selected or if the selected is not installed the script will search for a backend present in the system, and will use it.", - allowed = { - { "SDL2", "SDL2" }, - } -} - -function print_table( table_ref ) - for _, value in pairs( table_ref ) do - print(value) - end -end - -function table_length(T) - local count = 0 - for _ in pairs(T) do count = count + 1 end - return count -end - -function multiple_insert( parent_table, insert_table ) - for _, value in pairs( insert_table ) do - table.insert( parent_table, value ) - end -end - -function os_findlib( name ) - if os.istarget("macosx") and ( is_xcode() or _OPTIONS["use-frameworks"] ) then - local path = "/Library/Frameworks/" .. name .. ".framework" - - if os.isdir( path ) then - return path - end - end - - return os.findlib( name ) -end - -function get_backend_link_name( name ) - if os.istarget("macosx") and ( is_xcode() or _OPTIONS["use-frameworks"] ) then - local fname = name .. ".framework" - - if os_findlib( name ) then -- Search for the framework - return fname - end - end - - return name -end - -function string.starts(String,Start) - if ( _ACTION ) then - return string.sub(String,1,string.len(Start))==Start - end - - return false -end - -function is_vs() - return ( string.starts(_ACTION,"vs") ) -end - -function is_xcode() - return ( string.starts(_ACTION,"xcode") ) -end - -function set_kind() - if os.istarget("macosx") then - kind("ConsoleApp") - else - kind("WindowedApp") - end -end - -link_list = { } -os_links = { } -backends = { } -static_backends = { } -backend_selected = false -remote_sdl2_version = "SDL2-2.0.12" -remote_sdl2_devel_src_url = "https://libsdl.org/release/SDL2-2.0.12.zip" -remote_sdl2_devel_vc_url = "https://www.libsdl.org/release/SDL2-devel-2.0.12-VC.zip" - -function incdirs( dirs ) - if is_xcode() then - sysincludedirs { dirs } - end - includedirs { dirs } -end - -function download_and_extract_sdl(sdl_url) - print("Downloading: " .. sdl_url) - local dest_dir = "src/thirdparty/" - local local_file = dest_dir .. remote_sdl2_version .. ".zip" - local result_str, response_code = http.download(sdl_url, local_file) - if response_code == 200 then - print("Downloaded successfully to: " .. local_file) - zip.extract(local_file, dest_dir) - print("Extracted " .. local_file .. " into " .. dest_dir) - else - print("Failed to download: " .. sdl_url) - exit(1) - end -end - -function download_and_extract_dependencies() - if not os.isdir("src/thirdparty/" .. remote_sdl2_version) then - if _OPTIONS["windows-vc-build"] then - download_and_extract_sdl(remote_sdl2_devel_vc_url) - elseif os.istarget("ios") then - download_and_extract_sdl(remote_sdl2_devel_src_url) - end - end -end - -function build_base_configuration( package_name ) - incdirs { "src/thirdparty/zlib" } - - set_ios_config() - set_xcode_config() - - filter "not system:windows" - buildoptions{ "-fPIC" } - - filter "configurations:debug*" - targetname ( package_name .. "-debug" ) - - filter "configurations:release*" - targetname ( package_name ) - - filter "action:not vs*" - cdialect "gnu99" - buildoptions { "-Wall" } - - filter "action:vs*" - incdirs { "src/thirdparty/libzip/vs" } -end - -function build_base_cpp_configuration( package_name ) - if not os.istarget("windows") then - buildoptions{ "-fPIC" } - end - - set_ios_config() - set_xcode_config() - - filter "action:not vs*" - buildoptions { "-Wall" } - - filter "configurations:debug*" - defines { "DEBUG" } - symbols "On" - targetname ( package_name .. "-debug" ) - - filter "configurations:release*" - optimize "Speed" - targetname ( package_name ) -end - -function build_link_configuration( package_name, use_ee_icon ) - incdirs { "include" } - local extension = ""; - - if package_name == "eepp" then - defines { "EE_EXPORTS" } - elseif package_name == "eepp-static" then - defines { "EE_STATIC" } - end - - if package_name ~= "eepp" and package_name ~= "eepp-static" then - if not _OPTIONS["with-static-eepp"] then - links { "eepp-shared" } - else - links { "eepp-static" } - defines { "EE_STATIC" } - add_static_links() - links { link_list } - end - end - - set_ios_config() - set_xcode_config() - - filter { "system:windows", "action:not vs*" } - if ( true == use_ee_icon ) then - linkoptions { "../../bin/assets/icon/ee.res" } - end - - filter { "system:windows", "action:vs*" } - files { "bin/assets/icon/ee.rc", "bin/assets/icon/ee.ico" } - vpaths { ['Resources/*'] = { "ee.rc", "ee.ico" } } - - filter "action:not vs*" - cppdialect "C++14" - buildoptions { "-Wall" } - - filter { "configurations:debug*", "action:not vs*" } - buildoptions{ "-Wno-long-long" } - - filter { "configurations:release*", "action:not vs*" } - buildoptions { "-fno-strict-aliasing -ffast-math" } - - filter { "configurations:release*", "action:not vs*", "system:not macosx" } - buildoptions { "-s" } - - filter "configurations:debug*" - defines { "DEBUG", "EE_DEBUG", "EE_MEMORY_MANAGER" } - targetname ( package_name .. "-debug" .. extension ) - - filter "configurations:release*" - defines { "NDEBUG" } - targetname ( package_name .. extension ) - - filter { "system:windows", "action:not vs*" } - linkoptions { "-static-libgcc", "-static-libstdc++" } - - filter { "system:windows", "action:vs*" } - if table.contains( backends, "SDL2" ) then - links { "SDL2", "SDL2main" } - end - - filter { "options:windows-vc-build", "system:windows", "platforms:x86" } - syslibdirs { "src/thirdparty/" .. remote_sdl2_version .."/lib/x86" } - - filter { "options:windows-vc-build", "system:windows", "platforms:x86_64" } - syslibdirs { "src/thirdparty/" .. remote_sdl2_version .."/lib/x64" } - - filter "system:emscripten" - targetname ( package_name .. extension ) - linkoptions{ "-O2 -s TOTAL_MEMORY=67108864 -s ASM_JS=1 -s VERBOSE=1 -s DISABLE_EXCEPTION_CATCHING=0 -s USE_SDL=2 -s ERROR_ON_UNDEFINED_SYMBOLS=0 -s ERROR_ON_MISSING_LIBRARIES=0 -s FULL_ES3=1 -s \"BINARYEN_TRAP_MODE='clamp'\"" } - buildoptions { "-fno-strict-aliasing -O2 -s USE_SDL=2 -s PRECISE_F32=1 -s ENVIRONMENT=web" } - - if _OPTIONS["with-gles1"] and ( not _OPTIONS["with-gles2"] or _OPTIONS["force-gles1"] ) then - linkoptions{ "-s LEGACY_GL_EMULATION=1" } - end - - if _OPTIONS["with-gles2"] and not _OPTIONS["force-gles1"] then - linkoptions{ "-s FULL_ES2=1" } - end -end - -function generate_os_links() - if os.istarget("linux") then - multiple_insert( os_links, { "rt", "pthread", "X11", "GL", "Xcursor" } ) - - if _OPTIONS["with-static-eepp"] then - table.insert( os_links, "dl" ) - end - elseif os.istarget("windows") then - multiple_insert( os_links, { "opengl32", "glu32", "gdi32", "ws2_32", "winmm", "ole32" } ) - elseif os.istarget("mingw32") then - multiple_insert( os_links, { "opengl32", "glu32", "gdi32", "ws2_32", "winmm", "ole32" } ) - elseif os.istarget("macosx") then - multiple_insert( os_links, { "OpenGL.framework", "CoreFoundation.framework" } ) - elseif os.istarget("bsd") then - multiple_insert( os_links, { "rt", "pthread", "X11", "GL", "Xcursor" } ) - elseif os.istarget("haiku") then - multiple_insert( os_links, { "GL", "network" } ) - elseif os.istarget("ios") then - multiple_insert( os_links, { "OpenGLES.framework", "AudioToolbox.framework", "CoreAudio.framework", "Foundation.framework", "CoreFoundation.framework", "UIKit.framework", "QuartzCore.framework", "CoreGraphics.framework", "CoreMotion.framework", "AVFoundation.framework", "GameController.framework" } ) - elseif os.istarget("android") then - multiple_insert( os_links, { "GLESv1_CM", "GLESv2", "log" } ) - end - - if not _OPTIONS["with-mojoal"] then - if os.istarget("linux") or os.istarget("bsd") or os.istarget("haiku") or os.istarget("emscripten") then - multiple_insert( os_links, { "openal" } ) - elseif os.istarget("windows") or os.istarget("mingw32") then - multiple_insert( os_links, { "OpenAL32" } ) - elseif os.istarget("macosx") or os.istarget("ios") then - multiple_insert( os_links, { "OpenAL.framework" } ) - end - end -end - -function parse_args() - if _OPTIONS["with-gles2"] then - defines { "EE_GLES2", "SOIL_GLES2" } - end - - if _OPTIONS["with-gles1"] then - defines { "EE_GLES1", "SOIL_GLES1" } - end -end - -function add_static_links() - -- The linking order DOES matter - -- Expose the symbols that need one static library AFTER adding that static lib - - -- Add static backends - if next(static_backends) ~= nil then - for _, value in pairs( static_backends ) do - linkoptions { value } - end - end - - if not _OPTIONS["with-dynamic-freetype"] then - links { "freetype-static" } - end - - links { "SOIL2-static", - "chipmunk-static", - "libzip-static", - "jpeg-compressor-static", - "zlib-static", - "imageresampler-static", - "pugixml-static", - "vorbis-static" - } - - if _OPTIONS["with-mojoal"] then - links { "mojoal-static"} - end - - if not _OPTIONS["with-openssl"] then - links { "mbedtls-static" } - end - - if not os.istarget("haiku") and not os.istarget("ios") and not os.istarget("android") and not os.istarget("emscripten") then - links{ "glew-static" } - end -end - -function can_add_static_backend( name ) - if _OPTIONS["with-static-backend"] then - return true - end - return false -end - -function insert_static_backend( name ) - table.insert( static_backends, "../../libs/" .. os.target() .. "/lib" .. name .. ".a" ) -end - -function add_sdl2() - print("Using SDL2 backend"); - if not can_add_static_backend("SDL2") then - table.insert( link_list, get_backend_link_name( "SDL2" ) ) - else - print("Using static backend") - insert_static_backend( "SDL2" ) - end - - table.insert( backends, "SDL2" ) -end - -function set_xcode_config() - if is_xcode() or _OPTIONS["use-frameworks"] then - linkoptions { "-F /Library/Frameworks" } - incdirs { "/Library/Frameworks/SDL2.framework/Headers" } - defines { "EE_SDL2_FROM_ROOTPATH" } - end -end - -function set_ios_config() - if os.istarget("ios") then - local toolchainpath = os.getenv("TOOLCHAINPATH") - local iosversion = os.getenv("IOSVERSION") - local sysroot_path = os.getenv("SYSROOTPATH") - - if nil == os.getenv("TOOLCHAINPATH") then - toolchainpath = os.outputof("xcrun -find -sdk iphonesimulator clang") - end - - if nil == os.getenv("IOSVERSION") then - iosversion = os.outputof("xcrun --sdk iphonesimulator --show-sdk-version") - end - - if nil == os.getenv("SYSROOTPATH") then - local platform_path = os.outputof("xcrun --sdk iphonesimulator --show-sdk-platform-path") - sysroot_path = platform_path .. "/Developer/SDKs/iPhoneSimulator" .. iosversion .. ".sdk/" - end - - local framework_path = sysroot_path .. "/System/Library/Frameworks" - local framework_libs_path = framework_path .. "/usr/lib" - local sysroot_ver = " -miphoneos-version-min=9.0 -isysroot " .. sysroot_path - - buildoptions { sysroot_ver .. " -I" .. sysroot_path .. "/usr/include" } - linkoptions { sysroot_ver } - libdirs { framework_libs_path } - linkoptions { " -F" .. framework_path .. " -L" .. framework_libs_path .. " -isysroot " .. sysroot_path } - includedirs { "src/thirdparty/" .. remote_sdl2_version .. "/include" } - end -end - -function backend_is( name, libname ) - if not _OPTIONS["with-backend"] then - _OPTIONS["with-backend"] = "SDL2" - end - - if next(backends) == nil then - backends = string.explode(_OPTIONS["with-backend"],",") - end - - local backend_sel = table.contains( backends, name ) - - local ret_val = os_findlib( libname ) and backend_sel - - if os.istarget("mingw32") or os.istarget("emscripten") then - ret_val = backend_sel - end - - if ret_val then - backend_selected = true - end - - return ret_val -end - -function select_backend() - if backend_is("SDL2", "SDL2") then - print("Selected SDL2") - add_sdl2() - end - - -- If the selected backend is not present, try to find one present - if not backend_selected then - if os_findlib("SDL2", "SDL2") then - add_sdl2() - else - print("ERROR: Couldnt find any backend. Forced SDL2.") - add_sdl2() - end - end -end - -function check_ssl_support() - if _OPTIONS["with-openssl"] then - if os.istarget("windows") then - table.insert( link_list, get_backend_link_name( "libssl" ) ) - table.insert( link_list, get_backend_link_name( "libcrypto" ) ) - else - table.insert( link_list, get_backend_link_name( "ssl" ) ) - table.insert( link_list, get_backend_link_name( "crypto" ) ) - end - - files { "src/eepp/network/ssl/backend/openssl/*.cpp" } - - defines { "EE_OPENSSL" } - else - files { "src/eepp/network/ssl/backend/mbedtls/*.cpp" } - - defines { "EE_MBEDTLS" } - end - - defines { "EE_SSL_SUPPORT" } -end - -function build_eepp( build_name ) - files { "src/eepp/core/*.cpp", - "src/eepp/math/*.cpp", - "src/eepp/system/*.cpp", - "src/eepp/audio/*.cpp", - "src/eepp/graphics/*.cpp", - "src/eepp/graphics/renderer/*.cpp", - "src/eepp/window/*.cpp", - "src/eepp/network/*.cpp", - "src/eepp/network/ssl/*.cpp", - "src/eepp/network/http/*.cpp", - "src/eepp/scene/*.cpp", - "src/eepp/scene/actions/*.cpp", - "src/eepp/ui/*.cpp", - "src/eepp/ui/actions/*.cpp", - "src/eepp/ui/css/*.cpp", - "src/eepp/ui/doc/*.cpp", - "src/eepp/ui/tools/*.cpp", - "src/eepp/physics/*.cpp", - "src/eepp/physics/constraints/*.cpp", - "src/eepp/maps/*.cpp", - "src/eepp/maps/mapeditor/*.cpp" - } - - incdirs { "include", - "src", - "src/thirdparty", - "include/eepp/thirdparty", - "src/thirdparty/freetype2/include", - "src/thirdparty/zlib", - "src/thirdparty/libogg/include", - "src/thirdparty/libvorbis/include", - "src/thirdparty/mbedtls/include" - } - - add_static_links() - check_ssl_support() - - if table.contains( backends, "SDL2" ) then - files { "src/eepp/window/backend/SDL2/*.cpp" } - defines { "EE_BACKEND_SDL_ACTIVE", "EE_SDL_VERSION_2" } - end - - multiple_insert( link_list, os_links ) - - links { link_list } - - build_link_configuration( build_name ) - - filter "options:use-frameworks" - defines { "EE_USE_FRAMEWORKS" } - - filter { "system:macosx", "action:xcode* or options:use-frameworks" } - libdirs { "/System/Library/Frameworks", "/Library/Frameworks" } - - filter "system:windows" - files { "src/eepp/system/platform/win/*.cpp" } - files { "src/eepp/network/platform/win/*.cpp" } - - filter "system:not windows" - files { "src/eepp/system/platform/posix/*.cpp" } - files { "src/eepp/network/platform/unix/*.cpp" } - - filter "options:with-mojoal" - defines( "AL_LIBTYPE_STATIC" ) - incdirs { "src/thirdparty/mojoAL" } - - filter "options:windows-vc-build" - incdirs { "src/thirdparty/" .. remote_sdl2_version .. "/include" } - - filter "action:vs*" - incdirs { "src/thirdparty/libzip/vs" } - - filter "action:not vs*" - cppdialect "C++14" - - filter "options:with-dynamic-freetype" - if not os.istarget("ios") and os_findlib("freetype") then - table.insert( link_list, get_backend_link_name( "freetype" ) ) - end -end - -workspace "eepp" - targetdir("./bin/") - if os.istarget("ios") then - configurations { "debug-x64", "debug-arm64", "release-x64", "release-arm64" } - platforms { "x86_64", "arm64" } - else - configurations { "debug", "release" } - platforms { "x86_64", "x86" } - end - rtti "On" - download_and_extract_dependencies() - select_backend() - generate_os_links() - parse_args() - location("./make/" .. os.target() .. "/") - objdir("obj/" .. os.target() .. "/") - - filter "platforms:x86" - architecture "x86" - - filter "platforms:arm64 or configurations:debug-arm64 or configurations:release-arm64" - architecture "arm64" - - filter "platforms:x86_64 or configurations:debug-x64 or configurations:release-x64" - architecture "x86_64" - - filter "system:macosx" - defines { "GL_SILENCE_DEPRECATION" } - - filter "configurations:debug*" - defines { "DEBUG" } - symbols "On" - filter "configurations:release*" - optimize "Speed" - - filter { "system:windows", "action:vs*" } - flags { "MultiProcessorCompile" } - - project "SOIL2-static" - kind "StaticLib" - language "C" - targetdir("libs/" .. os.target() .. "/thirdparty/") - files { "src/thirdparty/SOIL2/src/SOIL2/*.c" } - incdirs { "src/thirdparty/SOIL2" } - build_base_configuration( "SOIL2" ) - - project "glew-static" - kind "StaticLib" - language "C" - defines { "GLEW_NO_GLU", "GLEW_STATIC" } - targetdir("libs/" .. os.target() .. "/thirdparty/") - files { "src/thirdparty/glew/*.c" } - incdirs { "include/thirdparty/glew" } - build_base_configuration( "glew" ) - - project "mbedtls-static" - kind "StaticLib" - language "C" - targetdir("libs/" .. os.target() .. "/thirdparty/") - incdirs { "src/thirdparty/mbedtls/include/" } - files { "src/thirdparty/mbedtls/library/*.c" } - build_base_cpp_configuration( "mbedtls" ) - - project "vorbis-static" - kind "StaticLib" - language "C" - targetdir("libs/" .. os.target() .. "/thirdparty/") - incdirs { "src/thirdparty/libvorbis/lib/", "src/thirdparty/libogg/include", "src/thirdparty/libvorbis/include" } - files { "src/thirdparty/libogg/**.c", "src/thirdparty/libvorbis/**.c" } - build_base_cpp_configuration( "vorbis" ) - - project "pugixml-static" - kind "StaticLib" - language "C++" - targetdir("libs/" .. os.target() .. "/thirdparty/") - files { "src/thirdparty/pugixml/*.cpp" } - build_base_cpp_configuration( "pugixml" ) - - project "zlib-static" - kind "StaticLib" - language "C" - targetdir("libs/" .. os.target() .. "/thirdparty/") - files { "src/thirdparty/zlib/*.c" } - build_base_configuration( "zlib" ) - - project "libzip-static" - kind "StaticLib" - language "C" - targetdir("libs/" .. os.target() .. "/thirdparty/") - files { "src/thirdparty/libzip/*.c" } - incdirs { "src/thirdparty/zlib" } - build_base_configuration( "libzip" ) - - project "freetype-static" - kind "StaticLib" - language "C" - targetdir("libs/" .. os.target() .. "/thirdparty/") - defines { "FT2_BUILD_LIBRARY" } - files { "src/thirdparty/freetype2/src/**.c" } - incdirs { "src/thirdparty/freetype2/include" } - build_base_configuration( "freetype" ) - - project "chipmunk-static" - kind "StaticLib" - targetdir("libs/" .. os.target() .. "/thirdparty/") - files { "src/thirdparty/chipmunk/*.c", "src/thirdparty/chipmunk/constraints/*.c" } - incdirs { "include/eepp/thirdparty/chipmunk" } - build_base_configuration( "chipmunk" ) - filter "action:vs*" - language "C++" - buildoptions { "/TP" } - filter "action:not vs*" - language "C" - - project "jpeg-compressor-static" - kind "StaticLib" - language "C++" - targetdir("libs/" .. os.target() .. "/thirdparty/") - files { "src/thirdparty/jpeg-compressor/*.cpp" } - build_base_cpp_configuration( "jpeg-compressor" ) - - project "imageresampler-static" - kind "StaticLib" - language "C++" - targetdir("libs/" .. os.target() .. "/thirdparty/") - files { "src/thirdparty/imageresampler/*.cpp" } - build_base_cpp_configuration( "imageresampler" ) - - project "mojoal-static" - kind "StaticLib" - language "C" - targetdir("libs/" .. os.target() .. "/thirdparty/") - incdirs { "include/eepp/thirdparty/mojoAL" } - defines( "AL_LIBTYPE_STATIC" ) - files { "src/thirdparty/mojoAL/*.c" } - build_base_cpp_configuration( "mojoal" ) - filter "options:windows-vc-build" - incdirs { "src/thirdparty/" .. remote_sdl2_version .. "/include" } - - project "efsw-static" - kind "StaticLib" - language "C++" - targetdir("libs/" .. os.target() .. "/thirdparty/") - incdirs { "src/thirdparty/efsw/include", "src/thirdparty/efsw/src" } - files { "src/thirdparty/efsw/src/efsw/*.cpp" } - build_base_cpp_configuration( "efsw" ) - filter "system:windows" - files { "src/thirdparty/efsw/src/efsw/platform/win/*.cpp" } - excludes { - "src/thirdparty/efsw/src/efsw/WatcherKqueue.cpp", - "src/thirdparty/efsw/src/efsw/WatcherFSEvents.cpp", - "src/thirdparty/efsw/src/efsw/WatcherInotify.cpp", - "src/thirdparty/efsw/src/efsw/FileWatcherKqueue.cpp", - "src/thirdparty/efsw/src/efsw/FileWatcherInotify.cpp", - "src/thirdparty/efsw/src/efsw/FileWatcherFSEvents.cpp" - } - filter "system:linux" - excludes { - "src/thirdparty/efsw/src/efsw/WatcherKqueue.cpp", - "src/thirdparty/efsw/src/efsw/WatcherFSEvents.cpp", - "src/thirdparty/efsw/src/efsw/WatcherWin32.cpp", - "src/thirdparty/efsw/src/efsw/FileWatcherKqueue.cpp", - "src/thirdparty/efsw/src/efsw/FileWatcherWin32.cpp", - "src/thirdparty/efsw/src/efsw/FileWatcherFSEvents.cpp" - } - filter "system:macosx or system:ios" - excludes { - "src/thirdparty/efsw/src/efsw/WatcherInotify.cpp", - "src/thirdparty/efsw/src/efsw/WatcherWin32.cpp", - "src/thirdparty/efsw/src/efsw/FileWatcherInotify.cpp", - "src/thirdparty/efsw/src/efsw/FileWatcherWin32.cpp" - } - filter "system:bsd" - excludes { - "src/thirdparty/efsw/src/efsw/WatcherInotify.cpp", - "src/thirdparty/efsw/src/efsw/WatcherWin32.cpp", - "src/thirdparty/efsw/src/efsw/WatcherFSEvents.cpp", - "src/thirdparty/efsw/src/efsw/FileWatcherInotify.cpp", - "src/thirdparty/efsw/src/efsw/FileWatcherWin32.cpp", - "src/thirdparty/efsw/src/efsw/FileWatcherFSEvents.cpp" - } - filter "system:not windows" - files { "src/thirdparty/efsw/src/efsw/platform/posix/*.cpp" } - - -- Library - project "eepp-main" - kind "StaticLib" - language "C++" - targetdir("libs/" .. os.target() .. "/") - files { "src/eepp/main/eepp_main.cpp" } - - project "eepp-static" - kind "StaticLib" - language "C++" - targetdir("libs/" .. os.target() .. "/") - build_eepp( "eepp-static" ) - - project "eepp-shared" - kind "SharedLib" - language "C++" - targetdir("libs/" .. os.target() .. "/") - build_eepp( "eepp" ) - - -- Examples - project "eepp-external-shader" - set_kind() - language "C++" - files { "src/examples/external_shader/*.cpp" } - build_link_configuration( "eepp-external-shader", true ) - - project "eepp-empty-window" - set_kind() - language "C++" - files { "src/examples/empty_window/*.cpp" } - build_link_configuration( "eepp-empty-window", true ) - - project "eepp-sound" - kind "ConsoleApp" - language "C++" - files { "src/examples/sound/*.cpp" } - build_link_configuration( "eepp-sound", true ) - - project "eepp-sprites" - set_kind() - language "C++" - files { "src/examples/sprites/*.cpp" } - build_link_configuration( "eepp-sprites", true ) - - project "eepp-fonts" - set_kind() - language "C++" - files { "src/examples/fonts/*.cpp" } - build_link_configuration( "eepp-fonts", true ) - - project "eepp-vbo-fbo-batch" - set_kind() - language "C++" - files { "src/examples/vbo_fbo_batch/*.cpp" } - build_link_configuration( "eepp-vbo-fbo-batch", true ) - - project "eepp-physics" - set_kind() - language "C++" - files { "src/examples/physics/*.cpp" } - build_link_configuration( "eepp-physics", true ) - - project "eepp-http-request" - kind "ConsoleApp" - language "C++" - files { "src/examples/http_request/*.cpp" } - incdirs { "src/thirdparty" } - build_link_configuration( "eepp-http-request", true ) - - project "eepp-ui-hello-world" - set_kind() - language "C++" - files { "src/examples/ui_hello_world/*.cpp" } - incdirs { "src/thirdparty" } - build_link_configuration( "eepp-ui-hello-world", true ) - - -- Tools - project "eepp-textureatlaseditor" - set_kind() - language "C++" - files { "src/tools/textureatlaseditor/*.cpp" } - build_link_configuration( "eepp-TextureAtlasEditor", true ) - - project "eepp-mapeditor" - set_kind() - language "C++" - files { "src/tools/mapeditor/*.cpp" } - build_link_configuration( "eepp-MapEditor", true ) - - project "eepp-uieditor" - set_kind() - language "C++" - incdirs { "src/thirdparty/efsw/include", "src/thirdparty" } - links { "efsw-static", "pugixml-static" } - files { "src/tools/uieditor/*.cpp" } - build_link_configuration( "eepp-UIEditor", true ) - filter "system:macosx" - links { "CoreFoundation.framework", "CoreServices.framework" } - filter { "system:not windows", "system:not haiku" } - links { "pthread" } - - project "ecode" - set_kind() - language "C++" - files { "src/tools/codeeditor/*.cpp" } - incdirs { "src/thirdparty" } - build_link_configuration( "ecode", true ) - - project "eepp-texturepacker" - kind "ConsoleApp" - language "C++" - incdirs { "src/thirdparty" } - files { "src/tools/texturepacker/*.cpp" } - build_link_configuration( "eepp-TexturePacker", true ) - - -- Tests - project "eepp-test" - set_kind() - language "C++" - files { "src/tests/test_all/*.cpp" } - build_link_configuration( "eepp-test", true ) - - project "eepp-ui-perf-test" - set_kind() - language "C++" - files { "src/tests/ui_perf_test/*.cpp" } - includedirs { "src/thirdparty" } - build_link_configuration( "eepp-ui-perf-test", true ) - -if os.isfile("external_projects.lua") then - dofile("external_projects.lua") -end +newoption { trigger = "with-openssl", description = "Enables OpenSSL support ( and disables mbedtls backend )." } +newoption { trigger = "with-dynamic-freetype", description = "Dynamic link against freetype." } +newoption { trigger = "with-static-eepp", description = "Force to build the demos and tests with eepp compiled statically" } +newoption { trigger = "with-static-backend", description = "It will try to compile the library with a static backend (only for gcc and mingw).\n\t\t\t\tThe backend should be placed in libs/your_platform/libYourBackend.a" } +newoption { trigger = "with-gles2", description = "Compile with GLES2 support" } +newoption { trigger = "with-gles1", description = "Compile with GLES1 support" } +newoption { trigger = "with-mojoal", description = "Compile with mojoAL as OpenAL implementation instead of using openal-soft (requires SDL2 backend)" } +newoption { trigger = "use-frameworks", description = "In macOS it will try to link the external libraries from its frameworks. For example, instead of linking against SDL2 it will link against SDL2.framework." } +newoption { trigger = "windows-vc-build", description = "This is used to build the framework in Visual Studio downloading its external dependencies and making them available to the VS project without having to install them manually." } +newoption { + trigger = "with-backend", + description = "Select the backend to use for window and input handling.\n\t\t\tIf no backend is selected or if the selected is not installed the script will search for a backend present in the system, and will use it.", + allowed = { + { "SDL2", "SDL2" }, + } +} + +function print_table( table_ref ) + for _, value in pairs( table_ref ) do + print(value) + end +end + +function table_length(T) + local count = 0 + for _ in pairs(T) do count = count + 1 end + return count +end + +function multiple_insert( parent_table, insert_table ) + for _, value in pairs( insert_table ) do + table.insert( parent_table, value ) + end +end + +function os_findlib( name ) + if os.istarget("macosx") and ( is_xcode() or _OPTIONS["use-frameworks"] ) then + local path = "/Library/Frameworks/" .. name .. ".framework" + + if os.isdir( path ) then + return path + end + end + + return os.findlib( name ) +end + +function get_backend_link_name( name ) + if os.istarget("macosx") and ( is_xcode() or _OPTIONS["use-frameworks"] ) then + local fname = name .. ".framework" + + if os_findlib( name ) then -- Search for the framework + return fname + end + end + + return name +end + +function string.starts(String,Start) + if ( _ACTION ) then + return string.sub(String,1,string.len(Start))==Start + end + + return false +end + +function is_vs() + return ( string.starts(_ACTION,"vs") ) +end + +function is_xcode() + return ( string.starts(_ACTION,"xcode") ) +end + +function set_kind() + if os.istarget("macosx") then + kind("ConsoleApp") + else + kind("WindowedApp") + end +end + +link_list = { } +os_links = { } +backends = { } +static_backends = { } +backend_selected = false +remote_sdl2_version = "SDL2-2.0.12" +remote_sdl2_devel_src_url = "https://libsdl.org/release/SDL2-2.0.12.zip" +remote_sdl2_devel_vc_url = "https://www.libsdl.org/release/SDL2-devel-2.0.12-VC.zip" + +function incdirs( dirs ) + if is_xcode() then + sysincludedirs { dirs } + end + includedirs { dirs } +end + +function download_and_extract_sdl(sdl_url) + print("Downloading: " .. sdl_url) + local dest_dir = "src/thirdparty/" + local local_file = dest_dir .. remote_sdl2_version .. ".zip" + local result_str, response_code = http.download(sdl_url, local_file) + if response_code == 200 then + print("Downloaded successfully to: " .. local_file) + zip.extract(local_file, dest_dir) + print("Extracted " .. local_file .. " into " .. dest_dir) + else + print("Failed to download: " .. sdl_url) + exit(1) + end +end + +function download_and_extract_dependencies() + if not os.isdir("src/thirdparty/" .. remote_sdl2_version) then + if _OPTIONS["windows-vc-build"] then + download_and_extract_sdl(remote_sdl2_devel_vc_url) + elseif os.istarget("ios") then + download_and_extract_sdl(remote_sdl2_devel_src_url) + end + end +end + +function build_base_configuration( package_name ) + incdirs { "src/thirdparty/zlib" } + + set_ios_config() + set_xcode_config() + + filter "not system:windows" + buildoptions{ "-fPIC" } + + filter "configurations:debug*" + targetname ( package_name .. "-debug" ) + + filter "configurations:release*" + targetname ( package_name ) + + filter "action:not vs*" + cdialect "gnu99" + buildoptions { "-Wall" } + + filter "action:vs*" + incdirs { "src/thirdparty/libzip/vs" } +end + +function build_base_cpp_configuration( package_name ) + if not os.istarget("windows") then + buildoptions{ "-fPIC" } + end + + set_ios_config() + set_xcode_config() + + filter "action:not vs*" + buildoptions { "-Wall" } + + filter "configurations:debug*" + defines { "DEBUG" } + symbols "On" + targetname ( package_name .. "-debug" ) + + filter "configurations:release*" + optimize "Speed" + targetname ( package_name ) +end + +function build_link_configuration( package_name, use_ee_icon ) + incdirs { "include" } + local extension = ""; + + if package_name == "eepp" then + defines { "EE_EXPORTS" } + elseif package_name == "eepp-static" then + defines { "EE_STATIC" } + end + + if package_name ~= "eepp" and package_name ~= "eepp-static" then + if not _OPTIONS["with-static-eepp"] then + links { "eepp-shared" } + else + links { "eepp-static" } + defines { "EE_STATIC" } + add_static_links() + links { link_list } + end + end + + set_ios_config() + set_xcode_config() + + filter { "system:windows", "action:not vs*" } + if ( true == use_ee_icon ) then + linkoptions { "../../bin/assets/icon/ee.res" } + end + + filter { "system:windows", "action:vs*" } + files { "bin/assets/icon/ee.rc", "bin/assets/icon/ee.ico" } + vpaths { ['Resources/*'] = { "ee.rc", "ee.ico" } } + + filter "action:not vs*" + cppdialect "C++14" + buildoptions { "-Wall" } + + filter { "configurations:debug*", "action:not vs*" } + buildoptions{ "-Wno-long-long" } + + filter { "configurations:release*", "action:not vs*" } + buildoptions { "-fno-strict-aliasing -ffast-math" } + + filter { "configurations:release*", "action:not vs*", "system:not macosx" } + buildoptions { "-s" } + + filter "configurations:debug*" + defines { "DEBUG", "EE_DEBUG", "EE_MEMORY_MANAGER" } + targetname ( package_name .. "-debug" .. extension ) + + filter "configurations:release*" + defines { "NDEBUG" } + targetname ( package_name .. extension ) + + filter { "system:windows", "action:not vs*" } + linkoptions { "-static-libgcc", "-static-libstdc++" } + + filter { "system:windows", "action:vs*" } + if table.contains( backends, "SDL2" ) then + links { "SDL2", "SDL2main" } + end + + filter { "options:windows-vc-build", "system:windows", "platforms:x86" } + syslibdirs { "src/thirdparty/" .. remote_sdl2_version .."/lib/x86" } + + filter { "options:windows-vc-build", "system:windows", "platforms:x86_64" } + syslibdirs { "src/thirdparty/" .. remote_sdl2_version .."/lib/x64" } + + filter "system:emscripten" + targetname ( package_name .. extension ) + linkoptions{ "-O2 -s TOTAL_MEMORY=67108864 -s ASM_JS=1 -s VERBOSE=1 -s DISABLE_EXCEPTION_CATCHING=0 -s USE_SDL=2 -s ERROR_ON_UNDEFINED_SYMBOLS=0 -s ERROR_ON_MISSING_LIBRARIES=0 -s FULL_ES3=1 -s \"BINARYEN_TRAP_MODE='clamp'\"" } + buildoptions { "-fno-strict-aliasing -O2 -s USE_SDL=2 -s PRECISE_F32=1 -s ENVIRONMENT=web" } + + if _OPTIONS["with-gles1"] and ( not _OPTIONS["with-gles2"] or _OPTIONS["force-gles1"] ) then + linkoptions{ "-s LEGACY_GL_EMULATION=1" } + end + + if _OPTIONS["with-gles2"] and not _OPTIONS["force-gles1"] then + linkoptions{ "-s FULL_ES2=1" } + end +end + +function generate_os_links() + if os.istarget("linux") then + multiple_insert( os_links, { "rt", "pthread", "X11", "GL", "Xcursor" } ) + + if _OPTIONS["with-static-eepp"] then + table.insert( os_links, "dl" ) + end + elseif os.istarget("windows") then + multiple_insert( os_links, { "opengl32", "glu32", "gdi32", "ws2_32", "winmm", "ole32" } ) + elseif os.istarget("mingw32") then + multiple_insert( os_links, { "opengl32", "glu32", "gdi32", "ws2_32", "winmm", "ole32" } ) + elseif os.istarget("macosx") then + multiple_insert( os_links, { "OpenGL.framework", "CoreFoundation.framework" } ) + elseif os.istarget("bsd") then + multiple_insert( os_links, { "rt", "pthread", "X11", "GL", "Xcursor" } ) + elseif os.istarget("haiku") then + multiple_insert( os_links, { "GL", "network" } ) + elseif os.istarget("ios") then + multiple_insert( os_links, { "OpenGLES.framework", "AudioToolbox.framework", "CoreAudio.framework", "Foundation.framework", "CoreFoundation.framework", "UIKit.framework", "QuartzCore.framework", "CoreGraphics.framework", "CoreMotion.framework", "AVFoundation.framework", "GameController.framework" } ) + elseif os.istarget("android") then + multiple_insert( os_links, { "GLESv1_CM", "GLESv2", "log" } ) + end + + if not _OPTIONS["with-mojoal"] then + if os.istarget("linux") or os.istarget("bsd") or os.istarget("haiku") or os.istarget("emscripten") then + multiple_insert( os_links, { "openal" } ) + elseif os.istarget("windows") or os.istarget("mingw32") then + multiple_insert( os_links, { "OpenAL32" } ) + elseif os.istarget("macosx") or os.istarget("ios") then + multiple_insert( os_links, { "OpenAL.framework" } ) + end + end +end + +function parse_args() + if _OPTIONS["with-gles2"] then + defines { "EE_GLES2", "SOIL_GLES2" } + end + + if _OPTIONS["with-gles1"] then + defines { "EE_GLES1", "SOIL_GLES1" } + end +end + +function add_static_links() + -- The linking order DOES matter + -- Expose the symbols that need one static library AFTER adding that static lib + + -- Add static backends + if next(static_backends) ~= nil then + for _, value in pairs( static_backends ) do + linkoptions { value } + end + end + + if not _OPTIONS["with-dynamic-freetype"] then + links { "freetype-static" } + end + + links { "SOIL2-static", + "chipmunk-static", + "libzip-static", + "jpeg-compressor-static", + "zlib-static", + "imageresampler-static", + "pugixml-static", + "vorbis-static" + } + + if _OPTIONS["with-mojoal"] then + links { "mojoal-static"} + end + + if not _OPTIONS["with-openssl"] then + links { "mbedtls-static" } + end + + if not os.istarget("haiku") and not os.istarget("ios") and not os.istarget("android") and not os.istarget("emscripten") then + links{ "glew-static" } + end +end + +function can_add_static_backend( name ) + if _OPTIONS["with-static-backend"] then + return true + end + return false +end + +function insert_static_backend( name ) + table.insert( static_backends, "../../libs/" .. os.target() .. "/lib" .. name .. ".a" ) +end + +function add_sdl2() + print("Using SDL2 backend"); + if not can_add_static_backend("SDL2") then + table.insert( link_list, get_backend_link_name( "SDL2" ) ) + else + print("Using static backend") + insert_static_backend( "SDL2" ) + end + + table.insert( backends, "SDL2" ) +end + +function set_xcode_config() + if is_xcode() or _OPTIONS["use-frameworks"] then + linkoptions { "-F /Library/Frameworks" } + incdirs { "/Library/Frameworks/SDL2.framework/Headers" } + defines { "EE_SDL2_FROM_ROOTPATH" } + end +end + +function set_ios_config() + if os.istarget("ios") then + local toolchainpath = os.getenv("TOOLCHAINPATH") + local iosversion = os.getenv("IOSVERSION") + local sysroot_path = os.getenv("SYSROOTPATH") + + if nil == os.getenv("TOOLCHAINPATH") then + toolchainpath = os.outputof("xcrun -find -sdk iphonesimulator clang") + end + + if nil == os.getenv("IOSVERSION") then + iosversion = os.outputof("xcrun --sdk iphonesimulator --show-sdk-version") + end + + if nil == os.getenv("SYSROOTPATH") then + local platform_path = os.outputof("xcrun --sdk iphonesimulator --show-sdk-platform-path") + sysroot_path = platform_path .. "/Developer/SDKs/iPhoneSimulator" .. iosversion .. ".sdk/" + end + + local framework_path = sysroot_path .. "/System/Library/Frameworks" + local framework_libs_path = framework_path .. "/usr/lib" + local sysroot_ver = " -miphoneos-version-min=9.0 -isysroot " .. sysroot_path + + buildoptions { sysroot_ver .. " -I" .. sysroot_path .. "/usr/include" } + linkoptions { sysroot_ver } + libdirs { framework_libs_path } + linkoptions { " -F" .. framework_path .. " -L" .. framework_libs_path .. " -isysroot " .. sysroot_path } + includedirs { "src/thirdparty/" .. remote_sdl2_version .. "/include" } + end +end + +function backend_is( name, libname ) + if not _OPTIONS["with-backend"] then + _OPTIONS["with-backend"] = "SDL2" + end + + if next(backends) == nil then + backends = string.explode(_OPTIONS["with-backend"],",") + end + + local backend_sel = table.contains( backends, name ) + + local ret_val = os_findlib( libname ) and backend_sel + + if os.istarget("mingw32") or os.istarget("emscripten") then + ret_val = backend_sel + end + + if ret_val then + backend_selected = true + end + + return ret_val +end + +function select_backend() + if backend_is("SDL2", "SDL2") then + print("Selected SDL2") + add_sdl2() + end + + -- If the selected backend is not present, try to find one present + if not backend_selected then + if os_findlib("SDL2", "SDL2") then + add_sdl2() + else + print("ERROR: Couldnt find any backend. Forced SDL2.") + add_sdl2() + end + end +end + +function check_ssl_support() + if _OPTIONS["with-openssl"] then + if os.istarget("windows") then + table.insert( link_list, get_backend_link_name( "libssl" ) ) + table.insert( link_list, get_backend_link_name( "libcrypto" ) ) + else + table.insert( link_list, get_backend_link_name( "ssl" ) ) + table.insert( link_list, get_backend_link_name( "crypto" ) ) + end + + files { "src/eepp/network/ssl/backend/openssl/*.cpp" } + + defines { "EE_OPENSSL" } + else + files { "src/eepp/network/ssl/backend/mbedtls/*.cpp" } + + defines { "EE_MBEDTLS" } + end + + defines { "EE_SSL_SUPPORT" } +end + +function build_eepp( build_name ) + files { "src/eepp/core/*.cpp", + "src/eepp/math/*.cpp", + "src/eepp/system/*.cpp", + "src/eepp/audio/*.cpp", + "src/eepp/graphics/*.cpp", + "src/eepp/graphics/renderer/*.cpp", + "src/eepp/window/*.cpp", + "src/eepp/network/*.cpp", + "src/eepp/network/ssl/*.cpp", + "src/eepp/network/http/*.cpp", + "src/eepp/scene/*.cpp", + "src/eepp/scene/actions/*.cpp", + "src/eepp/ui/*.cpp", + "src/eepp/ui/abstract/*.cpp", + "src/eepp/ui/css/*.cpp", + "src/eepp/ui/doc/*.cpp", + "src/eepp/ui/tools/*.cpp", + "src/eepp/physics/*.cpp", + "src/eepp/physics/constraints/*.cpp", + "src/eepp/maps/*.cpp", + "src/eepp/maps/mapeditor/*.cpp" + } + + incdirs { "include", + "src", + "src/thirdparty", + "include/eepp/thirdparty", + "src/thirdparty/freetype2/include", + "src/thirdparty/zlib", + "src/thirdparty/libogg/include", + "src/thirdparty/libvorbis/include", + "src/thirdparty/mbedtls/include" + } + + add_static_links() + check_ssl_support() + + if table.contains( backends, "SDL2" ) then + files { "src/eepp/window/backend/SDL2/*.cpp" } + defines { "EE_BACKEND_SDL_ACTIVE", "EE_SDL_VERSION_2" } + end + + multiple_insert( link_list, os_links ) + + links { link_list } + + build_link_configuration( build_name ) + + filter "options:use-frameworks" + defines { "EE_USE_FRAMEWORKS" } + + filter { "system:macosx", "action:xcode* or options:use-frameworks" } + libdirs { "/System/Library/Frameworks", "/Library/Frameworks" } + + filter "system:windows" + files { "src/eepp/system/platform/win/*.cpp" } + files { "src/eepp/network/platform/win/*.cpp" } + + filter "system:not windows" + files { "src/eepp/system/platform/posix/*.cpp" } + files { "src/eepp/network/platform/unix/*.cpp" } + + filter "options:with-mojoal" + defines( "AL_LIBTYPE_STATIC" ) + incdirs { "src/thirdparty/mojoAL" } + + filter "options:windows-vc-build" + incdirs { "src/thirdparty/" .. remote_sdl2_version .. "/include" } + + filter "action:vs*" + incdirs { "src/thirdparty/libzip/vs" } + + filter "action:not vs*" + cppdialect "C++14" + + filter "options:with-dynamic-freetype" + if not os.istarget("ios") and os_findlib("freetype") then + table.insert( link_list, get_backend_link_name( "freetype" ) ) + end +end + +workspace "eepp" + targetdir("./bin/") + if os.istarget("ios") then + configurations { "debug-x64", "debug-arm64", "release-x64", "release-arm64" } + platforms { "x86_64", "arm64" } + else + configurations { "debug", "release" } + platforms { "x86_64", "x86" } + end + rtti "On" + download_and_extract_dependencies() + select_backend() + generate_os_links() + parse_args() + location("./make/" .. os.target() .. "/") + objdir("obj/" .. os.target() .. "/") + + filter "platforms:x86" + architecture "x86" + + filter "platforms:arm64 or configurations:debug-arm64 or configurations:release-arm64" + architecture "arm64" + + filter "platforms:x86_64 or configurations:debug-x64 or configurations:release-x64" + architecture "x86_64" + + filter "system:macosx" + defines { "GL_SILENCE_DEPRECATION" } + + filter "configurations:debug*" + defines { "DEBUG" } + symbols "On" + filter "configurations:release*" + optimize "Speed" + + filter { "system:windows", "action:vs*" } + flags { "MultiProcessorCompile" } + + project "SOIL2-static" + kind "StaticLib" + language "C" + targetdir("libs/" .. os.target() .. "/thirdparty/") + files { "src/thirdparty/SOIL2/src/SOIL2/*.c" } + incdirs { "src/thirdparty/SOIL2" } + build_base_configuration( "SOIL2" ) + + project "glew-static" + kind "StaticLib" + language "C" + defines { "GLEW_NO_GLU", "GLEW_STATIC" } + targetdir("libs/" .. os.target() .. "/thirdparty/") + files { "src/thirdparty/glew/*.c" } + incdirs { "include/thirdparty/glew" } + build_base_configuration( "glew" ) + + project "mbedtls-static" + kind "StaticLib" + language "C" + targetdir("libs/" .. os.target() .. "/thirdparty/") + incdirs { "src/thirdparty/mbedtls/include/" } + files { "src/thirdparty/mbedtls/library/*.c" } + build_base_cpp_configuration( "mbedtls" ) + + project "vorbis-static" + kind "StaticLib" + language "C" + targetdir("libs/" .. os.target() .. "/thirdparty/") + incdirs { "src/thirdparty/libvorbis/lib/", "src/thirdparty/libogg/include", "src/thirdparty/libvorbis/include" } + files { "src/thirdparty/libogg/**.c", "src/thirdparty/libvorbis/**.c" } + build_base_cpp_configuration( "vorbis" ) + + project "pugixml-static" + kind "StaticLib" + language "C++" + targetdir("libs/" .. os.target() .. "/thirdparty/") + files { "src/thirdparty/pugixml/*.cpp" } + build_base_cpp_configuration( "pugixml" ) + + project "zlib-static" + kind "StaticLib" + language "C" + targetdir("libs/" .. os.target() .. "/thirdparty/") + files { "src/thirdparty/zlib/*.c" } + build_base_configuration( "zlib" ) + + project "libzip-static" + kind "StaticLib" + language "C" + targetdir("libs/" .. os.target() .. "/thirdparty/") + files { "src/thirdparty/libzip/*.c" } + incdirs { "src/thirdparty/zlib" } + build_base_configuration( "libzip" ) + + project "freetype-static" + kind "StaticLib" + language "C" + targetdir("libs/" .. os.target() .. "/thirdparty/") + defines { "FT2_BUILD_LIBRARY" } + files { "src/thirdparty/freetype2/src/**.c" } + incdirs { "src/thirdparty/freetype2/include" } + build_base_configuration( "freetype" ) + + project "chipmunk-static" + kind "StaticLib" + targetdir("libs/" .. os.target() .. "/thirdparty/") + files { "src/thirdparty/chipmunk/*.c", "src/thirdparty/chipmunk/constraints/*.c" } + incdirs { "include/eepp/thirdparty/chipmunk" } + build_base_configuration( "chipmunk" ) + filter "action:vs*" + language "C++" + buildoptions { "/TP" } + filter "action:not vs*" + language "C" + + project "jpeg-compressor-static" + kind "StaticLib" + language "C++" + targetdir("libs/" .. os.target() .. "/thirdparty/") + files { "src/thirdparty/jpeg-compressor/*.cpp" } + build_base_cpp_configuration( "jpeg-compressor" ) + + project "imageresampler-static" + kind "StaticLib" + language "C++" + targetdir("libs/" .. os.target() .. "/thirdparty/") + files { "src/thirdparty/imageresampler/*.cpp" } + build_base_cpp_configuration( "imageresampler" ) + + project "mojoal-static" + kind "StaticLib" + language "C" + targetdir("libs/" .. os.target() .. "/thirdparty/") + incdirs { "include/eepp/thirdparty/mojoAL" } + defines( "AL_LIBTYPE_STATIC" ) + files { "src/thirdparty/mojoAL/*.c" } + build_base_cpp_configuration( "mojoal" ) + filter "options:windows-vc-build" + incdirs { "src/thirdparty/" .. remote_sdl2_version .. "/include" } + + project "efsw-static" + kind "StaticLib" + language "C++" + targetdir("libs/" .. os.target() .. "/thirdparty/") + incdirs { "src/thirdparty/efsw/include", "src/thirdparty/efsw/src" } + files { "src/thirdparty/efsw/src/efsw/*.cpp" } + build_base_cpp_configuration( "efsw" ) + filter "system:windows" + files { "src/thirdparty/efsw/src/efsw/platform/win/*.cpp" } + excludes { + "src/thirdparty/efsw/src/efsw/WatcherKqueue.cpp", + "src/thirdparty/efsw/src/efsw/WatcherFSEvents.cpp", + "src/thirdparty/efsw/src/efsw/WatcherInotify.cpp", + "src/thirdparty/efsw/src/efsw/FileWatcherKqueue.cpp", + "src/thirdparty/efsw/src/efsw/FileWatcherInotify.cpp", + "src/thirdparty/efsw/src/efsw/FileWatcherFSEvents.cpp" + } + filter "system:linux" + excludes { + "src/thirdparty/efsw/src/efsw/WatcherKqueue.cpp", + "src/thirdparty/efsw/src/efsw/WatcherFSEvents.cpp", + "src/thirdparty/efsw/src/efsw/WatcherWin32.cpp", + "src/thirdparty/efsw/src/efsw/FileWatcherKqueue.cpp", + "src/thirdparty/efsw/src/efsw/FileWatcherWin32.cpp", + "src/thirdparty/efsw/src/efsw/FileWatcherFSEvents.cpp" + } + filter "system:macosx or system:ios" + excludes { + "src/thirdparty/efsw/src/efsw/WatcherInotify.cpp", + "src/thirdparty/efsw/src/efsw/WatcherWin32.cpp", + "src/thirdparty/efsw/src/efsw/FileWatcherInotify.cpp", + "src/thirdparty/efsw/src/efsw/FileWatcherWin32.cpp" + } + filter "system:bsd" + excludes { + "src/thirdparty/efsw/src/efsw/WatcherInotify.cpp", + "src/thirdparty/efsw/src/efsw/WatcherWin32.cpp", + "src/thirdparty/efsw/src/efsw/WatcherFSEvents.cpp", + "src/thirdparty/efsw/src/efsw/FileWatcherInotify.cpp", + "src/thirdparty/efsw/src/efsw/FileWatcherWin32.cpp", + "src/thirdparty/efsw/src/efsw/FileWatcherFSEvents.cpp" + } + filter "system:not windows" + files { "src/thirdparty/efsw/src/efsw/platform/posix/*.cpp" } + + -- Library + project "eepp-main" + kind "StaticLib" + language "C++" + targetdir("libs/" .. os.target() .. "/") + files { "src/eepp/main/eepp_main.cpp" } + + project "eepp-static" + kind "StaticLib" + language "C++" + targetdir("libs/" .. os.target() .. "/") + build_eepp( "eepp-static" ) + + project "eepp-shared" + kind "SharedLib" + language "C++" + targetdir("libs/" .. os.target() .. "/") + build_eepp( "eepp" ) + + -- Examples + project "eepp-external-shader" + set_kind() + language "C++" + files { "src/examples/external_shader/*.cpp" } + build_link_configuration( "eepp-external-shader", true ) + + project "eepp-empty-window" + set_kind() + language "C++" + files { "src/examples/empty_window/*.cpp" } + build_link_configuration( "eepp-empty-window", true ) + + project "eepp-sound" + kind "ConsoleApp" + language "C++" + files { "src/examples/sound/*.cpp" } + build_link_configuration( "eepp-sound", true ) + + project "eepp-sprites" + set_kind() + language "C++" + files { "src/examples/sprites/*.cpp" } + build_link_configuration( "eepp-sprites", true ) + + project "eepp-fonts" + set_kind() + language "C++" + files { "src/examples/fonts/*.cpp" } + build_link_configuration( "eepp-fonts", true ) + + project "eepp-vbo-fbo-batch" + set_kind() + language "C++" + files { "src/examples/vbo_fbo_batch/*.cpp" } + build_link_configuration( "eepp-vbo-fbo-batch", true ) + + project "eepp-physics" + set_kind() + language "C++" + files { "src/examples/physics/*.cpp" } + build_link_configuration( "eepp-physics", true ) + + project "eepp-http-request" + kind "ConsoleApp" + language "C++" + files { "src/examples/http_request/*.cpp" } + incdirs { "src/thirdparty" } + build_link_configuration( "eepp-http-request", true ) + + project "eepp-ui-hello-world" + set_kind() + language "C++" + files { "src/examples/ui_hello_world/*.cpp" } + incdirs { "src/thirdparty" } + build_link_configuration( "eepp-ui-hello-world", true ) + + -- Tools + project "eepp-textureatlaseditor" + set_kind() + language "C++" + files { "src/tools/textureatlaseditor/*.cpp" } + build_link_configuration( "eepp-TextureAtlasEditor", true ) + + project "eepp-mapeditor" + set_kind() + language "C++" + files { "src/tools/mapeditor/*.cpp" } + build_link_configuration( "eepp-MapEditor", true ) + + project "eepp-uieditor" + set_kind() + language "C++" + incdirs { "src/thirdparty/efsw/include", "src/thirdparty" } + links { "efsw-static", "pugixml-static" } + files { "src/tools/uieditor/*.cpp" } + build_link_configuration( "eepp-UIEditor", true ) + filter "system:macosx" + links { "CoreFoundation.framework", "CoreServices.framework" } + filter { "system:not windows", "system:not haiku" } + links { "pthread" } + + project "ecode" + set_kind() + language "C++" + files { "src/tools/codeeditor/*.cpp" } + incdirs { "src/thirdparty" } + build_link_configuration( "ecode", true ) + + project "eepp-texturepacker" + kind "ConsoleApp" + language "C++" + incdirs { "src/thirdparty" } + files { "src/tools/texturepacker/*.cpp" } + build_link_configuration( "eepp-TexturePacker", true ) + + -- Tests + project "eepp-test" + set_kind() + language "C++" + files { "src/tests/test_all/*.cpp" } + build_link_configuration( "eepp-test", true ) + + project "eepp-ui-perf-test" + set_kind() + language "C++" + files { "src/tests/ui_perf_test/*.cpp" } + includedirs { "src/thirdparty" } + build_link_configuration( "eepp-ui-perf-test", true ) + +if os.isfile("external_projects.lua") then + dofile("external_projects.lua") +end diff --git a/projects/android-project/app/jni/eepp.mk b/projects/android-project/app/jni/eepp.mk index 793e6d5e0..c59cb45d1 100644 --- a/projects/android-project/app/jni/eepp.mk +++ b/projects/android-project/app/jni/eepp.mk @@ -82,7 +82,7 @@ CODE_SRCS := \ ui/*.cpp \ ui/css/*.cpp \ ui/doc/*.cpp \ - ui/actions/*.cpp \ + ui/model/*.cpp \ ui/tools/*.cpp \ maps/*.cpp \ maps/mapeditor/*.cpp diff --git a/projects/linux/ee.creator.user b/projects/linux/ee.creator.user index 057dfcf17..0381c8ad0 100644 --- a/projects/linux/ee.creator.user +++ b/projects/linux/ee.creator.user @@ -1,6 +1,6 @@ - + EnvironmentId diff --git a/projects/linux/ee.files b/projects/linux/ee.files index 9cad74458..eebb8abc6 100644 --- a/projects/linux/ee.files +++ b/projects/linux/ee.files @@ -294,8 +294,12 @@ ../../include/eepp/thirdparty/chipmunk/cpSpatialIndex.h ../../include/eepp/thirdparty/chipmunk/cpVect.h ../../include/eepp/thirdparty/PlusCallback/callback.hpp +../../include/eepp/ui/abstract/model.hpp ../../include/eepp/ui/base.hpp ../../include/eepp/ui.hpp +../../include/eepp/ui/abstract/modeleditingdelegate.hpp +../../include/eepp/ui/abstract/modelindex.hpp +../../include/eepp/ui/abstract/modelselection.hpp ../../include/eepp/ui/border.hpp ../../include/eepp/ui/css/animationdefinition.hpp ../../include/eepp/ui/css/drawableimageparser.hpp @@ -331,9 +335,13 @@ ../../include/eepp/ui/doc/undostack.hpp ../../include/eepp/ui/keyboardshortcut.hpp ../../include/eepp/ui/marginmove/scale.hpp +../../include/eepp/ui/abstract/model.hpp +../../include/eepp/ui/abstract/modelindex.hpp +../../include/eepp/ui/abstract/modelselection.hpp ../../include/eepp/ui/tools/textureatlaseditor.hpp ../../include/eepp/ui/tools/uicodeeditorsplitter.hpp ../../include/eepp/ui/tools/uicolorpicker.hpp +../../include/eepp/ui/uiabstractview.hpp ../../include/eepp/ui/uibackgrounddrawable.hpp ../../include/eepp/ui/uiborderdrawable.hpp ../../include/eepp/ui/uicheckbox.hpp @@ -754,6 +762,8 @@ ../../src/eepp/system/translator.cpp ../../src/eepp/system/virtualfilesystem.cpp ../../src/eepp/system/zip.cpp +../../src/eepp/ui/abstract/model.cpp +../../src/eepp/ui/abstract/modelselection.cpp ../../src/eepp/ui/border.cpp ../../src/eepp/ui/css/animationdefinition.cpp ../../src/eepp/ui/css/drawableimageparser.cpp @@ -784,6 +794,8 @@ ../../src/eepp/ui/doc/textdocument.cpp ../../src/eepp/ui/doc/undostack.cpp ../../src/eepp/ui/keyboardshortcut.cpp +../../src/eepp/ui/abstract/model.cpp +../../src/eepp/ui/abstract/modelselection.cpp ../../src/eepp/ui/tools/textureatlaseditor.cpp ../../src/eepp/ui/tools/textureatlasnew.cpp ../../src/eepp/ui/tools/textureatlasnew.hpp @@ -791,6 +803,7 @@ ../../src/eepp/ui/tools/textureatlastextureregioneditor.hpp ../../src/eepp/ui/tools/uicodeeditorsplitter.cpp ../../src/eepp/ui/tools/uicolorpicker.cpp +../../src/eepp/ui/uiabstractview.cpp ../../src/eepp/ui/uibackgrounddrawable.cpp ../../src/eepp/ui/uiborderdrawable.cpp ../../src/eepp/ui/uicheckbox.cpp diff --git a/src/eepp/ui/abstract/model.cpp b/src/eepp/ui/abstract/model.cpp new file mode 100644 index 000000000..279ed4042 --- /dev/null +++ b/src/eepp/ui/abstract/model.cpp @@ -0,0 +1,42 @@ +#include +#include + +namespace EE { namespace UI { namespace Abstract { + +void Model::didUpdate( unsigned flags ) { + if ( onUpdate ) + onUpdate(); + forEachView( [&]( UIAbstractView* view ) { view->didUpdateModel( flags ); } ); +} + +void Model::forEachView( std::function callback ) { + for ( auto view : mViews ) + callback( view ); +} + +void Model::unregisterView( UIAbstractView* view ) { + mViews.erase( view ); +} + +void Model::registerView( UIAbstractView* view ) { + mViews.insert( view ); +} + +ModelIndex Model::createIndex( int row, int column, const void* data ) const { + return ModelIndex( *this, row, column, const_cast( data ) ); +} + +ModelIndex Model::sibling( int row, int column, const ModelIndex& parent ) const { + if ( !parent.isValid() ) + return index( row, column, {} ); + int rowCount = this->rowCount( parent ); + if ( row < 0 || row > rowCount ) + return {}; + return index( row, column, parent ); +} + +bool Model::acceptsDrag( const ModelIndex&, const std::string& dataType ) { + return false; +} + +}}} // namespace EE::UI::Abstract diff --git a/src/eepp/ui/abstract/modelselection.cpp b/src/eepp/ui/abstract/modelselection.cpp new file mode 100644 index 000000000..d273a5fb7 --- /dev/null +++ b/src/eepp/ui/abstract/modelselection.cpp @@ -0,0 +1,62 @@ +#include +#include +#include + +namespace EE { namespace UI { namespace Abstract { + +void ModelSelection::removeMatching( std::function filter ) { + std::vector::iterator> toRemove; + for ( auto it = mIndexes.begin(); it != mIndexes.end(); it++ ) { + if ( filter( *it ) ) + toRemove.push_back( it ); + } + for ( auto& index : toRemove ) + mIndexes.erase( index ); +} + +void ModelSelection::set( const ModelIndex& index ) { + eeASSERT( index.isValid() ); + if ( mIndexes.size() == 1 && contains( index ) ) + return; + mIndexes.clear(); + mIndexes.push_back( index ); + mView->notifySelectionChange(); +} + +void ModelSelection::add( const ModelIndex& index ) { + eeASSERT( index.isValid() ); + auto contains = std::find( mIndexes.begin(), mIndexes.end(), index ); + if ( contains == mIndexes.end() ) + return; + mIndexes.push_back( index ); + mView->notifySelectionChange(); +} + +void ModelSelection::toggle( const ModelIndex& index ) { + eeASSERT( index.isValid() ); + auto contains = std::find( mIndexes.begin(), mIndexes.end(), index ); + if ( contains != mIndexes.end() ) + mIndexes.erase( contains ); + else + mIndexes.push_back( index ); + mView->notifySelectionChange(); +} + +bool ModelSelection::remove( const ModelIndex& index ) { + eeASSERT( index.isValid() ); + auto contains = std::find( mIndexes.begin(), mIndexes.end(), index ); + if ( contains == mIndexes.end() ) + return false; + mIndexes.erase( contains ); + mView->notifySelectionChange(); + return true; +} + +void ModelSelection::clear() { + if ( mIndexes.empty() ) + return; + mIndexes.clear(); + mView->notifySelectionChange(); +} + +}}} // namespace EE::UI::Abstract diff --git a/src/eepp/ui/uiabstractview.cpp b/src/eepp/ui/uiabstractview.cpp new file mode 100644 index 000000000..4a463d1c9 --- /dev/null +++ b/src/eepp/ui/uiabstractview.cpp @@ -0,0 +1,59 @@ +#include +#include +#include + +using namespace EE::Scene; +using namespace EE::Window; + +namespace EE { namespace UI { + +UIAbstractView::UIAbstractView() : m_selection( this ) {} + +UIAbstractView::~UIAbstractView() {} + +void UIAbstractView::set_model( std::shared_ptr model ) { + if ( model.get() == m_model.get() ) + return; + if ( m_model ) + m_model->unregisterView( this ); + m_model = model; + if ( m_model ) + m_model->registerView( this ); + didUpdateModel( Model::InvalidateAllIndexes ); +} + +void UIAbstractView::didUpdateModel( unsigned flags ) { + m_hovered_index = {}; + if ( !model() || ( flags & Model::InvalidateAllIndexes ) ) { + selection().clear(); + } else { + selection().removeMatching( [this]( auto& index ) { return !model()->isValid( index ); } ); + } +} + +void UIAbstractView::didUpdateSelection() { + if ( model() && on_selection && selection().first().isValid() ) + on_selection( selection().first() ); +} + +void UIAbstractView::did_scroll() {} + +void UIAbstractView::activate( const ModelIndex& index ) { + if ( on_activation ) + on_activation( index ); +} + +void UIAbstractView::activate_selected() { + if ( !on_activation ) + return; + + selection().forEachIndex( [this]( auto& index ) { on_activation( index ); } ); +} + +void UIAbstractView::notifySelectionChange() { + didUpdateSelection(); + if ( on_selection_change ) + on_selection_change(); +} + +}} // namespace EE::UI From 1be31595d3657ecc8a27155162171f4a847ef50a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Sun, 5 Jul 2020 04:23:15 -0300 Subject: [PATCH 2/7] More WIP. --- bin/assets/ui/breeze.css | 21 + include/eepp/ui/abstract/model.hpp | 122 +++- include/eepp/ui/abstract/modelselection.hpp | 6 +- .../eepp/ui/abstract/uiabstracttableview.hpp | 102 ++-- include/eepp/ui/abstract/uiabstractview.hpp | 74 ++- include/eepp/ui/uilinearlayout.hpp | 2 + include/eepp/ui/uipushbutton.hpp | 3 + include/eepp/ui/uitextview.hpp | 1 + src/eepp/ui/abstract/model.cpp | 14 +- src/eepp/ui/abstract/modelselection.cpp | 2 +- src/eepp/ui/abstract/uiabstracttableview.cpp | 578 +++++------------- src/eepp/ui/abstract/uiabstractview.cpp | 58 +- src/eepp/ui/uilinearlayout.cpp | 5 + src/eepp/ui/uipushbutton.cpp | 29 +- src/eepp/ui/uitextview.cpp | 12 +- src/eepp/ui/uiwindow.cpp | 2 +- src/tests/ui_perf_test/ui_perf_test.cpp | 50 +- 17 files changed, 492 insertions(+), 589 deletions(-) diff --git a/bin/assets/ui/breeze.css b/bin/assets/ui/breeze.css index c20684374..65952e62c 100644 --- a/bin/assets/ui/breeze.css +++ b/bin/assets/ui/breeze.css @@ -736,6 +736,27 @@ Splitter::separator:hover { background-color: var(--primary); } +table::header { + background-color: var(--back); +} + +table::header::column { + background-color: var(--back); + border-right-color: var(--tab-line); + border-right-width: 1dp; + border-bottom-color: var(--tab-line); + border-bottom-width: 1dp; + border-type: inside; + color: var(--font); + padding: 2dp 6dp 2dp 6dp; + transition: all 0.125s; + text-align: left; +} + +table::header::column:hover { + background-color: var(--tab-hover); +} + .appbackground { background-color: var(--back); } diff --git a/include/eepp/ui/abstract/model.hpp b/include/eepp/ui/abstract/model.hpp index 33a814d44..e6a06a650 100644 --- a/include/eepp/ui/abstract/model.hpp +++ b/include/eepp/ui/abstract/model.hpp @@ -1,20 +1,100 @@ #ifndef EE_UI_MODEL_MODEL_HPP #define EE_UI_MODEL_MODEL_HPP +#include +#include +#include #include #include #include #include -namespace EE { namespace UI { -class UIAbstractView; -}} // namespace EE::UI +using namespace EE::Graphics; +using namespace EE::Math; namespace EE { namespace UI { namespace Abstract { enum class SortOrder { None, Ascending, Descending }; -class Variant; +class UIAbstractView; + +class Variant { + public: + enum class Type { + Invalid, + DataPtr, + String, + Bool, + Float, + Int, + Int64, + Drawable, + Vector2f, + Rectf + }; + Variant() : mType( Type::Invalid ) {} + Variant( const std::string& string ) : mType( Type::String ) { + mValue.asString = eeNew( std::string, ( string ) ); + } + Variant( Drawable* drawable, bool ownDrawable = false ) : mType( Type::Drawable ) { + mValue.asDrawable = drawable; + mOwnsObject = ownDrawable; + } + Variant( const Vector2f& v ) : mType( Type::Vector2f ) { + mValue.asVector2f = eeNew( Vector2f, ( v ) ); + } + Variant( void* data ) : mType( Type::DataPtr ) { mValue.asDataPtr = data; } + Variant( const Rectf& r ) : mType( Type::Rectf ) { mValue.asRectf = eeNew( Rectf, ( r ) ); } + Variant( bool val ) : mType( Type::Bool ) { mValue.asBool = val; } + Variant( const Float& val ) : mType( Type::Float ) { mValue.asFloat = val; } + Variant( const int& val ) : mType( Type::Int ) { mValue.asInt = val; } + Variant( const Int64& val ) : mType( Type::Int64 ) { mValue.asInt64 = val; } + ~Variant() { reset(); } + const std::string& asString() const { return *mValue.asString; } + const Drawable* asDrawable() const { return mValue.asDrawable; } + const bool& asBool() const { return mValue.asBool; } + const Float& asFloat() const { return mValue.asFloat; } + const int& asInt() const { return mValue.asInt; } + const Int64& asInt64() const { return mValue.asInt64; } + const Vector2f& asVector2f() const { return *mValue.asVector2f; } + const Rectf& asRectf() const { return *mValue.asRectf; } + void reset() { + switch ( mType ) { + case Type::String: + eeSAFE_DELETE( mValue.asString ); + break; + case Type::Drawable: + if ( mOwnsObject ) + eeSAFE_DELETE( mValue.asDrawable ); + break; + case Type::Vector2f: + eeSAFE_DELETE( mValue.asVector2f ); + break; + case Type::Rectf: + eeSAFE_DELETE( mValue.asRectf ); + break; + default: + break; + } + mType = Type::Invalid; + } + bool isValid() { return mType != Type::Invalid; } + + private: + union { + void* asDataPtr; + Drawable* asDrawable; + std::string* asString; + bool asBool; + Float asFloat; + int asInt; + Int64 asInt64; + Vector2f* asVector2f; + Rectf* asRectf; + } mValue; + Type mType; + bool mOwnsObject{false}; +}; class EE_API Model { public: @@ -25,33 +105,36 @@ class EE_API Model { enum class Role { Display, - Sort, - Custom, - ForegroundColor, - BackgroundColor, Icon, - Font, - DragData, - TextAlignment, }; virtual ~Model(){}; virtual int rowCount( const ModelIndex& = ModelIndex() ) const = 0; + virtual int columnCount( const ModelIndex& = ModelIndex() ) const = 0; - virtual std::string columnName( int ) const { return {}; } + + virtual std::string columnName( const size_t& /*column*/ ) const { return {}; } + virtual Variant data( const ModelIndex&, Role = Role::Display ) const = 0; + virtual void update() = 0; + virtual ModelIndex parentIndex( const ModelIndex& ) const { return {}; } + virtual ModelIndex index( int row, int column = 0, const ModelIndex& = ModelIndex() ) const { return createIndex( row, column ); } + virtual ModelIndex sibling( int row, int column, const ModelIndex& parent ) const; + virtual void setData( const ModelIndex&, const Variant& ) {} - virtual int treeColumn() const { return 0; } + + virtual size_t treeColumn() const { return 0; } + virtual bool acceptsDrag( const ModelIndex&, const std::string& dataType ); - virtual bool isColumnSortable( int /*columnIndex*/ ) const { return true; } + virtual bool isColumnSortable( const size_t& /*columnIndex*/ ) const { return true; } virtual std::string dragDataType() const { return {}; } @@ -62,24 +145,29 @@ class EE_API Model { } virtual int keyColumn() const { return -1; } + virtual SortOrder sortOrder() const { return SortOrder::None; } - virtual void setKeyColumnAndSortOrder( int, SortOrder ) {} + + virtual void setKeyColumnAndSortOrder( const size_t& /*column*/, const SortOrder& /*order*/ ) {} void registerView( UIAbstractView* ); + void unregisterView( UIAbstractView* ); - std::function onUpdate; + void setOnUpdate( const std::function& onUpdate ); protected: Model(){}; void forEachView( std::function ); - void didUpdate( unsigned flags = UpdateFlag::InvalidateAllIndexes ); + + void onModelUpdate( unsigned flags = UpdateFlag::InvalidateAllIndexes ); ModelIndex createIndex( int row, int column, const void* data = nullptr ) const; private: std::unordered_set mViews; + std::function mOnUpdate; }; inline ModelIndex ModelIndex::parent() const { diff --git a/include/eepp/ui/abstract/modelselection.hpp b/include/eepp/ui/abstract/modelselection.hpp index 5b87d55f5..2153f64a7 100644 --- a/include/eepp/ui/abstract/modelselection.hpp +++ b/include/eepp/ui/abstract/modelselection.hpp @@ -6,12 +6,10 @@ #include #include -namespace EE { namespace UI { -class UIAbstractView; -}} // namespace EE::UI - namespace EE { namespace UI { namespace Abstract { +class UIAbstractView; + class EE_API ModelSelection { public: ModelSelection( UIAbstractView* view ) : mView( view ) {} diff --git a/include/eepp/ui/abstract/uiabstracttableview.hpp b/include/eepp/ui/abstract/uiabstracttableview.hpp index 076e4682c..9ce89face 100644 --- a/include/eepp/ui/abstract/uiabstracttableview.hpp +++ b/include/eepp/ui/abstract/uiabstracttableview.hpp @@ -1,102 +1,68 @@ #ifndef EE_UI_UIABSTRACTTABLEVIEW_HPP #define EE_UI_UIABSTRACTTABLEVIEW_HPP -#include #include +#include using namespace EE::Math; +namespace EE { namespace UI { +class UIPushButton; +class UILinearLayout; +}} // namespace EE::UI + namespace EE { namespace UI { namespace Abstract { -class UITableCellDrawDelegate { - public: - virtual ~UITableCellDrawDelegate() {} - - virtual void draw( const Rectf&, const Model&, const ModelIndex& ) = 0; -}; - class EE_API UIAbstractTableView : public UIAbstractView { public: - int item_height() const { return 16; } + static UIAbstractTableView* New(); - bool alternating_row_colors() const { return m_alternating_row_colors; } - void set_alternating_row_colors( bool b ) { m_alternating_row_colors = b; } + int rowHeight() const { return PixelDensity::dpToPx( 16 ); } - int header_height() const { return m_headers_visible ? 16 : 0; } + Float getHeaderHeight() const; - bool headers_visible() const { return m_headers_visible; } - void set_headers_visible( bool headers_visible ) { m_headers_visible = headers_visible; } + bool headersVisible() const { return mHeadersVisible; } - bool is_column_hidden( int ) const; - void set_column_hidden( int, bool ); + void setHeadersVisible( bool visible ) { mHeadersVisible = visible; } - void set_cell_painting_delegate( int column, UITableCellDrawDelegate* ); + bool isColumnHidden( const size_t& column ) const; - int horizontal_padding() const { return m_horizontal_padding; } + void setColumnHidden( const size_t& column, bool hidden ); - Vector2i adjusted_position( const Vector2i& ) const; + virtual void selectAll(); - virtual Rect content_rect( const ModelIndex& ) const ; - Rect content_rect( int row, int column ) const; - Rect row_rect( int item_index ) const; + const Float& getDragBorderDistance() const; - //void scroll_into_view( const ModelIndex&, Orientation ); - - virtual ModelIndex index_at_event_position( const Vector2i&, bool& is_toggle ) const; - virtual ModelIndex index_at_event_position( const Vector2i& ) const ; - - virtual void select_all() ; - - void move_selection( int steps ); + void setDragBorderDistance( const Float& dragBorderDistance ); protected: - virtual ~UIAbstractTableView() ; - UIAbstractTableView(); + friend class UITableHeaderColumn; - virtual void didUpdateModel( unsigned flags ) ; - virtual Uint32 onMouseUp( const Vector2i& position, const Uint32& flags ); - virtual void mousedown_event( MouseEvent& ) ; - virtual void mousemove_event( MouseEvent& ) ; - virtual void doubleclick_event( MouseEvent& ) ; - virtual void leave_event( Core::Event& ) ; + virtual ~UIAbstractTableView(); - virtual void toggle_index( const ModelIndex& ) {} - - void paint_headers( Painter& ); - Rect header_rect( int column ) const; - - void update_headers(); - void set_hovered_header_index( int ); + UIAbstractTableView( const std::string& tag ); struct ColumnData { - int width{0}; - bool has_initialized_width{false}; - bool visibility{true}; - std::shared_ptr visibility_action; - UITableCellDrawDelegate* cell_painting_delegate; + Float width{0}; + bool visible{true}; + UIPushButton* widget{nullptr}; }; - ColumnData& column_data( int column ) const; - mutable std::vector m_column_data; + ColumnData& columnData( const size_t& column ) const; + mutable std::vector mColumn; - Rect column_resize_grabbable_rect( int ) const; - int column_width( int ) const; - void update_content_size(); - virtual void update_column_sizes(); - virtual int item_count() const; + virtual size_t itemCount() const; - private: - bool m_headers_visible{true}; - bool m_in_column_resize{false}; - bool m_alternating_row_colors{true}; - int m_horizontal_padding{5}; - Vector2i m_column_resize_origin; - int m_column_resize_original_width{0}; - int m_resizing_column{-1}; - int m_pressed_column_header_index{-1}; - bool m_pressed_column_header_is_pressed{false}; - int m_hovered_column_header_index{-1}; + virtual void onModelUpdate( unsigned flags ); + + virtual void createOrUpdateColumns(); + + virtual void onSizeChange(); + + UILinearLayout* mHeader; + Float mDragBorderDistance{8}; + bool mHeadersVisible{true}; }; }}} // namespace EE::UI::Abstract diff --git a/include/eepp/ui/abstract/uiabstractview.hpp b/include/eepp/ui/abstract/uiabstractview.hpp index 597f0bd2f..4736e8ff2 100644 --- a/include/eepp/ui/abstract/uiabstractview.hpp +++ b/include/eepp/ui/abstract/uiabstractview.hpp @@ -11,60 +11,56 @@ namespace EE { namespace UI { namespace Abstract { class EE_API UIAbstractView : public UIWidget { public: - void set_model( std::shared_ptr ); - Model* model() { return m_model.get(); } - const Model* model() const { return m_model.get(); } + void setModel( std::shared_ptr ); + Model* getModel() { return mModel.get(); } + const Model* getModel() const { return mModel.get(); } - ModelSelection& selection() { return m_selection; } - const ModelSelection& selection() const { return m_selection; } - virtual void select_all() = 0; + ModelSelection& getSelection() { return mSelection; } + const ModelSelection& getSelection() const { return mSelection; } + virtual void selectAll() = 0; - bool is_editable() const { return m_editable; } - void set_editable( bool editable ) { m_editable = editable; } + bool isEditable() const { return mEditable; } + void setEditable( bool editable ) { mEditable = editable; } - virtual void didUpdateModel( unsigned flags ); - virtual void didUpdateSelection(); - - virtual Vector2i content_rect( const ModelIndex& ) const { return {}; } - virtual ModelIndex index_at_event_position( const Vector2i& ) const = 0; - - void set_activates_on_selection( bool b ) { m_activates_on_selection = b; } - bool activates_on_selection() const { return m_activates_on_selection; } - - std::function on_selection_change; - std::function on_activation; - std::function on_selection; - std::function on_drop; - - //std::function( const ModelIndex& )> aid_create_editing_delegate; + void setActivatesOnSelection( bool b ) { mActivatesOnSelection = b; } + bool getActivatesOnSelection() const { return mActivatesOnSelection; } void notifySelectionChange(); protected: - UIAbstractView(); + friend class Model; + + virtual void onModelUpdate( unsigned flags ); + + virtual void onModelSelectionChange(); + + UIAbstractView( const std::string& tag ); virtual ~UIAbstractView(); - virtual void didScroll(); - void set_hovered_index( const ModelIndex& ); + void setHoveredIndex( const ModelIndex& ); void activate( const ModelIndex& ); - void activate_selected(); + void activateSelected(); - bool m_editable{false}; - ModelIndex m_edit_index; - UIWidget* m_edit_widget; - Vector2i m_edit_widget_content_rect; + bool mEditable{false}; + ModelIndex mEditIndex; + UIWidget* mEditWidget; + Rect mEditWidgetContentRect; - Vector2i m_left_mousedown_position; - bool m_might_drag{false}; + Vector2i mLeftMouseDownPosition; + bool mMightDrag{false}; - ModelIndex m_hovered_index; + ModelIndex mHoveredIndex; - private: - std::shared_ptr m_model; - std::unique_ptr m_editing_delegate; - ModelSelection m_selection; - bool m_activates_on_selection{false}; + std::shared_ptr mModel; + std::unique_ptr mEditingDelegate; + ModelSelection mSelection; + bool mActivatesOnSelection{false}; + + std::function mOnSelectionChange; + std::function mOnActivation; + std::function mOnSelection; + std::function mOnDrop; }; }}} // namespace EE::UI::Abstract diff --git a/include/eepp/ui/uilinearlayout.hpp b/include/eepp/ui/uilinearlayout.hpp index a863c4df0..919fcba97 100644 --- a/include/eepp/ui/uilinearlayout.hpp +++ b/include/eepp/ui/uilinearlayout.hpp @@ -7,6 +7,8 @@ namespace EE { namespace UI { class EE_API UILinearLayout : public UILayout { public: + static UILinearLayout* NewWithTag( const std::string& tag, const UIOrientation& orientation ); + static UILinearLayout* New(); static UILinearLayout* NewVertical(); diff --git a/include/eepp/ui/uipushbutton.hpp b/include/eepp/ui/uipushbutton.hpp index 4f0df8af2..85f979ffa 100644 --- a/include/eepp/ui/uipushbutton.hpp +++ b/include/eepp/ui/uipushbutton.hpp @@ -11,6 +11,8 @@ class EE_API UIPushButton : public UIWidget { public: static UIPushButton* New(); + static UIPushButton* NewWithTag( const std::string& tag ); + UIPushButton(); virtual ~UIPushButton(); @@ -40,6 +42,7 @@ class EE_API UIPushButton : public UIWidget { virtual std::string getPropertyString( const PropertyDefinition* propertyDef, const Uint32& propertyIndex = 0 ); + void setTextAlign( const Uint32& align ); protected: UIImage* mIcon; UITextView* mTextBox; diff --git a/include/eepp/ui/uitextview.hpp b/include/eepp/ui/uitextview.hpp index 94d5eda71..de38eacc1 100644 --- a/include/eepp/ui/uitextview.hpp +++ b/include/eepp/ui/uitextview.hpp @@ -86,6 +86,7 @@ class EE_API UITextView : public UIWidget { virtual std::string getPropertyString( const PropertyDefinition* propertyDef, const Uint32& propertyIndex = 0 ); + void setTextAlign( const Uint32& align ); protected: Text* mTextCache; String mString; diff --git a/src/eepp/ui/abstract/model.cpp b/src/eepp/ui/abstract/model.cpp index fa04b8cef..bafd03d22 100644 --- a/src/eepp/ui/abstract/model.cpp +++ b/src/eepp/ui/abstract/model.cpp @@ -1,12 +1,12 @@ #include -#include +#include namespace EE { namespace UI { namespace Abstract { -void Model::didUpdate( unsigned flags ) { - if ( onUpdate ) - onUpdate(); - forEachView( [&]( UIAbstractView* view ) { view->didUpdateModel( flags ); } ); +void Model::onModelUpdate( unsigned flags ) { + if ( mOnUpdate ) + mOnUpdate(); + forEachView( [&]( UIAbstractView* view ) { view->onModelUpdate( flags ); } ); } void Model::forEachView( std::function callback ) { @@ -26,6 +26,10 @@ ModelIndex Model::createIndex( int row, int column, const void* data ) const { return ModelIndex( *this, row, column, const_cast( data ) ); } +void Model::setOnUpdate( const std::function& onUpdate ) { + mOnUpdate = onUpdate; +} + ModelIndex Model::sibling( int row, int column, const ModelIndex& parent ) const { if ( !parent.isValid() ) return index( row, column, {} ); diff --git a/src/eepp/ui/abstract/modelselection.cpp b/src/eepp/ui/abstract/modelselection.cpp index d273a5fb7..5faeec7c2 100644 --- a/src/eepp/ui/abstract/modelselection.cpp +++ b/src/eepp/ui/abstract/modelselection.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include namespace EE { namespace UI { namespace Abstract { diff --git a/src/eepp/ui/abstract/uiabstracttableview.cpp b/src/eepp/ui/abstract/uiabstracttableview.cpp index fe3fa82dc..f24e008c1 100644 --- a/src/eepp/ui/abstract/uiabstracttableview.cpp +++ b/src/eepp/ui/abstract/uiabstracttableview.cpp @@ -1,445 +1,197 @@ #include +#include +#include +#include namespace EE { namespace UI { namespace Abstract { -static const int minimum_column_width = 2; +class UITableHeaderColumn : public UIPushButton { + public: + UITableHeaderColumn( UIAbstractTableView* view, const size_t& colIndex ) : + UIPushButton( "table::header::column" ), mView( view ), mColIndex( colIndex ) { + setDragEnabled( true ); + } -UIAbstractTableView::UIAbstractTableView() { - set_should_hide_unnecessary_scrollbars( true ); + protected: + UIAbstractTableView* mView; + size_t mColIndex; + + Uint32 onCalculateDrag( const Vector2f& position, const Uint32& flags ) { + if ( isDragEnabled() && isDragging() && NULL != getEventDispatcher() ) { + EventDispatcher* eventDispatcher = getEventDispatcher(); + if ( !( flags /*press trigger*/ & mDragButton ) ) { + setDragging( false ); + eventDispatcher->setNodeDragging( NULL ); + return 1; + } + Vector2f pos( eefloor( position.x ), eefloor( position.y ) ); + if ( mDragPoint != pos && std::abs( mDragPoint.x - pos.x ) > 1.f ) { + Sizef dragDiff( ( Float )( mDragPoint.x - pos.x ), 0 ); + if ( onDrag( pos, flags, dragDiff ) ) { + mDragPoint = pos; + eventDispatcher->setNodeDragging( this ); + } + } + } + return 1; + } + + Uint32 onMouseDown( const Vector2i& position, const Uint32& flags ) { + Vector2f localPos( convertToNodeSpace( position.asFloat() ) ); + if ( NULL != getEventDispatcher() && !getEventDispatcher()->isNodeDragging() && + !( getEventDispatcher()->getLastPressTrigger() & mDragButton ) && + ( flags & mDragButton ) && isDragEnabled() && !isDragging() && + localPos.x >= mSize.getWidth() - mView->getDragBorderDistance() ) { + startDragging( position.asFloat() ); + } + pushState( UIState::StatePressed ); + return Node::onMouseDown( position, flags ); + } + + Uint32 onDrag( const Vector2f& position, const Uint32&, const Sizef& dragDiff ) { + Vector2f localPos( convertToNodeSpace( position ) ); + if ( isDragging() || localPos.x >= mSize.getWidth() - mView->getDragBorderDistance() ) { + setPixelsSize( mSize.x - dragDiff.x, mSize.getHeight() ); + mView->columnData( mColIndex ).width = mSize.getWidth(); + return 1; + } + return 0; + } + + Uint32 onMouseLeave( const Vector2i& position, const Uint32& flags ) { + if ( !isDragging() ) + getUISceneNode()->setCursor( Cursor::Arrow ); + return UIPushButton::onMouseLeave( position, flags ); + } + + Uint32 onMouseMove( const Vector2i& position, const Uint32& flags ) { + Vector2f localPos( convertToNodeSpace( position.asFloat() ) ); + if ( isDragging() || localPos.x >= mSize.getWidth() - mView->getDragBorderDistance() ) { + getUISceneNode()->setCursor( Cursor::SizeWE ); + } else if ( !isDragging() ) { + getUISceneNode()->setCursor( Cursor::Arrow ); + } + return UIPushButton::onMouseMove( position, flags ); + }; + + Uint32 onDragStop( const Vector2i& pos, const Uint32& flags ) { + getUISceneNode()->setCursor( Cursor::Arrow ); + return UIPushButton::onDragStop( pos, flags ); + } +}; + +UIAbstractTableView* UIAbstractTableView::New() { + return eeNew( UIAbstractTableView, ( "abstractview" ) ); +} + +UIAbstractTableView::UIAbstractTableView( const std::string& tag ) : + UIAbstractView( tag ), mDragBorderDistance( PixelDensity::dpToPx( 4 ) ) { + mHeader = UILinearLayout::NewWithTag( "table::header", UIOrientation::Horizontal ); + mHeader->setLayoutSizePolicy( SizePolicy::Fixed, SizePolicy::Fixed ); + mHeader->setParent( this ); + mHeader->setVisible( true ); + mHeader->setEnabled( true ); } UIAbstractTableView::~UIAbstractTableView() {} -void UIAbstractTableView::select_all() { - selection().clear(); - for ( int item_index = 0; item_index < item_count(); ++item_index ) { - auto index = model()->index( item_index ); - selection().add( index ); +void UIAbstractTableView::selectAll() { + getSelection().clear(); + for ( size_t itemIndex = 0; itemIndex < itemCount(); ++itemIndex ) { + auto index = getModel()->index( itemIndex ); + getSelection().add( index ); } } -void UIAbstractTableView::update_column_sizes() { - if ( !model() ) - return; - - Model& model = *this->model(); - int column_count = model.columnCount(); - int row_count = model.rowCount(); - int key_column = model.keyColumn(); - - for ( int column = 0; column < column_count; ++column ) { - if ( is_column_hidden( column ) ) - continue; - int header_width = header_font().width( model.column_name( column ) ); - if ( column == key_column && model.isColumnSortable( column ) ) - header_width += font().width( " \xE2\xAC\x86" ); // UPWARDS BLACK ARROW - int column_width = header_width; - for ( int row = 0; row < row_count; ++row ) { - auto cell_data = model.data( model.index( row, column ) ); - int cell_width = 0; - if ( cell_data.is_icon() ) { - cell_width = item_height(); - } else if ( cell_data.is_bitmap() ) { - cell_width = cell_data.as_bitmap().width(); - } else if ( cell_data.is_valid() ) { - cell_width = font().width( cell_data.to_string() ); - } - column_width = max( column_width, cell_width ); - } - auto& column_data = this->column_data( column ); - column_data.width = max( column_data.width, column_width ); - column_data.has_initialized_width = true; - } -} - -void UIAbstractTableView::update_content_size() { - if ( !model() ) - return set_content_size( {} ); - - int content_width = 0; - int column_count = model()->column_count(); - for ( int i = 0; i < column_count; ++i ) { - if ( !is_column_hidden( i ) ) - content_width += column_width( i ) + horizontal_padding() * 2; - } - int content_height = item_count() * item_height(); - - set_content_size( {content_width, content_height} ); - set_size_occupied_by_fixed_elements( {0, header_height()} ); -} - -Rect UIAbstractTableView::header_rect( int column_index ) const { - if ( !model() ) - return {}; - if ( is_column_hidden( column_index ) ) - return {}; - int x_offset = 0; - for ( int i = 0; i < column_index; ++i ) { - if ( is_column_hidden( i ) ) - continue; - x_offset += column_width( i ) + horizontal_padding() * 2; - } - return {x_offset, 0, column_width( column_index ) + horizontal_padding() * 2, header_height()}; -} - -void UIAbstractTableView::set_hovered_header_index( int index ) { - if ( m_hovered_column_header_index == index ) - return; - m_hovered_column_header_index = index; - update_headers(); -} - -void UIAbstractTableView::paint_headers( Painter& painter ) { - if ( !headers_visible() ) - return; - int exposed_width = max( content_size().width(), width() ); - painter.fill_rect( {0, 0, exposed_width, header_height()}, palette().button() ); - painter.draw_line( {0, 0}, {exposed_width - 1, 0}, palette().threed_highlight() ); - painter.draw_line( {0, header_height() - 1}, {exposed_width - 1, header_height() - 1}, - palette().threed_shadow1() ); - int x_offset = 0; - int column_count = model()->column_count(); - for ( int column_index = 0; column_index < column_count; ++column_index ) { - if ( is_column_hidden( column_index ) ) - continue; - int column_width = this->column_width( column_index ); - bool is_key_column = model()->key_column() == column_index; - Rect cell_rect( x_offset, 0, column_width + horizontal_padding() * 2, header_height() ); - bool pressed = - column_index == m_pressed_column_header_index && m_pressed_column_header_is_pressed; - bool hovered = column_index == m_hovered_column_header_index && - model()->is_column_sortable( column_index ); - Gfx::StylePainter::paint_button( painter, cell_rect, palette(), Gfx::ButtonStyle::Normal, - pressed, hovered ); - String text; - if ( is_key_column ) { - StringBuilder builder; - builder.append( model()->column_name( column_index ) ); - auto sort_order = model()->sort_order(); - if ( sort_order == SortOrder::Ascending ) - builder.append( " \xE2\xAC\x86" ); // UPWARDS BLACK ARROW - else if ( sort_order == SortOrder::Descending ) - builder.append( " \xE2\xAC\x87" ); // DOWNWARDS BLACK ARROW - text = builder.to_string(); - } else { - text = model()->column_name( column_index ); - } - auto text_rect = cell_rect.translated( horizontal_padding(), 0 ); - if ( pressed ) - text_rect.move_by( 1, 1 ); - painter.draw_text( text_rect, text, header_font(), Gfx::TextAlignment::CenterLeft, - palette().button_text() ); - x_offset += column_width + horizontal_padding() * 2; - } -} - -bool UIAbstractTableView::is_column_hidden( int column ) const { - return !column_data( column ).visibility; -} - -void UIAbstractTableView::set_column_hidden( int column, bool hidden ) { - auto& column_data = this->column_data( column ); - if ( column_data.visibility == !hidden ) - return; - column_data.visibility = !hidden; - if ( column_data.visibility_action ) { - column_data.visibility_action->set_checked( !hidden ); - } - update_content_size(); - update(); -} - -void UIAbstractTableView::set_cell_painting_delegate( - int column, UITableCellDrawDelegate* delegate ) { - column_data( column ).cell_painting_delegate = delegate; -} - -void UIAbstractTableView::update_headers() { - Rect rect{0, 0, frame_inner_rect().width(), header_height()}; - rect.move_by( frame_thickness(), frame_thickness() ); - update( rect ); -} - -UIAbstractTableView::ColumnData& UIAbstractTableView::column_data( int column ) const { - if ( static_cast( column ) >= m_column_data.size() ) - m_column_data.resize( column + 1 ); - return m_column_data.at( column ); -} - -Rect UIAbstractTableView::column_resize_grabbable_rect( int column ) const { - if ( !model() ) - return {}; - auto header_rect = this->header_rect( column ); - return {header_rect.right() - 1, header_rect.top(), 4, header_rect.height()}; -} - -int UIAbstractTableView::column_width( int column_index ) const { - if ( !model() ) +size_t UIAbstractTableView::itemCount() const { + if ( !getModel() ) return 0; - return column_data( column_index ).width; + return getModel()->rowCount(); } -void UIAbstractTableView::mousemove_event( MouseEvent& event ) { - if ( !model() ) - return UIAbstractView::mousemove_event( event ); - - auto adjusted_position = this->adjusted_position( event.position() ); - Vector2i horizontally_adjusted_position( adjusted_position.x(), event.position().y() ); - - if ( m_in_column_resize ) { - auto delta = adjusted_position - m_column_resize_origin; - int new_width = m_column_resize_original_width + delta.x(); - if ( new_width <= minimum_column_width ) - new_width = minimum_column_width; - ASSERT( m_resizing_column >= 0 && m_resizing_column < model()->column_count() ); - auto& column_data = this->column_data( m_resizing_column ); - if ( column_data.width != new_width ) { - column_data.width = new_width; - update_content_size(); - update(); - } - return; - } - - if ( m_pressed_column_header_index != -1 ) { - auto header_rect = this->header_rect( m_pressed_column_header_index ); - if ( header_rect.contains( horizontally_adjusted_position ) ) { - set_hovered_header_index( m_pressed_column_header_index ); - if ( !m_pressed_column_header_is_pressed ) - update_headers(); - m_pressed_column_header_is_pressed = true; - } else { - set_hovered_header_index( -1 ); - if ( m_pressed_column_header_is_pressed ) - update_headers(); - m_pressed_column_header_is_pressed = false; - } - return; - } - - if ( event.buttons() == 0 ) { - int column_count = model()->column_count(); - bool found_hovered_header = false; - for ( int i = 0; i < column_count; ++i ) { - if ( column_resize_grabbable_rect( i ).contains( horizontally_adjusted_position ) ) { - window()->set_override_cursor( StandardCursor::ResizeHorizontal ); - set_hovered_header_index( -1 ); - return; - } - if ( header_rect( i ).contains( horizontally_adjusted_position ) ) { - set_hovered_header_index( i ); - found_hovered_header = true; - } - } - if ( !found_hovered_header ) - set_hovered_header_index( -1 ); - } - window()->set_override_cursor( StandardCursor::None ); - - UIAbstractView::mousemove_event( event ); +void UIAbstractTableView::onModelUpdate( unsigned flags ) { + UIAbstractView::onModelUpdate( flags ); + createOrUpdateColumns(); } -Uint32 UIAbstractTableView::onMouseUp( const Vector2i& position, const Uint32& flags ) { - auto adjusted_position = this->adjusted_position( event.position() ); - Vector2i horizontally_adjusted_position( adjusted_position.x(), event.position().y() ); - if ( event.button() == MouseButton::Left ) { - if ( m_in_column_resize ) { - if ( !column_resize_grabbable_rect( m_resizing_column ) - .contains( horizontally_adjusted_position ) ) - window()->set_override_cursor( StandardCursor::None ); - m_in_column_resize = false; - return; - } - if ( m_pressed_column_header_index != -1 ) { - auto header_rect = this->header_rect( m_pressed_column_header_index ); - if ( header_rect.contains( horizontally_adjusted_position ) ) { - auto new_sort_order = SortOrder::Ascending; - if ( model()->key_column() == m_pressed_column_header_index ) - new_sort_order = model()->sort_order() == SortOrder::Ascending - ? SortOrder::Descending - : SortOrder::Ascending; - model()->set_key_column_and_sort_order( m_pressed_column_header_index, - new_sort_order ); - } - m_pressed_column_header_index = -1; - m_pressed_column_header_is_pressed = false; - update_headers(); - return; - } - } - - UIAbstractView::mouseup_event( event ); -} - -void UIAbstractTableView::mousedown_event( MouseEvent& event ) { - if ( !model() ) - return UIAbstractView::mousedown_event( event ); - - if ( event.button() != MouseButton::Left ) - return UIAbstractView::mousedown_event( event ); - - auto adjusted_position = this->adjusted_position( event.position() ); - Vector2i horizontally_adjusted_position( adjusted_position.x(), event.position().y() ); - - if ( event.y() < header_height() ) { - int column_count = model()->column_count(); - for ( int i = 0; i < column_count; ++i ) { - if ( column_resize_grabbable_rect( i ).contains( horizontally_adjusted_position ) ) { - m_resizing_column = i; - m_in_column_resize = true; - m_column_resize_original_width = column_width( i ); - m_column_resize_origin = adjusted_position; - return; - } - auto header_rect = this->header_rect( i ); - if ( header_rect.contains( horizontally_adjusted_position ) && - model()->is_column_sortable( i ) ) { - m_pressed_column_header_index = i; - m_pressed_column_header_is_pressed = true; - update_headers(); - return; - } - } +void UIAbstractTableView::createOrUpdateColumns() { + Model* model = getModel(); + if ( !model ) return; - } - bool is_toggle; - auto index = index_at_event_position( event.position(), is_toggle ); + mHeader->setPixelsSize( mSize.getWidth(), getHeaderHeight() ); - if ( index.is_valid() && is_toggle && model()->row_count( index ) ) { - toggle_index( index ); - return; - } + size_t count = model->columnCount(); - UIAbstractView::mousedown_event( event ); -} - -ModelIndex UIAbstractTableView::index_at_event_position( const Vector2i& position, - bool& is_toggle ) const { - is_toggle = false; - if ( !model() ) - return {}; - - auto adjusted_position = this->adjusted_position( position ); - for ( int row = 0, row_count = model()->row_count(); row < row_count; ++row ) { - if ( !row_rect( row ).contains( adjusted_position ) ) + for ( size_t i = 0; i < count; i++ ) { + ColumnData& col = columnData( i ); + if ( !col.widget ) { + /*UIPushButton::NewWithTag( "table::header::column" )*/; + col.widget = eeNew( UITableHeaderColumn, ( this, i ) ); + col.widget->setParent( mHeader ); + col.widget->setEnabled( true ); + col.widget->setVisible( true ); + } + col.visible = !isColumnHidden( i ); + col.widget->setVisible( col.visible ); + if ( !col.visible ) continue; - for ( int column = 0, column_count = model()->column_count(); column < column_count; - ++column ) { - if ( !content_rect( row, column ).contains( adjusted_position ) ) - continue; - return model()->index( row, column ); - } - return model()->index( row, 0 ); + col.widget->setLayoutSizePolicy( SizePolicy::WrapContent, SizePolicy::WrapContent ); + col.widget->setText( model->columnName( i ) ); + col.widget->reloadStyle( true, true, true ); + col.width = eeceil( eemax( col.width, col.widget->getPixelsSize().getWidth() ) ); + col.widget->setLayoutSizePolicy( SizePolicy::Fixed, SizePolicy::Fixed ); + col.widget->setPixelsSize( col.width, getHeaderHeight() ); } - return {}; -} -ModelIndex UIAbstractTableView::index_at_event_position( const Vector2i& position ) const { - bool is_toggle; - auto index = index_at_event_position( position, is_toggle ); - return is_toggle ? ModelIndex() : index; -} - -int UIAbstractTableView::item_count() const { - if ( !model() ) - return 0; - return model()->row_count(); -} - -void UIAbstractTableView::move_selection( int steps ) { - if ( !model() ) - return; - auto& model = *this->model(); - ModelIndex new_index; - if ( !selection().is_empty() ) { - auto old_index = selection().first(); - new_index = model.index( old_index.row() + steps, old_index.column() ); - } else { - new_index = model.index( 0, 0 ); - } - if ( model.is_valid( new_index ) ) { - selection().set( new_index ); - scroll_into_view( new_index, Orientation::Vertical ); - update(); - } -} - -void UIAbstractTableView::scroll_into_view( const ModelIndex& index, Orientation orientation ) { - auto rect = row_rect( index.row() ).translated( 0, -header_height() ); - ScrollableWidget::scroll_into_view( rect, orientation ); -} - -void UIAbstractTableView::doubleclick_event( MouseEvent& event ) { - if ( !model() ) - return; - if ( event.button() == MouseButton::Left ) { - if ( event.y() < header_height() ) - return; - if ( !selection().is_empty() ) { - if ( is_editable() ) - begin_editing( selection().first() ); - else - activate_selected(); + if ( count < mColumn.size() ) { + for ( size_t i = count; i < mColumn.size(); i++ ) { + ColumnData& col = columnData( i ); + col.width = 0; + col.visible = false; + if ( col.widget ) { + col.widget->close(); + col.widget = nullptr; + } } } } -void UIAbstractTableView::context_menu_event( ContextMenuEvent& event ) { - if ( !model() ) - return; - if ( event.position().y() < header_height() ) { - ensure_header_context_menu().popup( event.screen_position() ); - return; +Float UIAbstractTableView::getHeaderHeight() const { + return mHeadersVisible ? eeceil( columnData( 0 ).widget + ? columnData( 0 ).widget->getPixelsSize().getHeight() + : 16 ) + : 0; +} + +void UIAbstractTableView::onSizeChange() { + UIWidget::onSizeChange(); + mHeader->setPixelsSize( mSize.getWidth(), getHeaderHeight() ); +} + +const Float& UIAbstractTableView::getDragBorderDistance() const { + return mDragBorderDistance; +} + +void UIAbstractTableView::setDragBorderDistance( const Float& dragBorderDistance ) { + mDragBorderDistance = dragBorderDistance; +} + +UIAbstractTableView::ColumnData& UIAbstractTableView::columnData( const size_t& column ) const { + if ( column >= mColumn.size() ) + mColumn.resize( column + 1 ); + return mColumn[column]; +} + +bool UIAbstractTableView::isColumnHidden( const size_t& column ) const { + return !columnData( column ).visible; +} + +void UIAbstractTableView::setColumnHidden( const size_t& column, bool hidden ) { + if ( columnData( column ).visible != !hidden ) { + columnData( column ).visible = !hidden; + createOrUpdateColumns(); } - - bool is_toggle; - auto index = index_at_event_position( event.position(), is_toggle ); - if ( index.is_valid() ) { - if ( !selection().contains( index ) ) - selection().set( index ); - } else { - selection().clear(); - } - if ( on_context_menu_request ) - on_context_menu_request( index, event ); -} - -void UIAbstractTableView::leave_event( Core::Event& event ) { - UIAbstractView::leave_event( event ); - window()->set_override_cursor( StandardCursor::None ); - set_hovered_header_index( -1 ); -} - -Rect UIAbstractTableView::content_rect( int row, int column ) const { - auto row_rect = this->row_rect( row ); - int x = 0; - for ( int i = 0; i < column; ++i ) - x += column_width( i ) + horizontal_padding() * 2; - - return {row_rect.x() + x, row_rect.y(), column_width( column ) + horizontal_padding() * 2, - item_height()}; -} - -Rect UIAbstractTableView::content_rect( const ModelIndex& index ) const { - return content_rect( index.row(), index.column() ); -} - -Rect UIAbstractTableView::row_rect( int item_index ) const { - return {0, header_height() + ( item_index * item_height() ), - max( content_size().width(), width() ), item_height()}; -} - -Vector2i UIAbstractTableView::adjusted_position( const Vector2i& position ) const { - return position.translated( horizontal_scrollbar().value() - frame_thickness(), - vertical_scrollbar().value() - frame_thickness() ); -} - -void UIAbstractTableView::didUpdateModel( unsigned flags ) { - UIAbstractView::didUpdateModel( flags ); - update_column_sizes(); - update_content_size(); - update(); } }}} // namespace EE::UI::Abstract diff --git a/src/eepp/ui/abstract/uiabstractview.cpp b/src/eepp/ui/abstract/uiabstractview.cpp index 84a25aad3..3bc527974 100644 --- a/src/eepp/ui/abstract/uiabstractview.cpp +++ b/src/eepp/ui/abstract/uiabstractview.cpp @@ -1,59 +1,53 @@ -#include #include -#include - -using namespace EE::Scene; -using namespace EE::Window; namespace EE { namespace UI { namespace Abstract { -UIAbstractView::UIAbstractView() : m_selection( this ) {} +UIAbstractView::UIAbstractView( const std::string& tag ) : UIWidget( tag ), mSelection( this ) {} UIAbstractView::~UIAbstractView() {} -void UIAbstractView::set_model( std::shared_ptr model ) { - if ( model.get() == m_model.get() ) +void UIAbstractView::setModel( std::shared_ptr model ) { + if ( model.get() == mModel.get() ) return; - if ( m_model ) - m_model->unregisterView( this ); - m_model = model; - if ( m_model ) - m_model->registerView( this ); - didUpdateModel( Model::InvalidateAllIndexes ); + if ( mModel ) + mModel->unregisterView( this ); + mModel = model; + if ( mModel ) + mModel->registerView( this ); + onModelUpdate( Model::InvalidateAllIndexes ); } -void UIAbstractView::didUpdateModel( unsigned flags ) { - m_hovered_index = {}; - if ( !model() || ( flags & Model::InvalidateAllIndexes ) ) { - selection().clear(); +void UIAbstractView::onModelUpdate( unsigned flags ) { + mHoveredIndex = {}; + if ( !getModel() || ( flags & Model::InvalidateAllIndexes ) ) { + getSelection().clear(); } else { - selection().removeMatching( [this]( auto& index ) { return !model()->isValid( index ); } ); + getSelection().removeMatching( + [this]( auto& index ) { return !getModel()->isValid( index ); } ); } } -void UIAbstractView::didUpdateSelection() { - if ( model() && on_selection && selection().first().isValid() ) - on_selection( selection().first() ); +void UIAbstractView::onModelSelectionChange() { + if ( getModel() && mOnSelection && getSelection().first().isValid() ) + mOnSelection( getSelection().first() ); } -void UIAbstractView::didScroll() {} - void UIAbstractView::activate( const ModelIndex& index ) { - if ( on_activation ) - on_activation( index ); + if ( mOnActivation ) + mOnActivation( index ); } -void UIAbstractView::activate_selected() { - if ( !on_activation ) +void UIAbstractView::activateSelected() { + if ( !mOnActivation ) return; - selection().forEachIndex( [this]( auto& index ) { on_activation( index ); } ); + getSelection().forEachIndex( [this]( auto& index ) { mOnActivation( index ); } ); } void UIAbstractView::notifySelectionChange() { - didUpdateSelection(); - if ( on_selection_change ) - on_selection_change(); + onModelSelectionChange(); + if ( mOnSelectionChange ) + mOnSelectionChange(); } }}} // namespace EE::UI::Abstract diff --git a/src/eepp/ui/uilinearlayout.cpp b/src/eepp/ui/uilinearlayout.cpp index 82873da02..347497740 100644 --- a/src/eepp/ui/uilinearlayout.cpp +++ b/src/eepp/ui/uilinearlayout.cpp @@ -4,6 +4,11 @@ namespace EE { namespace UI { +UILinearLayout* UILinearLayout::NewWithTag( const std::string& tag, + const UIOrientation& orientation ) { + return eeNew( UILinearLayout, ( tag, orientation ) ); +} + UILinearLayout* UILinearLayout::New() { return eeNew( UILinearLayout, () ); } diff --git a/src/eepp/ui/uipushbutton.cpp b/src/eepp/ui/uipushbutton.cpp index 1b5e38cee..a42c7f807 100644 --- a/src/eepp/ui/uipushbutton.cpp +++ b/src/eepp/ui/uipushbutton.cpp @@ -11,6 +11,10 @@ UIPushButton* UIPushButton::New() { return eeNew( UIPushButton, () ); } +UIPushButton* UIPushButton::NewWithTag( const std::string& tag ) { + return eeNew( UIPushButton, ( tag ) ); +} + UIPushButton::UIPushButton( const std::string& tag ) : UIWidget( tag ), mIcon( NULL ), mTextBox( NULL ) { mFlags |= ( UI_AUTO_SIZE | UI_VALIGN_CENTER | UI_HALIGN_CENTER ); @@ -221,6 +225,8 @@ void UIPushButton::onSizeChange() { if ( NULL != eWidget && eWidget->isVisible() ) { eWidget->setPixelsPosition( ePos ); } + + UIWidget::onSizeChange(); } void UIPushButton::setTheme( UITheme* Theme ) { @@ -322,6 +328,12 @@ UIWidget* UIPushButton::getExtraInnerWidget() { return NULL; } +void UIPushButton::setTextAlign( const Uint32& align ) { + mFlags &= ~( UI_HALIGN_CENTER | UI_HALIGN_RIGHT ); + mFlags |= align; + onAlignChange(); +} + std::string UIPushButton::getPropertyString( const PropertyDefinition* propertyDef, const Uint32& propertyIndex ) { if ( NULL == propertyDef ) @@ -336,6 +348,11 @@ std::string UIPushButton::getPropertyString( const PropertyDefinition* propertyD case PropertyId::MinIconSize: return String::format( "%ddp", mIconMinSize.getWidth() ) + " " + String::format( "%ddp", mIconMinSize.getHeight() ); + case PropertyId::TextAlign: + return Font::getHorizontalAlign( getFlags() ) == UI_HALIGN_CENTER + ? "center" + : ( Font::getHorizontalAlign( getFlags() ) == UI_HALIGN_RIGHT ? "right" + : "left" ); case PropertyId::Color: case PropertyId::ShadowColor: case PropertyId::SelectionColor: @@ -347,7 +364,6 @@ std::string UIPushButton::getPropertyString( const PropertyDefinition* propertyD case PropertyId::TextStrokeWidth: case PropertyId::TextStrokeColor: case PropertyId::TextSelection: - case PropertyId::TextAlign: return mTextBox->getPropertyString( propertyDef, propertyIndex ); default: return UIWidget::getPropertyString( propertyDef, propertyIndex ); @@ -381,6 +397,16 @@ bool UIPushButton::applyProperty( const StyleSheetProperty& attribute ) { case PropertyId::MinIconSize: setIconMinimumSize( attribute.asSizei() ); break; + case PropertyId::TextAlign: { + std::string align = String::toLower( attribute.value() ); + if ( align == "center" ) + setTextAlign( UI_HALIGN_CENTER ); + else if ( align == "left" ) + setTextAlign( UI_HALIGN_LEFT ); + else if ( align == "right" ) + setTextAlign( UI_HALIGN_RIGHT ); + break; + } case PropertyId::Color: case PropertyId::ShadowColor: case PropertyId::SelectionColor: @@ -392,7 +418,6 @@ bool UIPushButton::applyProperty( const StyleSheetProperty& attribute ) { case PropertyId::TextStrokeWidth: case PropertyId::TextStrokeColor: case PropertyId::TextSelection: - case PropertyId::TextAlign: attributeSet = mTextBox->applyProperty( attribute ); break; default: diff --git a/src/eepp/ui/uitextview.cpp b/src/eepp/ui/uitextview.cpp index 9b6d3c6c8..5254ad421 100644 --- a/src/eepp/ui/uitextview.cpp +++ b/src/eepp/ui/uitextview.cpp @@ -649,11 +649,11 @@ bool UITextView::applyProperty( const StyleSheetProperty& attribute ) { case PropertyId::TextAlign: { std::string align = String::toLower( attribute.value() ); if ( align == "center" ) - setFlags( UI_HALIGN_CENTER ); + setTextAlign( UI_HALIGN_CENTER ); else if ( align == "left" ) - setFlags( UI_HALIGN_LEFT ); + setTextAlign( UI_HALIGN_LEFT ); else if ( align == "right" ) - setFlags( UI_HALIGN_RIGHT ); + setTextAlign( UI_HALIGN_RIGHT ); break; } default: @@ -703,4 +703,10 @@ std::string UITextView::getPropertyString( const PropertyDefinition* propertyDef } } +void UITextView::setTextAlign( const Uint32& align ) { + mFlags &= ~(UI_HALIGN_CENTER|UI_HALIGN_RIGHT); + mFlags |= align; + onAlignChange(); +} + }} // namespace EE::UI diff --git a/src/eepp/ui/uiwindow.cpp b/src/eepp/ui/uiwindow.cpp index d616ede17..7df9759fd 100644 --- a/src/eepp/ui/uiwindow.cpp +++ b/src/eepp/ui/uiwindow.cpp @@ -60,7 +60,7 @@ UIWindow::UIWindow( UIWindow::WindowBaseContainerType type, const StyleConfig& w switch ( type ) { case LINEAR_LAYOUT: - mContainer = UILinearLayout::NewWithTag( "window::container" ); + mContainer = UILinearLayout::NewWithTag( "window::container", UIOrientation::Vertical ); break; case RELATIVE_LAYOUT: mContainer = UIRelativeLayout::NewWithTag( "window::container" ); diff --git a/src/tests/ui_perf_test/ui_perf_test.cpp b/src/tests/ui_perf_test/ui_perf_test.cpp index 41295fdf8..16ffb03e9 100644 --- a/src/tests/ui_perf_test/ui_perf_test.cpp +++ b/src/tests/ui_perf_test/ui_perf_test.cpp @@ -1,4 +1,23 @@ #include +#include +#include + +using namespace EE::UI::Abstract; + +class TestModel : public Model { + public: + virtual int rowCount( const ModelIndex& = ModelIndex() ) const { return 1; } + + virtual int columnCount( const ModelIndex& = ModelIndex() ) const { return 4; } + + virtual std::string columnName( const size_t& column ) const { + return String::format( "Column %ld", column ); + } + + virtual Variant data( const ModelIndex&, Role = Role::Display ) const { return Variant(); }; + + virtual void update() {} +}; // This file is used to test some UI related stuffs. // It's not a benchmark or a real test suite. @@ -13,6 +32,21 @@ void mainLoop() { win->close(); } + UISceneNode* uiSceneNode = SceneManager::instance()->getUISceneNode(); + + if ( win->getInput()->isKeyUp( KEY_F6 ) ) { + uiSceneNode->setHighlightFocus( !uiSceneNode->getHighlightFocus() ); + uiSceneNode->setHighlightOver( !uiSceneNode->getHighlightOver() ); + } + + if ( win->getInput()->isKeyUp( KEY_F7 ) ) { + uiSceneNode->setDrawBoxes( !uiSceneNode->getDrawBoxes() ); + } + + if ( win->getInput()->isKeyUp( KEY_F8 ) ) { + uiSceneNode->setDrawDebugData( !uiSceneNode->getDrawDebugData() ); + } + // Update the UI scene. SceneManager::instance()->update(); @@ -50,9 +84,10 @@ EE_MAIN_FUNC int main( int argc, char* argv[] ) { pd = "1.5x"; else if ( PixelDensity::getPixelDensity() >= 2.f ) pd = "2x"; - UITheme* theme = + /*UITheme* theme = UITheme::load( "uitheme" + pd, "uitheme" + pd, "assets/ui/uitheme" + pd + ".eta", font, - "assets/ui/uitheme.css" ); + "assets/ui/uitheme.css" );*/ + UITheme* theme = UITheme::load( "breeze", "breeze", "", font, "assets/ui/breeze.css" ); uiSceneNode->setStyleSheet( theme->getStyleSheet() ); uiSceneNode->getUIThemeManager() ->setDefaultEffectsEnabled( true ) @@ -63,6 +98,13 @@ EE_MAIN_FUNC int main( int argc, char* argv[] ) { auto* vlay = UILinearLayout::NewVertical(); vlay->setLayoutSizePolicy( SizePolicy::MatchParent, SizePolicy::MatchParent ); + auto model = std::make_shared(); + UIAbstractTableView* view = UIAbstractTableView::New(); + view->setId( "abstracttable" ); + view->setLayoutSizePolicy( SizePolicy::MatchParent, SizePolicy::MatchParent ); + view->setParent( vlay ); + view->setModel( model ); + /* ListBox test */ /* std::vector strings; for ( size_t i = 0; i < 10000; i++ ) @@ -101,7 +143,7 @@ EE_MAIN_FUNC int main( int argc, char* argv[] ) { uiSceneNode->getRoot()->childsCloseAll(); SceneManager::instance()->update();*/ - total.restart(); + /*total.restart(); for ( size_t i = 0; i < 100000; i++ ) { auto* widget = UIWidget::New(); widget->setParent( vlay ); @@ -115,7 +157,7 @@ EE_MAIN_FUNC int main( int argc, char* argv[] ) { widget->setBackgroundColor( Color::fromHsv( col ) ); } std::cout << "Time UIWidget total: " << total.getElapsedTime().asMilliseconds() << " ms" - << std::endl; + << std::endl;*/ /*UIWindow* wind = UIWindow::New(); wind->setSize( 500, 500 ); From 0df1a1b1f833ad86186d706e22848e3dc9420bc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Tue, 7 Jul 2020 03:43:18 -0300 Subject: [PATCH 3/7] More WIP. --- bin/assets/ui/breeze.css | 22 ++ include/eepp/scene/node.hpp | 6 +- include/eepp/ui/abstract/model.hpp | 8 +- include/eepp/ui/abstract/modelindex.hpp | 6 +- .../eepp/ui/abstract/uiabstracttableview.hpp | 17 +- include/eepp/ui/uiitemcontainer.hpp | 2 +- include/eepp/ui/uinode.hpp | 4 +- include/eepp/ui/uitreeview.hpp | 66 ++++++ include/eepp/ui/uiwindow.hpp | 2 +- projects/linux/ee.files | 2 + src/eepp/scene/node.cpp | 14 +- src/eepp/ui/abstract/uiabstracttableview.cpp | 47 +++- src/eepp/ui/uinode.cpp | 2 +- src/eepp/ui/uitreeview.cpp | 212 ++++++++++++++++++ src/eepp/ui/uiwindow.cpp | 2 +- src/tests/ui_perf_test/ui_perf_test.cpp | 78 ++++++- 16 files changed, 455 insertions(+), 35 deletions(-) create mode 100644 include/eepp/ui/uitreeview.hpp create mode 100644 src/eepp/ui/uitreeview.cpp diff --git a/bin/assets/ui/breeze.css b/bin/assets/ui/breeze.css index 65952e62c..a57067912 100644 --- a/bin/assets/ui/breeze.css +++ b/bin/assets/ui/breeze.css @@ -757,6 +757,28 @@ table::header::column:hover { background-color: var(--tab-hover); } +table::row { + background-color: var(--list-back); + transition: all 0.125s; +} + +table::row:nth-child(odd) { + background-color: var(--back); +} + +table::row:hover { + background-color: var(--back); +} + +table::cell { + background-color: transparent; + transition: all 0.125s; +} + +table::cell:hover { + background-color: var(--primary); +} + .appbackground { background-color: var(--back); } diff --git a/include/eepp/scene/node.hpp b/include/eepp/scene/node.hpp index d363f2178..9f4eb5b13 100644 --- a/include/eepp/scene/node.hpp +++ b/include/eepp/scene/node.hpp @@ -390,6 +390,10 @@ class EE_API Node : public Transformable { void forEachNode( std::function func ); + void forEachChild( std::function func ); + + virtual void nodeDraw(); + protected: typedef std::map> EventsMap; friend class EventDispatcher; @@ -488,8 +492,6 @@ class EE_API Node : public Transformable { virtual Uint32 onFocusLoss(); - virtual void internalDraw(); - void clipEnd(); void updateScreenPos(); diff --git a/include/eepp/ui/abstract/model.hpp b/include/eepp/ui/abstract/model.hpp index e6a06a650..dee50f412 100644 --- a/include/eepp/ui/abstract/model.hpp +++ b/include/eepp/ui/abstract/model.hpp @@ -110,9 +110,9 @@ class EE_API Model { virtual ~Model(){}; - virtual int rowCount( const ModelIndex& = ModelIndex() ) const = 0; + virtual size_t rowCount( const ModelIndex& = ModelIndex() ) const = 0; - virtual int columnCount( const ModelIndex& = ModelIndex() ) const = 0; + virtual size_t columnCount( const ModelIndex& = ModelIndex() ) const = 0; virtual std::string columnName( const size_t& /*column*/ ) const { return {}; } @@ -140,8 +140,8 @@ class EE_API Model { bool isValid( const ModelIndex& index ) const { auto parentIndex = this->parentIndex( index ); - return index.row() >= 0 && index.row() < rowCount( parentIndex ) && index.column() >= 0 && - index.column() < columnCount( parentIndex ); + return index.row() >= 0 && index.row() < (Int64)rowCount( parentIndex ) && + index.column() >= 0 && index.column() < (Int64)columnCount( parentIndex ); } virtual int keyColumn() const { return -1; } diff --git a/include/eepp/ui/abstract/modelindex.hpp b/include/eepp/ui/abstract/modelindex.hpp index 14b86a4b5..6c40fb692 100644 --- a/include/eepp/ui/abstract/modelindex.hpp +++ b/include/eepp/ui/abstract/modelindex.hpp @@ -19,6 +19,10 @@ class EE_API ModelIndex { ModelIndex parent() const; bool hasParent() const { return parent().isValid(); } + bool operator<( const ModelIndex& other ) const { + return mRow != other.mRow || mColumn != other.mColumn; + } + bool operator==( const ModelIndex& other ) const { return mModel == other.mModel && mRow == other.mRow && mColumn == other.mColumn && mData == other.mData; @@ -37,6 +41,6 @@ class EE_API ModelIndex { mModel( &model ), mRow( row ), mColumn( column ), mData( internalData ) {} }; -}}} // namespace EE::UI::Model +}}} // namespace EE::UI::Abstract #endif // EE_UI_MODEL_MODELINDEX_HPP diff --git a/include/eepp/ui/abstract/uiabstracttableview.hpp b/include/eepp/ui/abstract/uiabstracttableview.hpp index 9ce89face..ca128a6ac 100644 --- a/include/eepp/ui/abstract/uiabstracttableview.hpp +++ b/include/eepp/ui/abstract/uiabstracttableview.hpp @@ -17,13 +17,15 @@ class EE_API UIAbstractTableView : public UIAbstractView { public: static UIAbstractTableView* New(); - int rowHeight() const { return PixelDensity::dpToPx( 16 ); } + virtual Float getRowHeight() const { return getHeaderHeight(); } - Float getHeaderHeight() const; + virtual Float getHeaderHeight() const; - bool headersVisible() const { return mHeadersVisible; } + virtual Sizef getContentSize() const; - void setHeadersVisible( bool visible ) { mHeadersVisible = visible; } + bool areHeadersVisible() const; + + void setHeadersVisible( bool visible ); bool isColumnHidden( const size_t& column ) const; @@ -35,6 +37,8 @@ class EE_API UIAbstractTableView : public UIAbstractView { void setDragBorderDistance( const Float& dragBorderDistance ); + Vector2f getColumnPosition( const size_t& index ); + protected: friend class UITableHeaderColumn; @@ -52,7 +56,7 @@ class EE_API UIAbstractTableView : public UIAbstractView { mutable std::vector mColumn; - virtual size_t itemCount() const; + virtual size_t getItemCount() const; virtual void onModelUpdate( unsigned flags ); @@ -60,9 +64,10 @@ class EE_API UIAbstractTableView : public UIAbstractView { virtual void onSizeChange(); + virtual void onColumnSizeChange( const size_t& colIndex ); + UILinearLayout* mHeader; Float mDragBorderDistance{8}; - bool mHeadersVisible{true}; }; }}} // namespace EE::UI::Abstract diff --git a/include/eepp/ui/uiitemcontainer.hpp b/include/eepp/ui/uiitemcontainer.hpp index 4871319b9..a4ee49afe 100644 --- a/include/eepp/ui/uiitemcontainer.hpp +++ b/include/eepp/ui/uiitemcontainer.hpp @@ -59,7 +59,7 @@ template void UIItemContainer::drawChilds() { if ( tParent->mItems.size() ) { for ( Uint32 i = tParent->mVisibleFirst; i <= tParent->mVisibleLast; i++ ) if ( NULL != tParent->mItems[i] ) - tParent->mItems[i]->internalDraw(); + tParent->mItems[i]->nodeDraw(); } } diff --git a/include/eepp/ui/uinode.hpp b/include/eepp/ui/uinode.hpp index 213565e67..06e2cb263 100644 --- a/include/eepp/ui/uinode.hpp +++ b/include/eepp/ui/uinode.hpp @@ -257,6 +257,8 @@ class EE_API UINode : public Node { Rectf getLocalDpBounds() const; + virtual void nodeDraw(); + protected: Vector2f mDpPos; Sizef mDpSize; @@ -318,8 +320,6 @@ class EE_API UINode : public Node { void checkClose(); - virtual void internalDraw(); - virtual void onWidgetFocusLoss(); void writeFlag( const Uint32& Flag, const Uint32& Val ); diff --git a/include/eepp/ui/uitreeview.hpp b/include/eepp/ui/uitreeview.hpp new file mode 100644 index 000000000..3f69aec52 --- /dev/null +++ b/include/eepp/ui/uitreeview.hpp @@ -0,0 +1,66 @@ +#ifndef EE_UI_UITREEVIEW_HPP +#define EE_UI_UITREEVIEW_HPP + +#include +#include +#include + +using namespace EE::UI::Abstract; + +namespace EE { namespace UI { + +class EE_API UITreeView : public UIAbstractTableView { + public: + static UITreeView* New(); + + const Float& getIndentWidth() const; + + void setIndentWidth( const Float& indentWidth ); + + virtual Sizef getContentSize() const; + + virtual void drawChilds(); + + virtual Node* overFind( const Vector2f& point ); + + protected: + enum class IterationDecision { + Continue, + Break, + Stop, + }; + + Float mIndentWidth; + Sizef mContentSize; + + UITreeView(); + + virtual void createOrUpdateColumns(); + + struct MetadataForIndex; + + template void traverseTree( Callback ) const; + + mutable std::unordered_map> mViewMetadata; + mutable std::unordered_map> mWidgets; + mutable std::unordered_map mRows; + + virtual size_t getItemCount() const; + + UITreeView::MetadataForIndex& getIndexMetadata( const ModelIndex& index ) const; + + EE::UI::UIPushButton* getIndexWidget( const int& column, void* data ); + + virtual void onColumnSizeChange( const size_t& colIndex ); + + UIPushButton* updateCell( const ModelIndex& index, const size_t& col, const size_t& indentLevel, + const Float& yOffset ); + + UIWidget* updateRow( const ModelIndex& index, const Float& yOffset ); + + void updateContentSize(); +}; + +}} // namespace EE::UI + +#endif // EE_UI_UITREEVIEW_HPP diff --git a/include/eepp/ui/uiwindow.hpp b/include/eepp/ui/uiwindow.hpp index 4460cbdee..90d12940c 100644 --- a/include/eepp/ui/uiwindow.hpp +++ b/include/eepp/ui/uiwindow.hpp @@ -141,7 +141,7 @@ class EE_API UIWindow : public UIWidget { virtual bool applyProperty( const StyleSheetProperty& attribute ); - virtual void internalDraw(); + virtual void nodeDraw(); void invalidate( Node* invalidator ); diff --git a/projects/linux/ee.files b/projects/linux/ee.files index 6137ab823..40a6b5447 100644 --- a/projects/linux/ee.files +++ b/projects/linux/ee.files @@ -405,6 +405,7 @@ ../../include/eepp/ui/uithememanager.hpp ../../include/eepp/ui/uitooltip.hpp ../../include/eepp/ui/uitouchdraggablewidget.hpp +../../include/eepp/ui/uitreeview.hpp ../../include/eepp/ui/uiviewpager.hpp ../../include/eepp/ui/uiwidgetcreator.hpp ../../include/eepp/ui/uiwidget.hpp @@ -866,6 +867,7 @@ ../../src/eepp/ui/uithememanager.cpp ../../src/eepp/ui/uitooltip.cpp ../../src/eepp/ui/uitouchdraggablewidget.cpp +../../src/eepp/ui/uitreeview.cpp ../../src/eepp/ui/uiviewpager.cpp ../../src/eepp/ui/uiwidget.cpp ../../src/eepp/ui/uiwidgetcreator.cpp diff --git a/src/eepp/scene/node.cpp b/src/eepp/scene/node.cpp index edd3fbed2..efc645081 100644 --- a/src/eepp/scene/node.cpp +++ b/src/eepp/scene/node.cpp @@ -489,7 +489,7 @@ void Node::drawChilds() { while ( NULL != child ) { if ( child->mVisible ) { - child->internalDraw(); + child->nodeDraw(); } child = child->mPrev; @@ -499,7 +499,7 @@ void Node::drawChilds() { while ( NULL != child ) { if ( child->mVisible ) { - child->internalDraw(); + child->nodeDraw(); } child = child->mNext; @@ -507,7 +507,7 @@ void Node::drawChilds() { } } -void Node::internalDraw() { +void Node::nodeDraw() { if ( mVisible ) { if ( mNodeFlags & NODE_FLAG_POSITION_DIRTY ) updateScreenPos(); @@ -912,6 +912,14 @@ void Node::forEachNode( std::function func ) { } } +void Node::forEachChild( std::function func ) { + Node* node = mChild; + while ( node ) { + func( node ); + node = node->getNextNode(); + } +} + void Node::onSceneChange() { mSceneNode = findSceneNode(); diff --git a/src/eepp/ui/abstract/uiabstracttableview.cpp b/src/eepp/ui/abstract/uiabstracttableview.cpp index f24e008c1..b8d1a6697 100644 --- a/src/eepp/ui/abstract/uiabstracttableview.cpp +++ b/src/eepp/ui/abstract/uiabstracttableview.cpp @@ -52,7 +52,10 @@ class UITableHeaderColumn : public UIPushButton { Vector2f localPos( convertToNodeSpace( position ) ); if ( isDragging() || localPos.x >= mSize.getWidth() - mView->getDragBorderDistance() ) { setPixelsSize( mSize.x - dragDiff.x, mSize.getHeight() ); - mView->columnData( mColIndex ).width = mSize.getWidth(); + if ( mSize.getWidth() != mView->columnData( mColIndex ).width ) { + mView->columnData( mColIndex ).width = mSize.getWidth(); + mView->onColumnSizeChange( mColIndex ); + } return 1; } return 0; @@ -76,6 +79,8 @@ class UITableHeaderColumn : public UIPushButton { Uint32 onDragStop( const Vector2i& pos, const Uint32& flags ) { getUISceneNode()->setCursor( Cursor::Arrow ); + mView->columnData( mColIndex ).width = mSize.getWidth(); + mView->onColumnSizeChange( mColIndex ); return UIPushButton::onDragStop( pos, flags ); } }; @@ -97,13 +102,13 @@ UIAbstractTableView::~UIAbstractTableView() {} void UIAbstractTableView::selectAll() { getSelection().clear(); - for ( size_t itemIndex = 0; itemIndex < itemCount(); ++itemIndex ) { + for ( size_t itemIndex = 0; itemIndex < getItemCount(); ++itemIndex ) { auto index = getModel()->index( itemIndex ); getSelection().add( index ); } } -size_t UIAbstractTableView::itemCount() const { +size_t UIAbstractTableView::getItemCount() const { if ( !getModel() ) return 0; return getModel()->rowCount(); @@ -126,7 +131,6 @@ void UIAbstractTableView::createOrUpdateColumns() { for ( size_t i = 0; i < count; i++ ) { ColumnData& col = columnData( i ); if ( !col.widget ) { - /*UIPushButton::NewWithTag( "table::header::column" )*/; col.widget = eeNew( UITableHeaderColumn, ( this, i ) ); col.widget->setParent( mHeader ); col.widget->setEnabled( true ); @@ -155,13 +159,34 @@ void UIAbstractTableView::createOrUpdateColumns() { } } } + + mHeader->updateLayout(); } Float UIAbstractTableView::getHeaderHeight() const { - return mHeadersVisible ? eeceil( columnData( 0 ).widget - ? columnData( 0 ).widget->getPixelsSize().getHeight() - : 16 ) - : 0; + return areHeadersVisible() ? eeceil( columnData( 0 ).widget + ? columnData( 0 ).widget->getPixelsSize().getHeight() + : 16 ) + : 0; +} + +Sizef UIAbstractTableView::getContentSize() const { + size_t count = getModel()->columnCount(); + Sizef size; + for ( size_t i = 0; i < count; i++ ) + if ( !isColumnHidden( i ) ) + size.x += columnData( i ).width; + size.y = getHeaderHeight(); + size.y = getItemCount() * getRowHeight(); + return size; +} + +bool UIAbstractTableView::areHeadersVisible() const { + return mHeader->isVisible(); +} + +void UIAbstractTableView::setHeadersVisible( bool visible ) { + mHeader->setVisible( visible ); } void UIAbstractTableView::onSizeChange() { @@ -169,6 +194,8 @@ void UIAbstractTableView::onSizeChange() { mHeader->setPixelsSize( mSize.getWidth(), getHeaderHeight() ); } +void UIAbstractTableView::onColumnSizeChange( const size_t& ) {} + const Float& UIAbstractTableView::getDragBorderDistance() const { return mDragBorderDistance; } @@ -177,6 +204,10 @@ void UIAbstractTableView::setDragBorderDistance( const Float& dragBorderDistance mDragBorderDistance = dragBorderDistance; } +Vector2f UIAbstractTableView::getColumnPosition( const size_t& index ) { + return columnData( index ).widget->getPixelsPosition(); +} + UIAbstractTableView::ColumnData& UIAbstractTableView::columnData( const size_t& column ) const { if ( column >= mColumn.size() ) mColumn.resize( column + 1 ); diff --git a/src/eepp/ui/uinode.cpp b/src/eepp/ui/uinode.cpp index d521e7607..681ab0e18 100644 --- a/src/eepp/ui/uinode.cpp +++ b/src/eepp/ui/uinode.cpp @@ -726,7 +726,7 @@ void UINode::drawBorder() { } } -void UINode::internalDraw() { +void UINode::nodeDraw() { if ( mVisible ) { if ( mNodeFlags & NODE_FLAG_POSITION_DIRTY ) updateScreenPos(); diff --git a/src/eepp/ui/uitreeview.cpp b/src/eepp/ui/uitreeview.cpp new file mode 100644 index 000000000..132af743a --- /dev/null +++ b/src/eepp/ui/uitreeview.cpp @@ -0,0 +1,212 @@ +#include +#include +#include + +namespace EE { namespace UI { + +struct UITreeView::MetadataForIndex { + bool open{false}; +}; + +UITreeView* UITreeView::New() { + return eeNew( UITreeView, () ); +} + +UITreeView::UITreeView() : UIAbstractTableView( "treeview" ), mIndentWidth( 16 ) {} + +UITreeView::MetadataForIndex& UITreeView::getIndexMetadata( const ModelIndex& index ) const { + eeASSERT( index.isValid() ); + auto it = mViewMetadata.find( index.data() ); + if ( it != mViewMetadata.end() ) + return *it->second; + auto newMetadata = std::make_unique(); + auto* ref = newMetadata.get(); + mViewMetadata.insert( {index.data(), std::move( newMetadata )} ); + return *ref; +} + +UIPushButton* UITreeView::getIndexWidget( const int& column, void* data ) { + auto it = mWidgets[column].find( data ); + if ( it != mWidgets[column].end() ) + return it->second; + return nullptr; +} + +template void UITreeView::traverseTree( Callback callback ) const { + eeASSERT( getModel() ); + auto& model = *getModel(); + int indentLevel = 1; + Float yOffset = getHeaderHeight(); + std::function traverseIndex = + [&]( const ModelIndex& index ) { + if ( index.isValid() ) { + auto& metadata = getIndexMetadata( index ); + IterationDecision decision = callback( index, indentLevel, yOffset ); + if ( decision == IterationDecision::Break || decision == IterationDecision::Stop ) + return decision; + yOffset += getRowHeight(); + if ( !metadata.open ) { + return IterationDecision::Continue; + } + } + if ( indentLevel > 0 && !index.isValid() ) + return IterationDecision::Continue; + ++indentLevel; + int rowCount = model.rowCount( index ); + for ( int i = 0; i < rowCount; ++i ) { + IterationDecision decision = + traverseIndex( model.index( i, model.treeColumn(), index ) ); + if ( decision == IterationDecision::Break || decision == IterationDecision::Stop ) + return decision; + } + --indentLevel; + return IterationDecision::Continue; + }; + int rootCount = model.rowCount(); + for ( int rootIndex = 0; rootIndex < rootCount; ++rootIndex ) { + IterationDecision decision = + traverseIndex( model.index( rootIndex, model.treeColumn(), ModelIndex() ) ); + if ( decision == IterationDecision::Break || decision == IterationDecision::Stop ) + break; + } +} + +void UITreeView::createOrUpdateColumns() { + UIAbstractTableView::createOrUpdateColumns(); + updateContentSize(); + traverseTree( [&]( const ModelIndex& index, const size_t& indentLevel, const Float& yOffset ) { + for ( size_t col = 0; col < getModel()->columnCount(); col++ ) { + updateCell( index, col, indentLevel, yOffset ); + } + return IterationDecision::Continue; + } ); +} + +size_t UITreeView::getItemCount() const { + size_t count = 0; + traverseTree( [&]( const ModelIndex&, const size_t&, const Float& ) { + count++; + return IterationDecision::Continue; + } ); + return count; +} + +void UITreeView::onColumnSizeChange( const size_t& ) { + updateContentSize(); + traverseTree( [&]( const ModelIndex& index, const size_t& indentLevel, const Float& yOffset ) { + updateRow( getModel()->index( index.row(), getModel()->treeColumn(), index.parent() ), + yOffset ); + for ( size_t colIndex = 0; colIndex < getModel()->columnCount(); colIndex++ ) + updateCell( index, colIndex, indentLevel, yOffset ); + return IterationDecision::Continue; + } ); +} + +UIWidget* UITreeView::updateRow( const ModelIndex& index, const Float& yOffset ) { + UIWidget* rowWidget = nullptr; + auto it = mRows.find( index.data() ); + if ( it == mRows.end() ) { + rowWidget = UIWidget::NewWithTag( "table::row" ); + rowWidget->setParent( this ); + rowWidget->setLayoutSizePolicy( SizePolicy::Fixed, SizePolicy::Fixed ); + rowWidget->reloadStyle( true, true, true ); + mRows.insert( {index.data(), rowWidget} ); + } else { + rowWidget = it->second; + } + rowWidget->setPixelsSize( getContentSize().getWidth(), getRowHeight() ); + rowWidget->setPixelsPosition( {0, yOffset} ); + return rowWidget; +} + +void UITreeView::updateContentSize() { + mContentSize = UIAbstractTableView::getContentSize(); +} + +UIPushButton* UITreeView::updateCell( const ModelIndex& index, const size_t& col, + const size_t& indentLevel, const Float& yOffset ) { + UIPushButton* widget = getIndexWidget( col, index.data() ); + if ( !widget ) { + UIWidget* rowWidget = updateRow( + getModel()->index( index.row(), getModel()->treeColumn(), index.parent() ), yOffset ); + widget = UIPushButton::NewWithTag( "table::cell" ); + widget->setParent( rowWidget ); + widget->unsetFlags( UI_AUTO_SIZE ); + widget->clipEnable(); + widget->setLayoutSizePolicy( SizePolicy::Fixed, SizePolicy::Fixed ); + widget->setTextAlign( UI_HALIGN_LEFT ); + if ( col == getModel()->treeColumn() ) { + widget->addEventListener( Event::MouseDoubleClick, [&, index]( const Event* event ) { + auto mouseEvent = static_cast( event ); + if ( mouseEvent->getFlags() & EE_BUTTON_LMASK ) { + auto& data = getIndexMetadata( index ); + data.open = !data.open; + createOrUpdateColumns(); + } + } ); + } + mWidgets[col].insert( {index.data(), widget} ); + } + widget->setPixelsSize( columnData( col ).width, getRowHeight() ); + widget->setPixelsPosition( {getColumnPosition( col ).x, 0} ); + if ( col == getModel()->treeColumn() ) + widget->setPaddingLeft( getIndentWidth() * indentLevel ); + + Variant variant( getModel()->data( getModel()->index( index.row(), col, index.parent() ), + Model::Role::Display ) ); + if ( variant.isValid() ) + widget->setText( variant.asString() ); + + return widget; +} + +const Float& UITreeView::getIndentWidth() const { + return mIndentWidth; +} + +void UITreeView::setIndentWidth( const Float& indentWidth ) { + mIndentWidth = indentWidth; +} + +Sizef UITreeView::getContentSize() const { + return mContentSize; +} + +void UITreeView::drawChilds() { + if ( mHeader && mHeader->isVisible() ) + mHeader->nodeDraw(); + traverseTree( [&]( const ModelIndex& index, const size_t&, const Float& yOffset ) { + updateRow( getModel()->index( index.row(), getModel()->treeColumn(), index.parent() ), + yOffset ) + ->nodeDraw(); + return IterationDecision::Continue; + } ); +} + +Node* UITreeView::overFind( const Vector2f& point ) { + Node* pOver = NULL; + if ( mEnabled && mVisible ) { + updateWorldPolygon(); + if ( mWorldBounds.contains( point ) && mPoly.pointInside( point ) ) { + writeNodeFlag( NODE_FLAG_MOUSEOVER_ME_OR_CHILD, 1 ); + mSceneNode->addMouseOverNode( this ); + if ( mHeader && ( pOver = mHeader->overFind( point ) ) ) + return pOver; + traverseTree( + [&, point]( const ModelIndex& index, const size_t&, const Float& yOffset ) { + pOver = updateRow( getModel()->index( index.row(), getModel()->treeColumn(), + index.parent() ), + yOffset ) + ->overFind( point ); + if ( pOver ) + return IterationDecision::Stop; + return IterationDecision::Continue; + } ); + if ( NULL == pOver ) + pOver = this; + } + } + return pOver; +} + +}} // namespace EE::UI diff --git a/src/eepp/ui/uiwindow.cpp b/src/eepp/ui/uiwindow.cpp index 7df9759fd..ea86cee55 100644 --- a/src/eepp/ui/uiwindow.cpp +++ b/src/eepp/ui/uiwindow.cpp @@ -1212,7 +1212,7 @@ Uint32 UIWindow::onMouseDoubleClick( const Vector2i&, const Uint32& Flags ) { return 1; } -void UIWindow::internalDraw() { +void UIWindow::nodeDraw() { if ( mVisible && 0 != mAlpha ) { updateScreenPos(); diff --git a/src/tests/ui_perf_test/ui_perf_test.cpp b/src/tests/ui_perf_test/ui_perf_test.cpp index 16ffb03e9..4b484bb24 100644 --- a/src/tests/ui_perf_test/ui_perf_test.cpp +++ b/src/tests/ui_perf_test/ui_perf_test.cpp @@ -1,20 +1,88 @@ #include #include #include +#include using namespace EE::UI::Abstract; class TestModel : public Model { public: - virtual int rowCount( const ModelIndex& = ModelIndex() ) const { return 1; } + struct NodeT { + std::vector children; + NodeT* parent{nullptr}; - virtual int columnCount( const ModelIndex& = ModelIndex() ) const { return 4; } + ModelIndex index( const TestModel& model, int column ) const { + if ( !parent ) + return {}; + for ( size_t row = 0; row < parent->children.size(); ++row ) { + if ( parent->children[row] == this ) + return model.createIndex( row, column, const_cast( this ) ); + } + return {}; + } + }; + + TestModel() : Model() { + for ( size_t row = 0; row < 4; ++row ) { + NodeT* n = new NodeT(); + n->parent = &mRoot; + for ( size_t i = 0; i < 4; i++ ) { + NodeT* c = new NodeT(); + c->parent = n; + n->children.push_back( c ); + } + mRoot.children.push_back( n ); + } + } + + virtual ModelIndex parentIndex( const ModelIndex& index ) const { + if ( !index.isValid() ) + return {}; + auto node = this->node( index ); + if ( !node.parent ) { + eeASSERT( &node == &mRoot ); + return {}; + } + return node.parent->index( *this, index.column() ); + } + + virtual size_t rowCount( const ModelIndex& index = ModelIndex() ) const { + auto node = this->node( index ); + return node.children.size(); + } + + virtual size_t columnCount( const ModelIndex& index = ModelIndex() ) const { return 4; } + + NodeT mRoot; + const NodeT& node( const ModelIndex& index ) const { + if ( !index.isValid() ) + return mRoot; + return *(NodeT*)index.data(); + } + + ModelIndex index( int row, int column, const ModelIndex& parent ) const { + if ( row < 0 || column < 0 ) + return {}; + auto& node = this->node( parent ); + if ( static_cast( row ) >= node.children.size() ) + return {}; + return createIndex( row, column, node.children[row] ); + } virtual std::string columnName( const size_t& column ) const { return String::format( "Column %ld", column ); } - virtual Variant data( const ModelIndex&, Role = Role::Display ) const { return Variant(); }; + virtual Variant data( const ModelIndex& index, Role role = Role::Display ) const { + switch ( role ) { + case Role::Display: { + return Variant( String::format( "Test %lld-%lld", index.row(), index.column() ) ); + } + default: { + } + } + return Variant(); + }; virtual void update() {} }; @@ -99,8 +167,8 @@ EE_MAIN_FUNC int main( int argc, char* argv[] ) { vlay->setLayoutSizePolicy( SizePolicy::MatchParent, SizePolicy::MatchParent ); auto model = std::make_shared(); - UIAbstractTableView* view = UIAbstractTableView::New(); - view->setId( "abstracttable" ); + UITreeView* view = UITreeView::New(); + view->setId( "treeview" ); view->setLayoutSizePolicy( SizePolicy::MatchParent, SizePolicy::MatchParent ); view->setParent( vlay ); view->setModel( model ); From 2bd66a2136083affff4be78be534884a3c2e38cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Wed, 8 Jul 2020 02:40:54 -0300 Subject: [PATCH 4/7] Improvements to the UITreeView. --- include/eepp/ui/abstract/model.hpp | 2 +- include/eepp/ui/abstract/modelindex.hpp | 4 ++ include/eepp/ui/abstract/uiabstractview.hpp | 6 +++ include/eepp/ui/uitreeview.hpp | 12 ++++++ src/eepp/ui/uiimage.cpp | 3 ++ src/eepp/ui/uipushbutton.cpp | 6 ++- src/eepp/ui/uitreeview.cpp | 47 ++++++++++++++++++--- src/tests/ui_perf_test/ui_perf_test.cpp | 27 +++++++++++- 8 files changed, 98 insertions(+), 9 deletions(-) diff --git a/include/eepp/ui/abstract/model.hpp b/include/eepp/ui/abstract/model.hpp index dee50f412..48c9239fd 100644 --- a/include/eepp/ui/abstract/model.hpp +++ b/include/eepp/ui/abstract/model.hpp @@ -51,7 +51,7 @@ class Variant { Variant( const Int64& val ) : mType( Type::Int64 ) { mValue.asInt64 = val; } ~Variant() { reset(); } const std::string& asString() const { return *mValue.asString; } - const Drawable* asDrawable() const { return mValue.asDrawable; } + Drawable* asDrawable() const { return mValue.asDrawable; } const bool& asBool() const { return mValue.asBool; } const Float& asFloat() const { return mValue.asFloat; } const int& asInt() const { return mValue.asInt; } diff --git a/include/eepp/ui/abstract/modelindex.hpp b/include/eepp/ui/abstract/modelindex.hpp index 6c40fb692..36670bb86 100644 --- a/include/eepp/ui/abstract/modelindex.hpp +++ b/include/eepp/ui/abstract/modelindex.hpp @@ -14,9 +14,13 @@ class EE_API ModelIndex { bool isValid() const { return mRow != -1 && mColumn != -1; } const Int64& row() const { return mRow; } + const Int64& column() const { return mColumn; } + void* data() const { return mData; } + ModelIndex parent() const; + bool hasParent() const { return parent().isValid(); } bool operator<( const ModelIndex& other ) const { diff --git a/include/eepp/ui/abstract/uiabstractview.hpp b/include/eepp/ui/abstract/uiabstractview.hpp index 4736e8ff2..5cd0121c1 100644 --- a/include/eepp/ui/abstract/uiabstractview.hpp +++ b/include/eepp/ui/abstract/uiabstractview.hpp @@ -12,17 +12,23 @@ namespace EE { namespace UI { namespace Abstract { class EE_API UIAbstractView : public UIWidget { public: void setModel( std::shared_ptr ); + Model* getModel() { return mModel.get(); } + const Model* getModel() const { return mModel.get(); } ModelSelection& getSelection() { return mSelection; } + const ModelSelection& getSelection() const { return mSelection; } + virtual void selectAll() = 0; bool isEditable() const { return mEditable; } + void setEditable( bool editable ) { mEditable = editable; } void setActivatesOnSelection( bool b ) { mActivatesOnSelection = b; } + bool getActivatesOnSelection() const { return mActivatesOnSelection; } void notifySelectionChange(); diff --git a/include/eepp/ui/uitreeview.hpp b/include/eepp/ui/uitreeview.hpp index 3f69aec52..e18f6968c 100644 --- a/include/eepp/ui/uitreeview.hpp +++ b/include/eepp/ui/uitreeview.hpp @@ -23,6 +23,16 @@ class EE_API UITreeView : public UIAbstractTableView { virtual Node* overFind( const Vector2f& point ); + bool isExpanded( const ModelIndex& index ) const; + + Drawable* getExpandIcon() const; + + void setExpandedIcon( Drawable* expandIcon ); + + Drawable* getContractIcon() const; + + void setContractedIcon( Drawable* contractIcon ); + protected: enum class IterationDecision { Continue, @@ -32,6 +42,8 @@ class EE_API UITreeView : public UIAbstractTableView { Float mIndentWidth; Sizef mContentSize; + Drawable* mExpandIcon{nullptr}; + Drawable* mContractIcon{nullptr}; UITreeView(); diff --git a/src/eepp/ui/uiimage.cpp b/src/eepp/ui/uiimage.cpp index b1d5356f7..f6d2c2286 100644 --- a/src/eepp/ui/uiimage.cpp +++ b/src/eepp/ui/uiimage.cpp @@ -45,6 +45,9 @@ bool UIImage::isType( const Uint32& type ) const { } UIImage* UIImage::setDrawable( Drawable* drawable, bool ownIt ) { + if ( drawable == mDrawable ) + return this; + safeDeleteDrawable(); mDrawable = drawable; diff --git a/src/eepp/ui/uipushbutton.cpp b/src/eepp/ui/uipushbutton.cpp index a42c7f807..63cc81ba7 100644 --- a/src/eepp/ui/uipushbutton.cpp +++ b/src/eepp/ui/uipushbutton.cpp @@ -243,8 +243,10 @@ void UIPushButton::onThemeLoaded() { } UIPushButton* UIPushButton::setIcon( Drawable* Icon ) { - mIcon->setDrawable( Icon ); - onSizeChange(); + if ( mIcon->getDrawable() != Icon ) { + mIcon->setDrawable( Icon ); + onSizeChange(); + } return this; } diff --git a/src/eepp/ui/uitreeview.cpp b/src/eepp/ui/uitreeview.cpp index 132af743a..d08063eb8 100644 --- a/src/eepp/ui/uitreeview.cpp +++ b/src/eepp/ui/uitreeview.cpp @@ -1,5 +1,6 @@ #include #include +#include #include namespace EE { namespace UI { @@ -12,7 +13,10 @@ UITreeView* UITreeView::New() { return eeNew( UITreeView, () ); } -UITreeView::UITreeView() : UIAbstractTableView( "treeview" ), mIndentWidth( 16 ) {} +UITreeView::UITreeView() : UIAbstractTableView( "treeview" ), mIndentWidth( 16 ) { + mExpandIcon = getUISceneNode()->findIcon( "tree-expanded" ); + mContractIcon = getUISceneNode()->findIcon( "tree-contracted" ); +} UITreeView::MetadataForIndex& UITreeView::getIndexMetadata( const ModelIndex& index ) const { eeASSERT( index.isValid() ); @@ -72,6 +76,8 @@ template void UITreeView::traverseTree( Callback callback ) } void UITreeView::createOrUpdateColumns() { + if ( !getModel() ) + return; UIAbstractTableView::createOrUpdateColumns(); updateContentSize(); traverseTree( [&]( const ModelIndex& index, const size_t& indentLevel, const Float& yOffset ) { @@ -152,11 +158,13 @@ UIPushButton* UITreeView::updateCell( const ModelIndex& index, const size_t& col if ( col == getModel()->treeColumn() ) widget->setPaddingLeft( getIndentWidth() * indentLevel ); - Variant variant( getModel()->data( getModel()->index( index.row(), col, index.parent() ), - Model::Role::Display ) ); + ModelIndex idx( getModel()->index( index.row(), col, index.parent() ) ); + + Variant variant( getModel()->data( idx, Model::Role::Display ) ); if ( variant.isValid() ) widget->setText( variant.asString() ); - + if ( col == getModel()->treeColumn() && getModel()->rowCount( index ) > 0 ) + widget->setIcon( getIndexMetadata( index ).open ? mExpandIcon : mContractIcon ); return widget; } @@ -165,7 +173,10 @@ const Float& UITreeView::getIndentWidth() const { } void UITreeView::setIndentWidth( const Float& indentWidth ) { - mIndentWidth = indentWidth; + if ( mIndentWidth != indentWidth ) { + mIndentWidth = indentWidth; + createOrUpdateColumns(); + } } Sizef UITreeView::getContentSize() const { @@ -209,4 +220,30 @@ Node* UITreeView::overFind( const Vector2f& point ) { return pOver; } +bool UITreeView::isExpanded( const ModelIndex& index ) const { + return getIndexMetadata( index ).open; +} + +Drawable* UITreeView::getExpandIcon() const { + return mExpandIcon; +} + +void UITreeView::setExpandedIcon( Drawable* expandIcon ) { + if ( mExpandIcon != expandIcon ) { + mExpandIcon = expandIcon; + createOrUpdateColumns(); + } +} + +Drawable* UITreeView::getContractIcon() const { + return mContractIcon; +} + +void UITreeView::setContractedIcon( Drawable* contractIcon ) { + if ( mContractIcon != contractIcon ) { + mContractIcon = contractIcon; + createOrUpdateColumns(); + } +} + }} // namespace EE::UI diff --git a/src/tests/ui_perf_test/ui_perf_test.cpp b/src/tests/ui_perf_test/ui_perf_test.cpp index 4b484bb24..b71f41dd3 100644 --- a/src/tests/ui_perf_test/ui_perf_test.cpp +++ b/src/tests/ui_perf_test/ui_perf_test.cpp @@ -23,7 +23,7 @@ class TestModel : public Model { }; TestModel() : Model() { - for ( size_t row = 0; row < 4; ++row ) { + for ( size_t row = 0; row < 100; ++row ) { NodeT* n = new NodeT(); n->parent = &mRoot; for ( size_t i = 0; i < 4; i++ ) { @@ -78,6 +78,16 @@ class TestModel : public Model { case Role::Display: { return Variant( String::format( "Test %lld-%lld", index.row(), index.column() ) ); } + case Role::Icon: { + if ( index.column() == 0 ) { + return Variant( + SceneManager::instance() + ->getUISceneNode() + ->getUIIconThemeManager() + ->getCurrentTheme() + ->getIcon( node( index ).children.size() ? "folder-open" : "folder" ) ); + } + } default: { } } @@ -141,9 +151,22 @@ EE_MAIN_FUNC int main( int argc, char* argv[] ) { Engine::instance()->getDisplayManager()->getDisplayIndex( 0 )->getPixelDensity() ); FontTrueType* font = FontTrueType::New( "NotoSans-Regular", "assets/fonts/NotoSans-Regular.ttf" ); + FontTrueType* iconFont = FontTrueType::New( "icon", "assets/fonts/remixicon.ttf" ); + UIIconTheme* iconTheme = UIIconTheme::New( "remixicon" ); + auto addIcon = [iconTheme, iconFont]( const std::string& name, const Uint32& codePoint, + const Uint32& size ) -> Drawable* { + Drawable* ic = iconFont->getGlyphDrawable( codePoint, size ); + iconTheme->add( name, ic ); + return ic; + }; + Drawable* closed = addIcon( "folder", 0xed6a, 16 ); + Drawable* open = addIcon( "folder-open", 0xed70, 16 ); + addIcon( "tree-expanded", 0xea50, 24 ); + addIcon( "tree-contracted", 0xea54, 24 ); UISceneNode* uiSceneNode = UISceneNode::New(); SceneManager::instance()->add( uiSceneNode ); uiSceneNode->getUIThemeManager()->setDefaultFont( font ); + uiSceneNode->getUIIconThemeManager()->setCurrentTheme( iconTheme ); /*StyleSheetParser styleSheetParser; styleSheetParser.loadFromFile( "assets/ui/breeze.css" ); uiSceneNode->setStyleSheet( styleSheetParser.getStyleSheet() );*/ @@ -169,6 +192,8 @@ EE_MAIN_FUNC int main( int argc, char* argv[] ) { auto model = std::make_shared(); UITreeView* view = UITreeView::New(); view->setId( "treeview" ); + view->setExpandedIcon( open ); + view->setContractedIcon( closed ); view->setLayoutSizePolicy( SizePolicy::MatchParent, SizePolicy::MatchParent ); view->setParent( vlay ); view->setModel( model ); From 8a6dad67d9cc3b56322b751d10dd002a713db82a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Thu, 9 Jul 2020 03:26:05 -0300 Subject: [PATCH 5/7] Added UIScrollableWidget. More improvements to the UITreeView. --- include/eepp/ui/abstract/model.hpp | 9 +- .../eepp/ui/abstract/uiabstracttableview.hpp | 6 + include/eepp/ui/abstract/uiabstractview.hpp | 8 +- include/eepp/ui/uihelper.hpp | 4 + include/eepp/ui/uiscrollablewidget.hpp | 72 +++++ include/eepp/ui/uitreeview.hpp | 12 +- projects/linux/ee.files | 2 + src/eepp/ui/abstract/uiabstracttableview.cpp | 30 +- src/eepp/ui/abstract/uiabstractview.cpp | 11 +- src/eepp/ui/uiscrollablewidget.cpp | 287 ++++++++++++++++++ src/eepp/ui/uitreeview.cpp | 45 ++- src/eepp/ui/uiwidgetcreator.cpp | 2 + 12 files changed, 468 insertions(+), 20 deletions(-) create mode 100644 include/eepp/ui/uiscrollablewidget.hpp create mode 100644 src/eepp/ui/uiscrollablewidget.cpp diff --git a/include/eepp/ui/abstract/model.hpp b/include/eepp/ui/abstract/model.hpp index 48c9239fd..65ae66ce5 100644 --- a/include/eepp/ui/abstract/model.hpp +++ b/include/eepp/ui/abstract/model.hpp @@ -30,10 +30,11 @@ class Variant { Int64, Drawable, Vector2f, - Rectf + Rectf, + cstr }; Variant() : mType( Type::Invalid ) {} - Variant( const std::string& string ) : mType( Type::String ) { + explicit Variant( const std::string& string ) : mType( Type::String ) { mValue.asString = eeNew( std::string, ( string ) ); } Variant( Drawable* drawable, bool ownDrawable = false ) : mType( Type::Drawable ) { @@ -49,6 +50,7 @@ class Variant { Variant( const Float& val ) : mType( Type::Float ) { mValue.asFloat = val; } Variant( const int& val ) : mType( Type::Int ) { mValue.asInt = val; } Variant( const Int64& val ) : mType( Type::Int64 ) { mValue.asInt64 = val; } + explicit Variant( const char* data ) : mType( Type::cstr ) { mValue.asCStr = data; } ~Variant() { reset(); } const std::string& asString() const { return *mValue.asString; } Drawable* asDrawable() const { return mValue.asDrawable; } @@ -58,6 +60,8 @@ class Variant { const Int64& asInt64() const { return mValue.asInt64; } const Vector2f& asVector2f() const { return *mValue.asVector2f; } const Rectf& asRectf() const { return *mValue.asRectf; } + const char* asCStr() const { return mValue.asCStr; } + bool is( const Type& type ) const { return type == mType; } void reset() { switch ( mType ) { case Type::String: @@ -91,6 +95,7 @@ class Variant { Int64 asInt64; Vector2f* asVector2f; Rectf* asRectf; + const char* asCStr; } mValue; Type mType; bool mOwnsObject{false}; diff --git a/include/eepp/ui/abstract/uiabstracttableview.hpp b/include/eepp/ui/abstract/uiabstracttableview.hpp index ca128a6ac..66ceb69a5 100644 --- a/include/eepp/ui/abstract/uiabstracttableview.hpp +++ b/include/eepp/ui/abstract/uiabstracttableview.hpp @@ -17,6 +17,10 @@ class EE_API UIAbstractTableView : public UIAbstractView { public: static UIAbstractTableView* New(); + Uint32 getType() const; + + bool isType( const Uint32& type ) const; + virtual Float getRowHeight() const { return getHeaderHeight(); } virtual Float getHeaderHeight() const; @@ -66,6 +70,8 @@ class EE_API UIAbstractTableView : public UIAbstractView { virtual void onColumnSizeChange( const size_t& colIndex ); + void updateHeaderSize(); + UILinearLayout* mHeader; Float mDragBorderDistance{8}; }; diff --git a/include/eepp/ui/abstract/uiabstractview.hpp b/include/eepp/ui/abstract/uiabstractview.hpp index 5cd0121c1..02215ab9a 100644 --- a/include/eepp/ui/abstract/uiabstractview.hpp +++ b/include/eepp/ui/abstract/uiabstractview.hpp @@ -4,13 +4,17 @@ #include #include #include -#include +#include #include namespace EE { namespace UI { namespace Abstract { -class EE_API UIAbstractView : public UIWidget { +class EE_API UIAbstractView : public UIScrollableWidget { public: + Uint32 getType() const; + + bool isType( const Uint32& type ) const; + void setModel( std::shared_ptr ); Model* getModel() { return mModel.get(); } diff --git a/include/eepp/ui/uihelper.hpp b/include/eepp/ui/uihelper.hpp index eb2d77298..323602073 100644 --- a/include/eepp/ui/uihelper.hpp +++ b/include/eepp/ui/uihelper.hpp @@ -89,6 +89,10 @@ enum UINodeType { UI_TYPE_ITEMCONTAINER, UI_TYPE_CODEEDITOR, UI_TYPE_SPLITTER, + UI_TYPE_ABSTRACTVIEW, + UI_TYPE_ABSTRACTTABLEVIEW, + UI_TYPE_TREEVIEW, + UI_TYPE_SCROLLABLEWIDGET, UI_TYPE_USER = 10000 }; diff --git a/include/eepp/ui/uiscrollablewidget.hpp b/include/eepp/ui/uiscrollablewidget.hpp new file mode 100644 index 000000000..02961f639 --- /dev/null +++ b/include/eepp/ui/uiscrollablewidget.hpp @@ -0,0 +1,72 @@ +#ifndef EE_UI_UISCROLLABLEWIDGET_HPP +#define EE_UI_UISCROLLABLEWIDGET_HPP + +#include + +namespace EE { namespace UI { + +class UIScrollBar; + +class EE_API UIScrollableWidget : public UIWidget { + public: + enum ScrollViewType { Inclusive, Exclusive }; + + virtual Uint32 getType() const; + + virtual bool isType( const Uint32& type ) const; + + void setVerticalScrollMode( const ScrollBarMode& Mode ); + + const ScrollBarMode& getVerticalScrollMode(); + + void setHorizontalScrollMode( const ScrollBarMode& Mode ); + + const ScrollBarMode& getHorizontalScrollMode(); + + const ScrollViewType& getViewType() const; + + void setViewType( const ScrollViewType& viewType ); + + UIScrollBar* getVerticalScrollBar() const; + + UIScrollBar* getHorizontalScrollBar() const; + + virtual bool applyProperty( const StyleSheetProperty& attribute ); + + virtual std::string getPropertyString( const PropertyDefinition* propertyDef, + const Uint32& propertyIndex = 0 ); + + virtual Sizef getContentSize() const = 0; + + protected: + ScrollViewType mViewType; + ScrollBarMode mVScrollMode; + ScrollBarMode mHScrollMode; + UIScrollBar* mVScroll; + UIScrollBar* mHScroll; + Uint32 mSizeChangeCb; + Uint32 mPosChangeCb; + Vector2f mScrollOffset; + + UIScrollableWidget( const std::string& tag ); + + virtual Uint32 onMessage( const NodeMessage* Msg ); + + virtual void onSizeChange(); + + virtual void onAlphaChange(); + + virtual void onPaddingChange(); + + void onValueChangeCb( const Event* Event ); + + virtual void onContentSizeChange(); + + virtual void updateScroll(); + + virtual void onScrollChange(); +}; + +}} // namespace EE::UI + +#endif // EE_UI_UISCROLLABLEWIDGET_HPP diff --git a/include/eepp/ui/uitreeview.hpp b/include/eepp/ui/uitreeview.hpp index e18f6968c..4e8305cb8 100644 --- a/include/eepp/ui/uitreeview.hpp +++ b/include/eepp/ui/uitreeview.hpp @@ -13,6 +13,10 @@ class EE_API UITreeView : public UIAbstractTableView { public: static UITreeView* New(); + Uint32 getType() const; + + bool isType( const Uint32& type ) const; + const Float& getIndentWidth() const; void setIndentWidth( const Float& indentWidth ); @@ -65,10 +69,12 @@ class EE_API UITreeView : public UIAbstractTableView { virtual void onColumnSizeChange( const size_t& colIndex ); - UIPushButton* updateCell( const ModelIndex& index, const size_t& col, const size_t& indentLevel, - const Float& yOffset ); + virtual UIPushButton* updateCell( const ModelIndex& index, const size_t& col, + const size_t& indentLevel, const Float& yOffset ); - UIWidget* updateRow( const ModelIndex& index, const Float& yOffset ); + virtual UIWidget* updateRow( const ModelIndex& index, const Float& yOffset ); + + virtual void onScrollChange(); void updateContentSize(); }; diff --git a/projects/linux/ee.files b/projects/linux/ee.files index 40a6b5447..0f5aa06ab 100644 --- a/projects/linux/ee.files +++ b/projects/linux/ee.files @@ -381,6 +381,7 @@ ../../include/eepp/ui/uiradiobutton.hpp ../../include/eepp/ui/uirelativelayout.hpp ../../include/eepp/ui/uiscenenode.hpp +../../include/eepp/ui/uiscrollablewidget.hpp ../../include/eepp/ui/uiscrollbar.hpp ../../include/eepp/ui/uiscrollview.hpp ../../include/eepp/ui/uiselectbutton.hpp @@ -843,6 +844,7 @@ ../../src/eepp/ui/uiradiobutton.cpp ../../src/eepp/ui/uirelativelayout.cpp ../../src/eepp/ui/uiscenenode.cpp +../../src/eepp/ui/uiscrollablewidget.cpp ../../src/eepp/ui/uiscrollbar.cpp ../../src/eepp/ui/uiscrollview.cpp ../../src/eepp/ui/uiselectbutton.cpp diff --git a/src/eepp/ui/abstract/uiabstracttableview.cpp b/src/eepp/ui/abstract/uiabstracttableview.cpp index b8d1a6697..76517bb26 100644 --- a/src/eepp/ui/abstract/uiabstracttableview.cpp +++ b/src/eepp/ui/abstract/uiabstracttableview.cpp @@ -54,6 +54,7 @@ class UITableHeaderColumn : public UIPushButton { setPixelsSize( mSize.x - dragDiff.x, mSize.getHeight() ); if ( mSize.getWidth() != mView->columnData( mColIndex ).width ) { mView->columnData( mColIndex ).width = mSize.getWidth(); + mView->updateHeaderSize(); mView->onColumnSizeChange( mColIndex ); } return 1; @@ -80,7 +81,9 @@ class UITableHeaderColumn : public UIPushButton { Uint32 onDragStop( const Vector2i& pos, const Uint32& flags ) { getUISceneNode()->setCursor( Cursor::Arrow ); mView->columnData( mColIndex ).width = mSize.getWidth(); + mView->updateHeaderSize(); mView->onColumnSizeChange( mColIndex ); + return UIPushButton::onDragStop( pos, flags ); } }; @@ -100,6 +103,14 @@ UIAbstractTableView::UIAbstractTableView( const std::string& tag ) : UIAbstractTableView::~UIAbstractTableView() {} +Uint32 UIAbstractTableView::getType() const { + return UI_TYPE_ABSTRACTTABLEVIEW; +} + +bool UIAbstractTableView::isType( const Uint32& type ) const { + return UIAbstractTableView::getType() == type ? true : UIAbstractView::isType( type ); +} + void UIAbstractTableView::selectAll() { getSelection().clear(); for ( size_t itemIndex = 0; itemIndex < getItemCount(); ++itemIndex ) { @@ -124,9 +135,8 @@ void UIAbstractTableView::createOrUpdateColumns() { if ( !model ) return; - mHeader->setPixelsSize( mSize.getWidth(), getHeaderHeight() ); - size_t count = model->columnCount(); + Float totalWidth = 0; for ( size_t i = 0; i < count; i++ ) { ColumnData& col = columnData( i ); @@ -146,6 +156,7 @@ void UIAbstractTableView::createOrUpdateColumns() { col.width = eeceil( eemax( col.width, col.widget->getPixelsSize().getWidth() ) ); col.widget->setLayoutSizePolicy( SizePolicy::Fixed, SizePolicy::Fixed ); col.widget->setPixelsSize( col.width, getHeaderHeight() ); + totalWidth += col.width; } if ( count < mColumn.size() ) { @@ -160,6 +171,7 @@ void UIAbstractTableView::createOrUpdateColumns() { } } + mHeader->setPixelsSize( totalWidth, getHeaderHeight() ); mHeader->updateLayout(); } @@ -177,7 +189,7 @@ Sizef UIAbstractTableView::getContentSize() const { if ( !isColumnHidden( i ) ) size.x += columnData( i ).width; size.y = getHeaderHeight(); - size.y = getItemCount() * getRowHeight(); + size.y += getItemCount() * getRowHeight(); return size; } @@ -190,12 +202,22 @@ void UIAbstractTableView::setHeadersVisible( bool visible ) { } void UIAbstractTableView::onSizeChange() { - UIWidget::onSizeChange(); + UIAbstractView::onSizeChange(); mHeader->setPixelsSize( mSize.getWidth(), getHeaderHeight() ); } void UIAbstractTableView::onColumnSizeChange( const size_t& ) {} +void UIAbstractTableView::updateHeaderSize() { + size_t count = getModel()->columnCount(); + Float totalWidth = 0; + for ( size_t i = 0; i < count; i++ ) { + ColumnData& col = columnData( i ); + totalWidth += col.width; + } + mHeader->setPixelsSize( totalWidth, getHeaderHeight() ); +} + const Float& UIAbstractTableView::getDragBorderDistance() const { return mDragBorderDistance; } diff --git a/src/eepp/ui/abstract/uiabstractview.cpp b/src/eepp/ui/abstract/uiabstractview.cpp index 3bc527974..49067fe7b 100644 --- a/src/eepp/ui/abstract/uiabstractview.cpp +++ b/src/eepp/ui/abstract/uiabstractview.cpp @@ -2,10 +2,19 @@ namespace EE { namespace UI { namespace Abstract { -UIAbstractView::UIAbstractView( const std::string& tag ) : UIWidget( tag ), mSelection( this ) {} +UIAbstractView::UIAbstractView( const std::string& tag ) : + UIScrollableWidget( tag ), mSelection( this ) {} UIAbstractView::~UIAbstractView() {} +Uint32 UIAbstractView::getType() const { + return UI_TYPE_ABSTRACTVIEW; +} + +bool UIAbstractView::isType( const Uint32& type ) const { + return UIAbstractView::getType() == type ? true : UIScrollableWidget::isType( type ); +} + void UIAbstractView::setModel( std::shared_ptr model ) { if ( model.get() == mModel.get() ) return; diff --git a/src/eepp/ui/uiscrollablewidget.cpp b/src/eepp/ui/uiscrollablewidget.cpp new file mode 100644 index 000000000..134cf750d --- /dev/null +++ b/src/eepp/ui/uiscrollablewidget.cpp @@ -0,0 +1,287 @@ +#include +#include +#include + +namespace EE { namespace UI { + +UIScrollableWidget::UIScrollableWidget( const std::string& tag ) : + UIWidget( tag ), + mViewType( Exclusive ), + mVScrollMode( ScrollBarMode::Auto ), + mHScrollMode( ScrollBarMode::Auto ), + mVScroll( UIScrollBar::NewVertical() ), + mHScroll( UIScrollBar::NewHorizontal() ), + mSizeChangeCb( 0 ), + mPosChangeCb( 0 ) { + mFlags |= UI_OWNS_CHILDS_POSITION; + + mVScroll->setParent( this ); + mHScroll->setParent( this ); + + mVScroll->addEventListener( Event::OnValueChange, + cb::Make1( this, &UIScrollableWidget::onValueChangeCb ) ); + mHScroll->addEventListener( Event::OnValueChange, + cb::Make1( this, &UIScrollableWidget::onValueChangeCb ) ); + + applyDefaultTheme(); +} + +Uint32 UIScrollableWidget::getType() const { + return UI_TYPE_SCROLLABLEWIDGET; +} + +bool UIScrollableWidget::isType( const Uint32& type ) const { + return UIWidget::getType() == type ? true : UIWidget::isType( type ); +} + +void UIScrollableWidget::onSizeChange() { + onContentSizeChange(); + UIWidget::onSizeChange(); +} + +void UIScrollableWidget::onAlphaChange() { + UIWidget::onAlphaChange(); + mVScroll->setAlpha( mAlpha ); + mHScroll->setAlpha( mAlpha ); +} + +void UIScrollableWidget::onPaddingChange() { + onContentSizeChange(); + UIWidget::onPaddingChange(); +} + +void UIScrollableWidget::setVerticalScrollMode( const ScrollBarMode& Mode ) { + if ( Mode != mVScrollMode ) { + mVScrollMode = Mode; + onContentSizeChange(); + } +} + +const ScrollBarMode& UIScrollableWidget::getVerticalScrollMode() { + return mVScrollMode; +} + +void UIScrollableWidget::setHorizontalScrollMode( const ScrollBarMode& Mode ) { + if ( Mode != mHScrollMode ) { + mHScrollMode = Mode; + onContentSizeChange(); + } +} + +const ScrollBarMode& UIScrollableWidget::getHorizontalScrollMode() { + return mHScrollMode; +} + +const UIScrollableWidget::ScrollViewType& UIScrollableWidget::getViewType() const { + return mViewType; +} + +void UIScrollableWidget::setViewType( const ScrollViewType& viewType ) { + if ( viewType != mViewType ) { + mViewType = viewType; + onContentSizeChange(); + } +} + +UIScrollBar* UIScrollableWidget::getVerticalScrollBar() const { + return mVScroll; +} + +UIScrollBar* UIScrollableWidget::getHorizontalScrollBar() const { + return mHScroll; +} + +void UIScrollableWidget::onContentSizeChange() { + Sizef contentSize( getContentSize() ); + + if ( ScrollBarMode::AlwaysOn == mHScrollMode ) { + mHScroll->setVisible( true ); + mHScroll->setEnabled( true ); + } else if ( ScrollBarMode::AlwaysOff == mHScrollMode ) { + mHScroll->setVisible( false ); + mHScroll->setEnabled( false ); + } else { + bool visible = contentSize.getWidth() > + getPixelsSize().getWidth() - getPixelsPadding().Left - + getPixelsPadding().Right - mVScroll->getPixelsSize().getWidth(); + + mHScroll->setVisible( visible ); + mHScroll->setEnabled( visible ); + } + + if ( ScrollBarMode::AlwaysOn == mVScrollMode ) { + mVScroll->setVisible( true ); + mVScroll->setEnabled( true ); + } else if ( ScrollBarMode::AlwaysOff == mVScrollMode ) { + mVScroll->setVisible( false ); + mVScroll->setEnabled( false ); + } else { + bool visible = contentSize.getHeight() > + getPixelsSize().getHeight() - getPixelsPadding().Top - + getPixelsPadding().Bottom - mHScroll->getPixelsSize().getHeight(); + + mVScroll->setVisible( visible ); + mVScroll->setEnabled( visible ); + } + + Sizef size = getPixelsSize() - mRealPadding; + + if ( Exclusive == mViewType ) { + if ( mVScroll->isVisible() ) + size.x -= mVScroll->getPixelsSize().getWidth(); + + if ( mHScroll->isVisible() ) + size.y -= mHScroll->getPixelsSize().getHeight(); + } + + mVScroll->setPixelsPosition( getPixelsSize().getWidth() - mVScroll->getPixelsSize().getWidth() - + mRealPadding.Right, + mRealPadding.Top ); + mHScroll->setPixelsPosition( mRealPadding.Left, getPixelsSize().getHeight() - + mHScroll->getPixelsSize().getHeight() - + mRealPadding.Bottom ); + + mVScroll->setPixelsSize( mVScroll->getPixelsSize().getWidth(), + getPixelsSize().getHeight() - mRealPadding.Top - mRealPadding.Bottom ); + + mHScroll->setPixelsSize( + getPixelsSize().getWidth() - mRealPadding.Left - mRealPadding.Right - + ( mVScroll->isVisible() ? mVScroll->getPixelsSize().getWidth() : 0 ), + mHScroll->getPixelsSize().getHeight() ); + + if ( size.getWidth() > 0 ) + mHScroll->setPageStep( size.getWidth() / contentSize.getWidth() ); + if ( size.getHeight() > 0 ) + mVScroll->setPageStep( size.getHeight() / contentSize.getHeight() ); + + updateScroll(); +} + +void UIScrollableWidget::updateScroll() { + Sizef contentSize( getContentSize() ); + Sizef size = getPixelsSize() - mRealPadding; + if ( mVScroll->isVisible() ) + size.x -= mVScroll->getPixelsSize().getWidth(); + if ( mHScroll->isVisible() ) + size.y -= mHScroll->getPixelsSize().getHeight(); + Sizef totalScroll( contentSize - size ); + + Vector2f initScroll( mScrollOffset ); + mScrollOffset = Vector2f::Zero; + + if ( mVScroll->isVisible() && totalScroll.y > 0 ) + mScrollOffset.y = totalScroll.y * mVScroll->getValue(); + + if ( mHScroll->isVisible() && totalScroll.x > 0 ) + mScrollOffset.x = totalScroll.x * mHScroll->getValue(); + + if ( initScroll != mScrollOffset ) + onScrollChange(); +} + +void UIScrollableWidget::onScrollChange() {} + +void UIScrollableWidget::onValueChangeCb( const Event* ) { + updateScroll(); +} + +std::string UIScrollableWidget::getPropertyString( const PropertyDefinition* propertyDef, + const Uint32& propertyIndex ) { + if ( NULL == propertyDef ) + return ""; + + switch ( propertyDef->getPropertyId() ) { + case PropertyId::VScrollMode: + return getVerticalScrollMode() == ScrollBarMode::Auto + ? "auto" + : ( getVerticalScrollMode() == ScrollBarMode::AlwaysOn ? "on" : "off" ); + case PropertyId::HScrollMode: + return getHorizontalScrollMode() == ScrollBarMode::Auto + ? "auto" + : ( getHorizontalScrollMode() == ScrollBarMode::AlwaysOn ? "on" : "off" ); + case PropertyId::ScrollBarStyle: + return mVScroll->getScrollBarType() == UIScrollBar::NoButtons ? "no-buttons" + : "two-buttons"; + case PropertyId::ScrollBarMode: + return getViewType() == Inclusive ? "inclusive" : "exclusive"; + default: + return UIWidget::getPropertyString( propertyDef, propertyIndex ); + } +} + +bool UIScrollableWidget::applyProperty( const StyleSheetProperty& attribute ) { + if ( !checkPropertyDefinition( attribute ) ) + return false; + + switch ( attribute.getPropertyDefinition()->getPropertyId() ) { + case PropertyId::ScrollBarMode: { + std::string val( attribute.asString() ); + String::toLowerInPlace( val ); + if ( "inclusive" == val || "inside" == val ) + setViewType( Inclusive ); + else if ( "exclusive" == val || "outside" == val ) + setViewType( Exclusive ); + break; + } + case PropertyId::VScrollMode: { + std::string val( attribute.asString() ); + String::toLowerInPlace( val ); + + if ( "on" == val ) + setVerticalScrollMode( ScrollBarMode::AlwaysOn ); + else if ( "off" == val ) + setVerticalScrollMode( ScrollBarMode::AlwaysOn ); + else if ( "auto" == val ) + setVerticalScrollMode( ScrollBarMode::Auto ); + break; + } + case PropertyId::HScrollMode: { + std::string val( attribute.asString() ); + String::toLowerInPlace( val ); + + if ( "on" == val ) + setHorizontalScrollMode( ScrollBarMode::AlwaysOn ); + else if ( "off" == val ) + setHorizontalScrollMode( ScrollBarMode::AlwaysOn ); + else if ( "auto" == val ) + setHorizontalScrollMode( ScrollBarMode::Auto ); + break; + } + case PropertyId::ScrollBarStyle: { + std::string val( attribute.asString() ); + String::toLowerInPlace( val ); + + if ( "no-buttons" == val || "nobuttons" == val ) { + mVScroll->setScrollBarStyle( UIScrollBar::NoButtons ); + mHScroll->setScrollBarStyle( UIScrollBar::NoButtons ); + } else if ( "two-buttons" == val || "twobuttons" == val ) { + mVScroll->setScrollBarStyle( UIScrollBar::TwoButtons ); + mHScroll->setScrollBarStyle( UIScrollBar::NoButtons ); + } + break; + } + default: + return UIWidget::applyProperty( attribute ); + } + + return true; +} + +Uint32 UIScrollableWidget::onMessage( const NodeMessage* Msg ) { + switch ( Msg->getMsg() ) { + case NodeMessage::MouseUp: { + if ( mVScroll->isEnabled() ) { + if ( Msg->getFlags() & EE_BUTTON_WUMASK ) { + mVScroll->setValue( mVScroll->getValue() - mVScroll->getClickStep() ); + return 1; + } else if ( Msg->getFlags() & EE_BUTTON_WDMASK ) { + mVScroll->setValue( mVScroll->getValue() + mVScroll->getClickStep() ); + return 1; + } + } + } + } + return UIWidget::onMessage( Msg ); +} + +}} // namespace EE::UI diff --git a/src/eepp/ui/uitreeview.cpp b/src/eepp/ui/uitreeview.cpp index d08063eb8..ebe5764b0 100644 --- a/src/eepp/ui/uitreeview.cpp +++ b/src/eepp/ui/uitreeview.cpp @@ -1,6 +1,8 @@ +#include #include #include #include +#include #include namespace EE { namespace UI { @@ -18,6 +20,14 @@ UITreeView::UITreeView() : UIAbstractTableView( "treeview" ), mIndentWidth( 16 ) mContractIcon = getUISceneNode()->findIcon( "tree-contracted" ); } +Uint32 UITreeView::getType() const { + return UI_TYPE_TREEVIEW; +} + +bool UITreeView::isType( const Uint32& type ) const { + return UITreeView::getType() == type ? true : UIAbstractTableView::isType( type ); +} + UITreeView::MetadataForIndex& UITreeView::getIndexMetadata( const ModelIndex& index ) const { eeASSERT( index.isValid() ); auto it = mViewMetadata.find( index.data() ); @@ -81,9 +91,8 @@ void UITreeView::createOrUpdateColumns() { UIAbstractTableView::createOrUpdateColumns(); updateContentSize(); traverseTree( [&]( const ModelIndex& index, const size_t& indentLevel, const Float& yOffset ) { - for ( size_t col = 0; col < getModel()->columnCount(); col++ ) { + for ( size_t col = 0; col < getModel()->columnCount(); col++ ) updateCell( index, col, indentLevel, yOffset ); - } return IterationDecision::Continue; } ); } @@ -121,12 +130,20 @@ UIWidget* UITreeView::updateRow( const ModelIndex& index, const Float& yOffset ) rowWidget = it->second; } rowWidget->setPixelsSize( getContentSize().getWidth(), getRowHeight() ); - rowWidget->setPixelsPosition( {0, yOffset} ); + rowWidget->setPixelsPosition( {-mScrollOffset.x, yOffset - mScrollOffset.y} ); return rowWidget; } +void UITreeView::onScrollChange() { + mHeader->setPixelsPosition( -mScrollOffset.x, 0 ); + invalidateDraw(); +} + void UITreeView::updateContentSize() { + Sizef oldSize( mContentSize ); mContentSize = UIAbstractTableView::getContentSize(); + if ( oldSize != mContentSize ) + onContentSizeChange(); } UIPushButton* UITreeView::updateCell( const ModelIndex& index, const size_t& col, @@ -161,8 +178,12 @@ UIPushButton* UITreeView::updateCell( const ModelIndex& index, const size_t& col ModelIndex idx( getModel()->index( index.row(), col, index.parent() ) ); Variant variant( getModel()->data( idx, Model::Role::Display ) ); - if ( variant.isValid() ) - widget->setText( variant.asString() ); + if ( variant.isValid() ) { + if ( variant.is( Variant::Type::String ) ) + widget->setText( variant.asString() ); + else if ( variant.is( Variant::Type::cstr ) ) + widget->setText( variant.asCStr() ); + } if ( col == getModel()->treeColumn() && getModel()->rowCount( index ) > 0 ) widget->setIcon( getIndexMetadata( index ).open ? mExpandIcon : mContractIcon ); return widget; @@ -184,14 +205,18 @@ Sizef UITreeView::getContentSize() const { } void UITreeView::drawChilds() { - if ( mHeader && mHeader->isVisible() ) - mHeader->nodeDraw(); traverseTree( [&]( const ModelIndex& index, const size_t&, const Float& yOffset ) { updateRow( getModel()->index( index.row(), getModel()->treeColumn(), index.parent() ), yOffset ) ->nodeDraw(); return IterationDecision::Continue; } ); + if ( mHeader && mHeader->isVisible() ) + mHeader->nodeDraw(); + if ( mHScroll->isVisible() ) + mHScroll->nodeDraw(); + if ( mVScroll->isVisible() ) + mVScroll->nodeDraw(); } Node* UITreeView::overFind( const Vector2f& point ) { @@ -201,6 +226,10 @@ Node* UITreeView::overFind( const Vector2f& point ) { if ( mWorldBounds.contains( point ) && mPoly.pointInside( point ) ) { writeNodeFlag( NODE_FLAG_MOUSEOVER_ME_OR_CHILD, 1 ); mSceneNode->addMouseOverNode( this ); + if ( mHScroll->isVisible() && ( pOver = mHScroll->overFind( point ) ) ) + return pOver; + if ( mVScroll->isVisible() && ( pOver = mVScroll->overFind( point ) ) ) + return pOver; if ( mHeader && ( pOver = mHeader->overFind( point ) ) ) return pOver; traverseTree( @@ -213,7 +242,7 @@ Node* UITreeView::overFind( const Vector2f& point ) { return IterationDecision::Stop; return IterationDecision::Continue; } ); - if ( NULL == pOver ) + if ( !pOver ) pOver = this; } } diff --git a/src/eepp/ui/uiwidgetcreator.cpp b/src/eepp/ui/uiwidgetcreator.cpp index 933ad7c85..2f1aa0e9a 100644 --- a/src/eepp/ui/uiwidgetcreator.cpp +++ b/src/eepp/ui/uiwidgetcreator.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -80,6 +81,7 @@ void UIWidgetCreator::createBaseWidgetList() { registeredWidget["viewpager"] = UIViewPager::New; registeredWidget["codeeditor"] = UICodeEditor::New; registeredWidget["splitter"] = UISplitter::New; + registeredWidget["treeview"] = UITreeView::New; registeredWidget["hbox"] = UILinearLayout::NewHorizontal; registeredWidget["vbox"] = UILinearLayout::NewVertical; From 62814d8cc6ad2801b44660ae6c4e6c068c9d419a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Fri, 10 Jul 2020 01:16:18 -0300 Subject: [PATCH 6/7] More WIP, some minor changes. Important fix in Color::toHexString(). --- bin/assets/ui/breeze.css | 14 +-- .../eepp/ui/abstract/modeleditingdelegate.hpp | 4 +- .../eepp/ui/abstract/uiabstracttableview.hpp | 7 +- include/eepp/ui/abstract/uiabstractview.hpp | 16 --- include/eepp/ui/uimenuitem.hpp | 2 +- include/eepp/ui/uimenusubmenu.hpp | 2 +- include/eepp/ui/uipushbutton.hpp | 5 +- include/eepp/ui/uitab.hpp | 2 +- include/eepp/ui/uitableheadercolumn.hpp | 38 ++++++ include/eepp/ui/uitreeview.hpp | 16 ++- projects/linux/ee.files | 2 + src/eepp/scene/node.cpp | 2 +- src/eepp/system/color.cpp | 2 +- src/eepp/ui/abstract/uiabstracttableview.cpp | 89 +------------- src/eepp/ui/abstract/uiabstractview.cpp | 13 -- .../ui/css/stylesheetpropertyanimation.cpp | 4 +- src/eepp/ui/uimenuitem.cpp | 2 +- src/eepp/ui/uimenusubmenu.cpp | 2 +- src/eepp/ui/uinode.cpp | 8 +- src/eepp/ui/uipushbutton.cpp | 27 ++++- src/eepp/ui/uitab.cpp | 2 +- src/eepp/ui/uitableheadercolumn.cpp | 90 ++++++++++++++ src/eepp/ui/uitreeview.cpp | 114 ++++++++++++------ src/tests/ui_perf_test/ui_perf_test.cpp | 16 ++- 24 files changed, 284 insertions(+), 195 deletions(-) create mode 100644 include/eepp/ui/uitableheadercolumn.hpp create mode 100644 src/eepp/ui/uitableheadercolumn.cpp diff --git a/bin/assets/ui/breeze.css b/bin/assets/ui/breeze.css index a57067912..c93381611 100644 --- a/bin/assets/ui/breeze.css +++ b/bin/assets/ui/breeze.css @@ -759,11 +759,7 @@ table::header::column:hover { table::row { background-color: var(--list-back); - transition: all 0.125s; -} - -table::row:nth-child(odd) { - background-color: var(--back); + transition: all 0.5s; } table::row:hover { @@ -772,11 +768,15 @@ table::row:hover { table::cell { background-color: transparent; - transition: all 0.125s; + /*transition: all 0.125s;*/ } table::cell:hover { - background-color: var(--primary); + /*background-color: var(--primary);*/ +} + +TreeView > ScrollBar { + background-color: var(--back); } .appbackground { diff --git a/include/eepp/ui/abstract/modeleditingdelegate.hpp b/include/eepp/ui/abstract/modeleditingdelegate.hpp index dbf2ec4dd..d95e773ec 100644 --- a/include/eepp/ui/abstract/modeleditingdelegate.hpp +++ b/include/eepp/ui/abstract/modeleditingdelegate.hpp @@ -24,7 +24,7 @@ class EE_API ModelEditingDelegate { std::function onCommit; - virtual Variant value() const = 0; + virtual Variant getValue() const = 0; virtual void setValue( const Variant& ) = 0; virtual void willBeginEditing() {} @@ -45,4 +45,4 @@ class EE_API ModelEditingDelegate { }}} // namespace EE::UI::Abstract -#endif // MODELEDITINGDELEGATE_HPP +#endif // EE_UI_MODELEDITINGDELEGATE_HPP diff --git a/include/eepp/ui/abstract/uiabstracttableview.hpp b/include/eepp/ui/abstract/uiabstracttableview.hpp index 66ceb69a5..819890220 100644 --- a/include/eepp/ui/abstract/uiabstracttableview.hpp +++ b/include/eepp/ui/abstract/uiabstracttableview.hpp @@ -3,6 +3,7 @@ #include #include +#include using namespace EE::Math; @@ -15,8 +16,6 @@ namespace EE { namespace UI { namespace Abstract { class EE_API UIAbstractTableView : public UIAbstractView { public: - static UIAbstractTableView* New(); - Uint32 getType() const; bool isType( const Uint32& type ) const; @@ -44,7 +43,7 @@ class EE_API UIAbstractTableView : public UIAbstractView { Vector2f getColumnPosition( const size_t& index ); protected: - friend class UITableHeaderColumn; + friend class EE::UI::UITableHeaderColumn; virtual ~UIAbstractTableView(); @@ -70,6 +69,8 @@ class EE_API UIAbstractTableView : public UIAbstractView { virtual void onColumnSizeChange( const size_t& colIndex ); + virtual void onColumnResizeToContent( const size_t& colIndex ); + void updateHeaderSize(); UILinearLayout* mHeader; diff --git a/include/eepp/ui/abstract/uiabstractview.hpp b/include/eepp/ui/abstract/uiabstractview.hpp index 02215ab9a..78615dae4 100644 --- a/include/eepp/ui/abstract/uiabstractview.hpp +++ b/include/eepp/ui/abstract/uiabstractview.hpp @@ -31,10 +31,6 @@ class EE_API UIAbstractView : public UIScrollableWidget { void setEditable( bool editable ) { mEditable = editable; } - void setActivatesOnSelection( bool b ) { mActivatesOnSelection = b; } - - bool getActivatesOnSelection() const { return mActivatesOnSelection; } - void notifySelectionChange(); protected: @@ -48,29 +44,17 @@ class EE_API UIAbstractView : public UIScrollableWidget { virtual ~UIAbstractView(); - void setHoveredIndex( const ModelIndex& ); - void activate( const ModelIndex& ); - void activateSelected(); - bool mEditable{false}; ModelIndex mEditIndex; UIWidget* mEditWidget; Rect mEditWidgetContentRect; - Vector2i mLeftMouseDownPosition; - bool mMightDrag{false}; - - ModelIndex mHoveredIndex; - std::shared_ptr mModel; std::unique_ptr mEditingDelegate; ModelSelection mSelection; - bool mActivatesOnSelection{false}; std::function mOnSelectionChange; - std::function mOnActivation; std::function mOnSelection; - std::function mOnDrop; }; }}} // namespace EE::UI::Abstract diff --git a/include/eepp/ui/uimenuitem.hpp b/include/eepp/ui/uimenuitem.hpp index b6bc86573..7af52df05 100644 --- a/include/eepp/ui/uimenuitem.hpp +++ b/include/eepp/ui/uimenuitem.hpp @@ -36,7 +36,7 @@ class EE_API UIMenuItem : public UIPushButton { virtual Uint32 onMouseClick( const Vector2i& pos, const Uint32& flags ); - virtual UIWidget* getExtraInnerWidget(); + virtual UIWidget* getExtraInnerWidget() const; void createShortcutView(); }; diff --git a/include/eepp/ui/uimenusubmenu.hpp b/include/eepp/ui/uimenusubmenu.hpp index ab5722c78..37ddbb0cd 100644 --- a/include/eepp/ui/uimenusubmenu.hpp +++ b/include/eepp/ui/uimenusubmenu.hpp @@ -51,7 +51,7 @@ class EE_API UIMenuSubMenu : public UIMenuItem { virtual void onAlphaChange(); - virtual UIWidget* getExtraInnerWidget(); + virtual UIWidget* getExtraInnerWidget() const; void onSubMenuFocusLoss( const Event* Event ); diff --git a/include/eepp/ui/uipushbutton.hpp b/include/eepp/ui/uipushbutton.hpp index 85f979ffa..99a7b4ccf 100644 --- a/include/eepp/ui/uipushbutton.hpp +++ b/include/eepp/ui/uipushbutton.hpp @@ -43,6 +43,9 @@ class EE_API UIPushButton : public UIWidget { const Uint32& propertyIndex = 0 ); void setTextAlign( const Uint32& align ); + + virtual Sizef getContentSize() const; + protected: UIImage* mIcon; UITextView* mTextBox; @@ -68,7 +71,7 @@ class EE_API UIPushButton : public UIWidget { virtual Uint32 onKeyUp( const KeyEvent& Event ); - virtual UIWidget* getExtraInnerWidget(); + virtual UIWidget* getExtraInnerWidget() const; }; }} // namespace EE::UI diff --git a/include/eepp/ui/uitab.hpp b/include/eepp/ui/uitab.hpp index 208acd6e9..ac33626cb 100644 --- a/include/eepp/ui/uitab.hpp +++ b/include/eepp/ui/uitab.hpp @@ -62,7 +62,7 @@ class EE_API UITab : public UISelectButton { virtual void onSizeChange(); - virtual UIWidget* getExtraInnerWidget(); + virtual UIWidget* getExtraInnerWidget() const; void setOwnedNode(); diff --git a/include/eepp/ui/uitableheadercolumn.hpp b/include/eepp/ui/uitableheadercolumn.hpp new file mode 100644 index 000000000..3c83c159e --- /dev/null +++ b/include/eepp/ui/uitableheadercolumn.hpp @@ -0,0 +1,38 @@ +#ifndef EE_UI_UITABLEHEADERCOLUMN_HPP +#define EE_UI_UITABLEHEADERCOLUMN_HPP + +#include + +namespace EE { namespace UI { + +namespace Abstract { +class UIAbstractTableView; +} +using namespace Abstract; + +class UITableHeaderColumn : public UIPushButton { + public: + UITableHeaderColumn( UIAbstractTableView* view, const size_t& colIndex ); + + protected: + UIAbstractTableView* mView; + size_t mColIndex; + + Uint32 onCalculateDrag( const Vector2f& position, const Uint32& flags ); + + Uint32 onMouseDown( const Vector2i& position, const Uint32& flags ); + + Uint32 onDrag( const Vector2f& position, const Uint32&, const Sizef& dragDiff ); + + Uint32 onMouseLeave( const Vector2i& position, const Uint32& flags ); + + Uint32 onMouseMove( const Vector2i& position, const Uint32& flags ); + + Uint32 onMouseDoubleClick( const Vector2i& position, const Uint32& flags ); + + Uint32 onDragStop( const Vector2i& pos, const Uint32& flags ); +}; + +}} + +#endif // EE_UI_UITABLEHEADERCOLUMN_HPP diff --git a/include/eepp/ui/uitreeview.hpp b/include/eepp/ui/uitreeview.hpp index 4e8305cb8..7681e70bb 100644 --- a/include/eepp/ui/uitreeview.hpp +++ b/include/eepp/ui/uitreeview.hpp @@ -57,25 +57,29 @@ class EE_API UITreeView : public UIAbstractTableView { template void traverseTree( Callback ) const; - mutable std::unordered_map> mViewMetadata; - mutable std::unordered_map> mWidgets; - mutable std::unordered_map mRows; + mutable std::map> mViewMetadata; + mutable std::map> mWidgets; + mutable std::map mRows; virtual size_t getItemCount() const; UITreeView::MetadataForIndex& getIndexMetadata( const ModelIndex& index ) const; - EE::UI::UIPushButton* getIndexWidget( const int& column, void* data ); + UIWidget* getIndexWidget( const int& column, void* data ); virtual void onColumnSizeChange( const size_t& colIndex ); - virtual UIPushButton* updateCell( const ModelIndex& index, const size_t& col, - const size_t& indentLevel, const Float& yOffset ); + virtual UIWidget* updateCell( const ModelIndex& index, const size_t& col, + const size_t& indentLevel, const Float& yOffset ); virtual UIWidget* updateRow( const ModelIndex& index, const Float& yOffset ); + virtual UIWidget* createCell( UIWidget* rowWidget, const ModelIndex& index, const size_t& col ); + virtual void onScrollChange(); + virtual void onColumnResizeToContent( const size_t& colIndex ); + void updateContentSize(); }; diff --git a/projects/linux/ee.files b/projects/linux/ee.files index 0f5aa06ab..e5b2e32a5 100644 --- a/projects/linux/ee.files +++ b/projects/linux/ee.files @@ -396,6 +396,7 @@ ../../include/eepp/ui/uitab.hpp ../../include/eepp/ui/uitablecell.hpp ../../include/eepp/ui/uitable.hpp +../../include/eepp/ui/uitableheadercolumn.hpp ../../include/eepp/ui/uitabwidget.hpp ../../include/eepp/ui/uitextedit.hpp ../../include/eepp/ui/uitextinput.hpp @@ -859,6 +860,7 @@ ../../src/eepp/ui/uitab.cpp ../../src/eepp/ui/uitablecell.cpp ../../src/eepp/ui/uitable.cpp +../../src/eepp/ui/uitableheadercolumn.cpp ../../src/eepp/ui/uitabwidget.cpp ../../src/eepp/ui/uitextedit.cpp ../../src/eepp/ui/uitextinput.cpp diff --git a/src/eepp/scene/node.cpp b/src/eepp/scene/node.cpp index efc645081..f16cf6724 100644 --- a/src/eepp/scene/node.cpp +++ b/src/eepp/scene/node.cpp @@ -48,7 +48,7 @@ Node::~Node() { if ( NULL != mParentNode ) mParentNode->childRemove( this ); - EventDispatcher* eventDispatcher = NULL != mSceneNode ? mSceneNode->getEventDispatcher() : NULL; + EventDispatcher* eventDispatcher = getEventDispatcher(); if ( NULL != eventDispatcher ) { if ( eventDispatcher->getFocusNode() == this && mSceneNode != this ) { diff --git a/src/eepp/system/color.cpp b/src/eepp/system/color.cpp index 709f90a8e..d5929ed85 100644 --- a/src/eepp/system/color.cpp +++ b/src/eepp/system/color.cpp @@ -231,7 +231,7 @@ std::string Color::toHexString( const bool& prependHashtag ) const { stream << std::setfill( '0' ) << std::setw( sizeof( Color ) * 2 ) << std::hex << getValue(); std::string str = stream.str(); if ( this->a == 255 ) - return str.substr( 0, 6 ); + return str.substr( 0, prependHashtag ? 7 : 6 ); return str; } diff --git a/src/eepp/ui/abstract/uiabstracttableview.cpp b/src/eepp/ui/abstract/uiabstracttableview.cpp index 76517bb26..b25170c11 100644 --- a/src/eepp/ui/abstract/uiabstracttableview.cpp +++ b/src/eepp/ui/abstract/uiabstracttableview.cpp @@ -5,93 +5,6 @@ namespace EE { namespace UI { namespace Abstract { -class UITableHeaderColumn : public UIPushButton { - public: - UITableHeaderColumn( UIAbstractTableView* view, const size_t& colIndex ) : - UIPushButton( "table::header::column" ), mView( view ), mColIndex( colIndex ) { - setDragEnabled( true ); - } - - protected: - UIAbstractTableView* mView; - size_t mColIndex; - - Uint32 onCalculateDrag( const Vector2f& position, const Uint32& flags ) { - if ( isDragEnabled() && isDragging() && NULL != getEventDispatcher() ) { - EventDispatcher* eventDispatcher = getEventDispatcher(); - if ( !( flags /*press trigger*/ & mDragButton ) ) { - setDragging( false ); - eventDispatcher->setNodeDragging( NULL ); - return 1; - } - Vector2f pos( eefloor( position.x ), eefloor( position.y ) ); - if ( mDragPoint != pos && std::abs( mDragPoint.x - pos.x ) > 1.f ) { - Sizef dragDiff( ( Float )( mDragPoint.x - pos.x ), 0 ); - if ( onDrag( pos, flags, dragDiff ) ) { - mDragPoint = pos; - eventDispatcher->setNodeDragging( this ); - } - } - } - return 1; - } - - Uint32 onMouseDown( const Vector2i& position, const Uint32& flags ) { - Vector2f localPos( convertToNodeSpace( position.asFloat() ) ); - if ( NULL != getEventDispatcher() && !getEventDispatcher()->isNodeDragging() && - !( getEventDispatcher()->getLastPressTrigger() & mDragButton ) && - ( flags & mDragButton ) && isDragEnabled() && !isDragging() && - localPos.x >= mSize.getWidth() - mView->getDragBorderDistance() ) { - startDragging( position.asFloat() ); - } - pushState( UIState::StatePressed ); - return Node::onMouseDown( position, flags ); - } - - Uint32 onDrag( const Vector2f& position, const Uint32&, const Sizef& dragDiff ) { - Vector2f localPos( convertToNodeSpace( position ) ); - if ( isDragging() || localPos.x >= mSize.getWidth() - mView->getDragBorderDistance() ) { - setPixelsSize( mSize.x - dragDiff.x, mSize.getHeight() ); - if ( mSize.getWidth() != mView->columnData( mColIndex ).width ) { - mView->columnData( mColIndex ).width = mSize.getWidth(); - mView->updateHeaderSize(); - mView->onColumnSizeChange( mColIndex ); - } - return 1; - } - return 0; - } - - Uint32 onMouseLeave( const Vector2i& position, const Uint32& flags ) { - if ( !isDragging() ) - getUISceneNode()->setCursor( Cursor::Arrow ); - return UIPushButton::onMouseLeave( position, flags ); - } - - Uint32 onMouseMove( const Vector2i& position, const Uint32& flags ) { - Vector2f localPos( convertToNodeSpace( position.asFloat() ) ); - if ( isDragging() || localPos.x >= mSize.getWidth() - mView->getDragBorderDistance() ) { - getUISceneNode()->setCursor( Cursor::SizeWE ); - } else if ( !isDragging() ) { - getUISceneNode()->setCursor( Cursor::Arrow ); - } - return UIPushButton::onMouseMove( position, flags ); - }; - - Uint32 onDragStop( const Vector2i& pos, const Uint32& flags ) { - getUISceneNode()->setCursor( Cursor::Arrow ); - mView->columnData( mColIndex ).width = mSize.getWidth(); - mView->updateHeaderSize(); - mView->onColumnSizeChange( mColIndex ); - - return UIPushButton::onDragStop( pos, flags ); - } -}; - -UIAbstractTableView* UIAbstractTableView::New() { - return eeNew( UIAbstractTableView, ( "abstractview" ) ); -} - UIAbstractTableView::UIAbstractTableView( const std::string& tag ) : UIAbstractView( tag ), mDragBorderDistance( PixelDensity::dpToPx( 4 ) ) { mHeader = UILinearLayout::NewWithTag( "table::header", UIOrientation::Horizontal ); @@ -208,6 +121,8 @@ void UIAbstractTableView::onSizeChange() { void UIAbstractTableView::onColumnSizeChange( const size_t& ) {} +void UIAbstractTableView::onColumnResizeToContent( const size_t& ) {} + void UIAbstractTableView::updateHeaderSize() { size_t count = getModel()->columnCount(); Float totalWidth = 0; diff --git a/src/eepp/ui/abstract/uiabstractview.cpp b/src/eepp/ui/abstract/uiabstractview.cpp index 49067fe7b..ee8ddbfbe 100644 --- a/src/eepp/ui/abstract/uiabstractview.cpp +++ b/src/eepp/ui/abstract/uiabstractview.cpp @@ -27,7 +27,6 @@ void UIAbstractView::setModel( std::shared_ptr model ) { } void UIAbstractView::onModelUpdate( unsigned flags ) { - mHoveredIndex = {}; if ( !getModel() || ( flags & Model::InvalidateAllIndexes ) ) { getSelection().clear(); } else { @@ -41,18 +40,6 @@ void UIAbstractView::onModelSelectionChange() { mOnSelection( getSelection().first() ); } -void UIAbstractView::activate( const ModelIndex& index ) { - if ( mOnActivation ) - mOnActivation( index ); -} - -void UIAbstractView::activateSelected() { - if ( !mOnActivation ) - return; - - getSelection().forEachIndex( [this]( auto& index ) { mOnActivation( index ); } ); -} - void UIAbstractView::notifySelectionChange() { onModelSelectionChange(); if ( mOnSelectionChange ) diff --git a/src/eepp/ui/css/stylesheetpropertyanimation.cpp b/src/eepp/ui/css/stylesheetpropertyanimation.cpp index 4f27605bf..7d75a3216 100644 --- a/src/eepp/ui/css/stylesheetpropertyanimation.cpp +++ b/src/eepp/ui/css/stylesheetpropertyanimation.cpp @@ -130,7 +130,7 @@ void StyleSheetPropertyAnimation::tweenProperty( UIWidget* widget, const Float& StyleSheetPropertyAnimation* StyleSheetPropertyAnimation::fromAnimationKeyframes( const AnimationDefinition& animation, const KeyframesDefinition& keyframes, const PropertyDefinition* propertyDef, UIWidget* widget, const Uint32& propertyIndex, - const AnimationOrigin& animationOrigin ) { + const AnimationOrigin& ) { std::vector properties; std::vector times; @@ -314,7 +314,7 @@ void StyleSheetPropertyAnimation::onStart() { } } -void StyleSheetPropertyAnimation::onUpdate( const Time& time ) { +void StyleSheetPropertyAnimation::onUpdate( const Time& ) { if ( NULL != mNode && mNode->isWidget() ) { UIWidget* widget = mNode->asType(); diff --git a/src/eepp/ui/uimenuitem.cpp b/src/eepp/ui/uimenuitem.cpp index 75fffd49e..f05c86f2b 100644 --- a/src/eepp/ui/uimenuitem.cpp +++ b/src/eepp/ui/uimenuitem.cpp @@ -72,7 +72,7 @@ Uint32 UIMenuItem::onMouseClick( const Vector2i&, const Uint32& flags ) { return 1; } -UIWidget* UIMenuItem::getExtraInnerWidget() { +UIWidget* UIMenuItem::getExtraInnerWidget() const { return mShortcutView; } diff --git a/src/eepp/ui/uimenusubmenu.cpp b/src/eepp/ui/uimenusubmenu.cpp index a4053eaf4..7e5986cc5 100644 --- a/src/eepp/ui/uimenusubmenu.cpp +++ b/src/eepp/ui/uimenusubmenu.cpp @@ -60,7 +60,7 @@ void UIMenuSubMenu::onAlphaChange() { mArrow->setAlpha( mAlpha ); } -UIWidget* UIMenuSubMenu::getExtraInnerWidget() { +UIWidget* UIMenuSubMenu::getExtraInnerWidget() const { return mArrow; } diff --git a/src/eepp/ui/uinode.cpp b/src/eepp/ui/uinode.cpp index 681ab0e18..bb1e3198d 100644 --- a/src/eepp/ui/uinode.cpp +++ b/src/eepp/ui/uinode.cpp @@ -736,21 +736,21 @@ void UINode::nodeDraw() { matrixSet(); - clipStart(); - if ( mWorldBounds.intersect( mSceneNode->getWorldBounds() ) ) { + clipStart(); + draw(); drawChilds(); if ( 0.f != mAlpha ) drawForeground(); + + clipEnd(); } else if ( !isClipped() ) { drawChilds(); } - clipEnd(); - drawBorder(); drawHighlightFocus(); diff --git a/src/eepp/ui/uipushbutton.cpp b/src/eepp/ui/uipushbutton.cpp index 63cc81ba7..9ad12c559 100644 --- a/src/eepp/ui/uipushbutton.cpp +++ b/src/eepp/ui/uipushbutton.cpp @@ -326,7 +326,7 @@ const Sizei& UIPushButton::getIconMinimumSize() const { return mIconMinSize; } -UIWidget* UIPushButton::getExtraInnerWidget() { +UIWidget* UIPushButton::getExtraInnerWidget() const { return NULL; } @@ -336,6 +336,31 @@ void UIPushButton::setTextAlign( const Uint32& align ) { onAlignChange(); } +Sizef UIPushButton::getContentSize() const { + Float sH = getSkinSize().getHeight(); + Float sHS = getSkinSize( UIState::StateFlagSelected ).getHeight(); + Float tH = mTextBox->getPixelsSize().getHeight(); + Float eH = + NULL != getExtraInnerWidget() ? getExtraInnerWidget()->getPixelsSize().getHeight() : 0; + Float minHeight = eeceil( eemax( eemax( PixelDensity::dpToPx( eemax( sH, sHS ) ), tH ), eH ) ); + Int32 txtW = mTextBox->getPixelsSize().getWidth(); + Int32 iconSize = mIcon->getPixelsSize().getWidth() > 0 + ? mIcon->getPixelsSize().getWidth() + + PixelDensity::dpToPxI( mIcon->getLayoutMargin().Left + + mIcon->getLayoutMargin().Right ) + : 0; + UIWidget* eWidget = getExtraInnerWidget(); + Int32 eWidgetSize = NULL != eWidget ? PixelDensity::dpToPxI( eWidget->getSize().getWidth() + + eWidget->getLayoutMargin().Left + + eWidget->getLayoutMargin().Right ) + : 0; + Int32 minWidth = txtW + iconSize + eWidgetSize + mRealPadding.Left + mRealPadding.Right + + ( NULL != getSkin() ? PixelDensity::dpToPxI( getSkin()->getBorderSize().Left + + getSkin()->getBorderSize().Right ) + : 0 ); + return Sizef( minWidth, minHeight ); +} + std::string UIPushButton::getPropertyString( const PropertyDefinition* propertyDef, const Uint32& propertyIndex ) { if ( NULL == propertyDef ) diff --git a/src/eepp/ui/uitab.cpp b/src/eepp/ui/uitab.cpp index 13a9d18f5..f7e5ce589 100644 --- a/src/eepp/ui/uitab.cpp +++ b/src/eepp/ui/uitab.cpp @@ -125,7 +125,7 @@ void UITab::onSizeChange() { UISelectButton::onSizeChange(); } -UIWidget* UITab::getExtraInnerWidget() { +UIWidget* UITab::getExtraInnerWidget() const { return mCloseButton; } diff --git a/src/eepp/ui/uitableheadercolumn.cpp b/src/eepp/ui/uitableheadercolumn.cpp new file mode 100644 index 000000000..07e3aabe8 --- /dev/null +++ b/src/eepp/ui/uitableheadercolumn.cpp @@ -0,0 +1,90 @@ +#include +#include +#include + +namespace EE { namespace UI { + +UITableHeaderColumn::UITableHeaderColumn( UIAbstractTableView* view, const size_t& colIndex ) : + UIPushButton( "table::header::column" ), mView( view ), mColIndex( colIndex ) { + setDragEnabled( true ); +} + +Uint32 UITableHeaderColumn::onCalculateDrag( const Vector2f& position, const Uint32& flags ) { + if ( isDragEnabled() && isDragging() && NULL != getEventDispatcher() ) { + EventDispatcher* eventDispatcher = getEventDispatcher(); + if ( !( flags /*press trigger*/ & mDragButton ) ) { + setDragging( false ); + eventDispatcher->setNodeDragging( NULL ); + return 1; + } + Vector2f pos( eefloor( position.x ), eefloor( position.y ) ); + if ( mDragPoint != pos && std::abs( mDragPoint.x - pos.x ) > 1.f ) { + Sizef dragDiff( ( Float )( mDragPoint.x - pos.x ), 0 ); + if ( onDrag( pos, flags, dragDiff ) ) { + mDragPoint = pos; + eventDispatcher->setNodeDragging( this ); + } + } + } + return 1; +} + +Uint32 UITableHeaderColumn::onMouseDown( const Vector2i& position, const Uint32& flags ) { + Vector2f localPos( convertToNodeSpace( position.asFloat() ) ); + if ( NULL != getEventDispatcher() && !getEventDispatcher()->isNodeDragging() && + !( getEventDispatcher()->getLastPressTrigger() & mDragButton ) && + ( flags & mDragButton ) && isDragEnabled() && !isDragging() && + localPos.x >= mSize.getWidth() - mView->getDragBorderDistance() ) { + startDragging( position.asFloat() ); + } + pushState( UIState::StatePressed ); + return Node::onMouseDown( position, flags ); +} + +Uint32 UITableHeaderColumn::onDrag( const Vector2f& position, const Uint32&, + const Sizef& dragDiff ) { + Vector2f localPos( convertToNodeSpace( position ) ); + if ( isDragging() || localPos.x >= mSize.getWidth() - mView->getDragBorderDistance() ) { + setPixelsSize( mSize.x - dragDiff.x, mSize.getHeight() ); + if ( mSize.getWidth() != mView->columnData( mColIndex ).width ) { + mView->columnData( mColIndex ).width = mSize.getWidth(); + mView->updateHeaderSize(); + mView->onColumnSizeChange( mColIndex ); + } + return 1; + } + return 0; +} + +Uint32 UITableHeaderColumn::onMouseLeave( const Vector2i& position, const Uint32& flags ) { + if ( !isDragging() ) + getUISceneNode()->setCursor( Cursor::Arrow ); + return UIPushButton::onMouseLeave( position, flags ); +} + +Uint32 UITableHeaderColumn::onMouseMove( const Vector2i& position, const Uint32& flags ) { + Vector2f localPos( convertToNodeSpace( position.asFloat() ) ); + if ( isDragging() || localPos.x >= mSize.getWidth() - mView->getDragBorderDistance() ) { + getUISceneNode()->setCursor( Cursor::SizeWE ); + } else if ( !isDragging() ) { + getUISceneNode()->setCursor( Cursor::Arrow ); + } + return UIPushButton::onMouseMove( position, flags ); +} + +Uint32 UITableHeaderColumn::onMouseDoubleClick( const Vector2i& position, const Uint32& flags ) { + Vector2f localPos( convertToNodeSpace( position.asFloat() ) ); + if ( localPos.x >= mSize.getWidth() - mView->getDragBorderDistance() ) + mView->onColumnResizeToContent( mColIndex ); + return UIPushButton::onMouseDoubleClick( position, flags ); +} + +Uint32 UITableHeaderColumn::onDragStop( const Vector2i& pos, const Uint32& flags ) { + getUISceneNode()->setCursor( Cursor::Arrow ); + mView->columnData( mColIndex ).width = mSize.getWidth(); + mView->updateHeaderSize(); + mView->onColumnSizeChange( mColIndex ); + return UIPushButton::onDragStop( pos, flags ); +} + +}} // namespace EE::UI diff --git a/src/eepp/ui/uitreeview.cpp b/src/eepp/ui/uitreeview.cpp index ebe5764b0..9f3647b81 100644 --- a/src/eepp/ui/uitreeview.cpp +++ b/src/eepp/ui/uitreeview.cpp @@ -39,7 +39,7 @@ UITreeView::MetadataForIndex& UITreeView::getIndexMetadata( const ModelIndex& in return *ref; } -UIPushButton* UITreeView::getIndexWidget( const int& column, void* data ) { +UIWidget* UITreeView::getIndexWidget( const int& column, void* data ) { auto it = mWidgets[column].find( data ); if ( it != mWidgets[column].end() ) return it->second; @@ -90,11 +90,6 @@ void UITreeView::createOrUpdateColumns() { return; UIAbstractTableView::createOrUpdateColumns(); updateContentSize(); - traverseTree( [&]( const ModelIndex& index, const size_t& indentLevel, const Float& yOffset ) { - for ( size_t col = 0; col < getModel()->columnCount(); col++ ) - updateCell( index, col, indentLevel, yOffset ); - return IterationDecision::Continue; - } ); } size_t UITreeView::getItemCount() const { @@ -108,13 +103,6 @@ size_t UITreeView::getItemCount() const { void UITreeView::onColumnSizeChange( const size_t& ) { updateContentSize(); - traverseTree( [&]( const ModelIndex& index, const size_t& indentLevel, const Float& yOffset ) { - updateRow( getModel()->index( index.row(), getModel()->treeColumn(), index.parent() ), - yOffset ); - for ( size_t colIndex = 0; colIndex < getModel()->columnCount(); colIndex++ ) - updateCell( index, colIndex, indentLevel, yOffset ); - return IterationDecision::Continue; - } ); } UIWidget* UITreeView::updateRow( const ModelIndex& index, const Float& yOffset ) { @@ -122,6 +110,7 @@ UIWidget* UITreeView::updateRow( const ModelIndex& index, const Float& yOffset ) auto it = mRows.find( index.data() ); if ( it == mRows.end() ) { rowWidget = UIWidget::NewWithTag( "table::row" ); + rowWidget->clipEnable(); rowWidget->setParent( this ); rowWidget->setLayoutSizePolicy( SizePolicy::Fixed, SizePolicy::Fixed ); rowWidget->reloadStyle( true, true, true ); @@ -134,6 +123,27 @@ UIWidget* UITreeView::updateRow( const ModelIndex& index, const Float& yOffset ) return rowWidget; } +UIWidget* UITreeView::createCell( UIWidget* rowWidget, const ModelIndex& index, + const size_t& col ) { + UIPushButton* widget = UIPushButton::NewWithTag( "table::cell" ); + widget->setParent( rowWidget ); + widget->unsetFlags( UI_AUTO_SIZE ); + widget->clipEnable(); + widget->setLayoutSizePolicy( SizePolicy::Fixed, SizePolicy::Fixed ); + widget->asType()->setTextAlign( UI_HALIGN_LEFT ); + if ( col == getModel()->treeColumn() ) { + widget->addEventListener( Event::MouseDoubleClick, [&, index]( const Event* event ) { + auto mouseEvent = static_cast( event ); + if ( mouseEvent->getFlags() & EE_BUTTON_LMASK ) { + auto& data = getIndexMetadata( index ); + data.open = !data.open; + createOrUpdateColumns(); + } + } ); + } + return widget; +} + void UITreeView::onScrollChange() { mHeader->setPixelsPosition( -mScrollOffset.x, 0 ); invalidateDraw(); @@ -146,46 +156,37 @@ void UITreeView::updateContentSize() { onContentSizeChange(); } -UIPushButton* UITreeView::updateCell( const ModelIndex& index, const size_t& col, - const size_t& indentLevel, const Float& yOffset ) { - UIPushButton* widget = getIndexWidget( col, index.data() ); +UIWidget* UITreeView::updateCell( const ModelIndex& index, const size_t& col, + const size_t& indentLevel, const Float& yOffset ) { + auto* widget = getIndexWidget( col, index.data() ); if ( !widget ) { UIWidget* rowWidget = updateRow( getModel()->index( index.row(), getModel()->treeColumn(), index.parent() ), yOffset ); - widget = UIPushButton::NewWithTag( "table::cell" ); - widget->setParent( rowWidget ); - widget->unsetFlags( UI_AUTO_SIZE ); - widget->clipEnable(); - widget->setLayoutSizePolicy( SizePolicy::Fixed, SizePolicy::Fixed ); - widget->setTextAlign( UI_HALIGN_LEFT ); - if ( col == getModel()->treeColumn() ) { - widget->addEventListener( Event::MouseDoubleClick, [&, index]( const Event* event ) { - auto mouseEvent = static_cast( event ); - if ( mouseEvent->getFlags() & EE_BUTTON_LMASK ) { - auto& data = getIndexMetadata( index ); - data.open = !data.open; - createOrUpdateColumns(); - } - } ); - } + widget = createCell( rowWidget, index, col ); mWidgets[col].insert( {index.data(), widget} ); } widget->setPixelsSize( columnData( col ).width, getRowHeight() ); widget->setPixelsPosition( {getColumnPosition( col ).x, 0} ); + if ( col == getModel()->treeColumn() ) widget->setPaddingLeft( getIndentWidth() * indentLevel ); ModelIndex idx( getModel()->index( index.row(), col, index.parent() ) ); Variant variant( getModel()->data( idx, Model::Role::Display ) ); - if ( variant.isValid() ) { - if ( variant.is( Variant::Type::String ) ) - widget->setText( variant.asString() ); - else if ( variant.is( Variant::Type::cstr ) ) - widget->setText( variant.asCStr() ); + + if ( widget->isType( UI_TYPE_PUSHBUTTON ) ) { + UIPushButton* pushButton = widget->asType(); + if ( variant.isValid() ) { + if ( variant.is( Variant::Type::String ) ) + pushButton->setText( variant.asString() ); + else if ( variant.is( Variant::Type::cstr ) ) + pushButton->setText( variant.asCStr() ); + } + if ( col == getModel()->treeColumn() && getModel()->rowCount( index ) > 0 ) + pushButton->setIcon( getIndexMetadata( index ).open ? mExpandIcon : mContractIcon ); } - if ( col == getModel()->treeColumn() && getModel()->rowCount( index ) > 0 ) - widget->setIcon( getIndexMetadata( index ).open ? mExpandIcon : mContractIcon ); + return widget; } @@ -205,7 +206,13 @@ Sizef UITreeView::getContentSize() const { } void UITreeView::drawChilds() { - traverseTree( [&]( const ModelIndex& index, const size_t&, const Float& yOffset ) { + traverseTree( [&]( const ModelIndex& index, const size_t& indentLevel, const Float& yOffset ) { + if ( yOffset - mScrollOffset.y > mSize.getHeight() ) + return IterationDecision::Stop; + if ( yOffset - mScrollOffset.y + getRowHeight() < 0 ) + return IterationDecision::Continue; + for ( size_t colIndex = 0; colIndex < getModel()->columnCount(); colIndex++ ) + updateCell( index, colIndex, indentLevel, yOffset ); updateRow( getModel()->index( index.row(), getModel()->treeColumn(), index.parent() ), yOffset ) ->nodeDraw(); @@ -234,6 +241,10 @@ Node* UITreeView::overFind( const Vector2f& point ) { return pOver; traverseTree( [&, point]( const ModelIndex& index, const size_t&, const Float& yOffset ) { + if ( yOffset - mScrollOffset.y > mSize.getHeight() ) + return IterationDecision::Stop; + if ( yOffset - mScrollOffset.y + getRowHeight() < 0 ) + return IterationDecision::Continue; pOver = updateRow( getModel()->index( index.row(), getModel()->treeColumn(), index.parent() ), yOffset ) @@ -275,4 +286,27 @@ void UITreeView::setContractedIcon( Drawable* contractIcon ) { } } +void UITreeView::onColumnResizeToContent( const size_t& colIndex ) { + UIWidget* lWidget = nullptr; + Float lWidth = 0; + getUISceneNode()->setIsLoading( true ); + traverseTree( + [&, colIndex]( const ModelIndex& index, const size_t& indentLevel, const Float& yOffset ) { + UIWidget* widget = updateCell( index, colIndex, indentLevel, yOffset ); + if ( widget->isType( UI_TYPE_PUSHBUTTON ) ) { + Float w = widget->asType()->getContentSize().getWidth(); + if ( w > lWidth ) { + lWidget = widget; + lWidth = w; + } + } + return IterationDecision::Continue; + } ); + getUISceneNode()->setIsLoading( false ); + if ( lWidget ) { + columnData( colIndex ).width = lWidth; + createOrUpdateColumns(); + } +} + }} // namespace EE::UI diff --git a/src/tests/ui_perf_test/ui_perf_test.cpp b/src/tests/ui_perf_test/ui_perf_test.cpp index b71f41dd3..e626cf8b6 100644 --- a/src/tests/ui_perf_test/ui_perf_test.cpp +++ b/src/tests/ui_perf_test/ui_perf_test.cpp @@ -22,11 +22,15 @@ class TestModel : public Model { } }; + size_t getRows() const { return 10000; } + size_t getCols() const { return 4; } + size_t getChilds() const { return 50; } + TestModel() : Model() { - for ( size_t row = 0; row < 100; ++row ) { + for ( size_t row = 0; row < getRows(); ++row ) { NodeT* n = new NodeT(); n->parent = &mRoot; - for ( size_t i = 0; i < 4; i++ ) { + for ( size_t i = 0; i < getChilds(); i++ ) { NodeT* c = new NodeT(); c->parent = n; n->children.push_back( c ); @@ -38,7 +42,7 @@ class TestModel : public Model { virtual ModelIndex parentIndex( const ModelIndex& index ) const { if ( !index.isValid() ) return {}; - auto node = this->node( index ); + auto& node = this->node( index ); if ( !node.parent ) { eeASSERT( &node == &mRoot ); return {}; @@ -47,11 +51,11 @@ class TestModel : public Model { } virtual size_t rowCount( const ModelIndex& index = ModelIndex() ) const { - auto node = this->node( index ); + auto& node = this->node( index ); return node.children.size(); } - virtual size_t columnCount( const ModelIndex& index = ModelIndex() ) const { return 4; } + virtual size_t columnCount( const ModelIndex& = ModelIndex() ) const { return getCols(); } NodeT mRoot; const NodeT& node( const ModelIndex& index ) const { @@ -189,6 +193,7 @@ EE_MAIN_FUNC int main( int argc, char* argv[] ) { auto* vlay = UILinearLayout::NewVertical(); vlay->setLayoutSizePolicy( SizePolicy::MatchParent, SizePolicy::MatchParent ); + Clock clock; auto model = std::make_shared(); UITreeView* view = UITreeView::New(); view->setId( "treeview" ); @@ -197,6 +202,7 @@ EE_MAIN_FUNC int main( int argc, char* argv[] ) { view->setLayoutSizePolicy( SizePolicy::MatchParent, SizePolicy::MatchParent ); view->setParent( vlay ); view->setModel( model ); + eePRINTL( "Total time: %.2fms", clock.getElapsedTime().asMilliseconds() ); /* ListBox test */ /* std::vector strings; From 7accb1c2960b188d6793728460571332750e0f63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Sat, 11 Jul 2020 04:58:13 -0300 Subject: [PATCH 7/7] Several fixes and optimizations for the UITreeView. --- bin/assets/ui/breeze.css | 11 +- include/eepp/scene/event.hpp | 1 + include/eepp/ui/abstract/uiabstractview.hpp | 26 +- include/eepp/ui/uiscrollablewidget.hpp | 8 + include/eepp/ui/uitreeview.hpp | 19 +- src/eepp/ui/abstract/uiabstracttableview.cpp | 2 +- src/eepp/ui/abstract/uiabstractview.cpp | 16 + src/eepp/ui/uiscrollablewidget.cpp | 20 +- src/eepp/ui/uitreeview.cpp | 314 +++++++++++++++---- src/tests/ui_perf_test/ui_perf_test.cpp | 14 +- 10 files changed, 342 insertions(+), 89 deletions(-) diff --git a/bin/assets/ui/breeze.css b/bin/assets/ui/breeze.css index c93381611..71541faf4 100644 --- a/bin/assets/ui/breeze.css +++ b/bin/assets/ui/breeze.css @@ -759,20 +759,19 @@ table::header::column:hover { table::row { background-color: var(--list-back); - transition: all 0.5s; + transition: all 0.125s; } table::row:hover { background-color: var(--back); } -table::cell { - background-color: transparent; - /*transition: all 0.125s;*/ +table::row:selected { + background-color: var(--primary); } -table::cell:hover { - /*background-color: var(--primary);*/ +TreeView { + background-color: var(--list-back); } TreeView > ScrollBar { diff --git a/include/eepp/scene/event.hpp b/include/eepp/scene/event.hpp index 14b3c3b7d..53dff7265 100644 --- a/include/eepp/scene/event.hpp +++ b/include/eepp/scene/event.hpp @@ -73,6 +73,7 @@ class EE_API Event { OnSelectionChanged, OnNodeDropped, OnSave, + OnModelEvent, UserEvent, NoEvent = eeINDEX_NOT_FOUND }; diff --git a/include/eepp/ui/abstract/uiabstractview.hpp b/include/eepp/ui/abstract/uiabstractview.hpp index 78615dae4..5739af215 100644 --- a/include/eepp/ui/abstract/uiabstractview.hpp +++ b/include/eepp/ui/abstract/uiabstractview.hpp @@ -9,6 +9,20 @@ namespace EE { namespace UI { namespace Abstract { +class ModelEvent : public Event { + public: + ModelEvent( Model* model, const ModelIndex& index, Node* node ) : + Event( node, Event::OnModelEvent ), model( model ), index( index ) {} + + const Model* getModel() const { return model; } + + const ModelIndex& getModelIndex() const { return index; } + + protected: + const Model* model; + ModelIndex index; +}; + class EE_API UIAbstractView : public UIScrollableWidget { public: Uint32 getType() const; @@ -27,12 +41,16 @@ class EE_API UIAbstractView : public UIScrollableWidget { virtual void selectAll() = 0; - bool isEditable() const { return mEditable; } - - void setEditable( bool editable ) { mEditable = editable; } - void notifySelectionChange(); + std::function getOnSelectionChange() const; + + void setOnSelectionChange( const std::function& onSelectionChange ); + + std::function getOnSelection() const; + + void setOnSelection( const std::function& onSelection ); + protected: friend class Model; diff --git a/include/eepp/ui/uiscrollablewidget.hpp b/include/eepp/ui/uiscrollablewidget.hpp index 02961f639..76e40860e 100644 --- a/include/eepp/ui/uiscrollablewidget.hpp +++ b/include/eepp/ui/uiscrollablewidget.hpp @@ -38,6 +38,14 @@ class EE_API UIScrollableWidget : public UIWidget { virtual Sizef getContentSize() const = 0; + void scrollToTop(); + + void scrollToBottom(); + + Sizef getScrollableArea() const; + + Sizef getVisibleArea() const; + protected: ScrollViewType mViewType; ScrollBarMode mVScrollMode; diff --git a/include/eepp/ui/uitreeview.hpp b/include/eepp/ui/uitreeview.hpp index 7681e70bb..847db8964 100644 --- a/include/eepp/ui/uitreeview.hpp +++ b/include/eepp/ui/uitreeview.hpp @@ -9,6 +9,8 @@ using namespace EE::UI::Abstract; namespace EE { namespace UI { +class UITableRow; + class EE_API UITreeView : public UIAbstractTableView { public: static UITreeView* New(); @@ -58,21 +60,20 @@ class EE_API UITreeView : public UIAbstractTableView { template void traverseTree( Callback ) const; mutable std::map> mViewMetadata; - mutable std::map> mWidgets; - mutable std::map mRows; + mutable std::vector> mWidgets; + mutable std::vector mRows; virtual size_t getItemCount() const; UITreeView::MetadataForIndex& getIndexMetadata( const ModelIndex& index ) const; - UIWidget* getIndexWidget( const int& column, void* data ); - virtual void onColumnSizeChange( const size_t& colIndex ); - virtual UIWidget* updateCell( const ModelIndex& index, const size_t& col, + virtual UIWidget* updateCell( const int& rowIndex, const ModelIndex& index, const size_t& col, const size_t& indentLevel, const Float& yOffset ); - virtual UIWidget* updateRow( const ModelIndex& index, const Float& yOffset ); + virtual UIWidget* updateRow( const int& rowIndex, const ModelIndex& index, + const Float& yOffset ); virtual UIWidget* createCell( UIWidget* rowWidget, const ModelIndex& index, const size_t& col ); @@ -80,6 +81,12 @@ class EE_API UITreeView : public UIAbstractTableView { virtual void onColumnResizeToContent( const size_t& colIndex ); + virtual void onModelSelectionChange(); + + virtual Uint32 onKeyDown( const KeyEvent& event ); + + virtual void onOpenModelIndex( const ModelIndex& index ); + void updateContentSize(); }; diff --git a/src/eepp/ui/abstract/uiabstracttableview.cpp b/src/eepp/ui/abstract/uiabstracttableview.cpp index b25170c11..bf31207d9 100644 --- a/src/eepp/ui/abstract/uiabstracttableview.cpp +++ b/src/eepp/ui/abstract/uiabstracttableview.cpp @@ -116,7 +116,7 @@ void UIAbstractTableView::setHeadersVisible( bool visible ) { void UIAbstractTableView::onSizeChange() { UIAbstractView::onSizeChange(); - mHeader->setPixelsSize( mSize.getWidth(), getHeaderHeight() ); + createOrUpdateColumns(); } void UIAbstractTableView::onColumnSizeChange( const size_t& ) {} diff --git a/src/eepp/ui/abstract/uiabstractview.cpp b/src/eepp/ui/abstract/uiabstractview.cpp index ee8ddbfbe..12e8e311b 100644 --- a/src/eepp/ui/abstract/uiabstractview.cpp +++ b/src/eepp/ui/abstract/uiabstractview.cpp @@ -7,6 +7,22 @@ UIAbstractView::UIAbstractView( const std::string& tag ) : UIAbstractView::~UIAbstractView() {} +std::function UIAbstractView::getOnSelection() const { + return mOnSelection; +} + +void UIAbstractView::setOnSelection( const std::function& onSelection ) { + mOnSelection = onSelection; +} + +std::function UIAbstractView::getOnSelectionChange() const { + return mOnSelectionChange; +} + +void UIAbstractView::setOnSelectionChange( const std::function& onSelectionChange ) { + mOnSelectionChange = onSelectionChange; +} + Uint32 UIAbstractView::getType() const { return UI_TYPE_ABSTRACTVIEW; } diff --git a/src/eepp/ui/uiscrollablewidget.cpp b/src/eepp/ui/uiscrollablewidget.cpp index 134cf750d..0a16aef38 100644 --- a/src/eepp/ui/uiscrollablewidget.cpp +++ b/src/eepp/ui/uiscrollablewidget.cpp @@ -157,15 +157,23 @@ void UIScrollableWidget::onContentSizeChange() { updateScroll(); } -void UIScrollableWidget::updateScroll() { +Sizef UIScrollableWidget::getScrollableArea() const { Sizef contentSize( getContentSize() ); + Sizef size = getVisibleArea(); + return contentSize - size; +} + +Sizef UIScrollableWidget::getVisibleArea() const { Sizef size = getPixelsSize() - mRealPadding; if ( mVScroll->isVisible() ) size.x -= mVScroll->getPixelsSize().getWidth(); if ( mHScroll->isVisible() ) size.y -= mHScroll->getPixelsSize().getHeight(); - Sizef totalScroll( contentSize - size ); + return size; +} +void UIScrollableWidget::updateScroll() { + Sizef totalScroll = getScrollableArea(); Vector2f initScroll( mScrollOffset ); mScrollOffset = Vector2f::Zero; @@ -209,6 +217,14 @@ std::string UIScrollableWidget::getPropertyString( const PropertyDefinition* pro } } +void UIScrollableWidget::scrollToTop() { + mVScroll->setValue( 0 ); +} + +void UIScrollableWidget::scrollToBottom() { + mVScroll->setValue( 1 ); +} + bool UIScrollableWidget::applyProperty( const StyleSheetProperty& attribute ) { if ( !checkPropertyDefinition( attribute ) ) return false; diff --git a/src/eepp/ui/uitreeview.cpp b/src/eepp/ui/uitreeview.cpp index 9f3647b81..54b4958e6 100644 --- a/src/eepp/ui/uitreeview.cpp +++ b/src/eepp/ui/uitreeview.cpp @@ -39,23 +39,18 @@ UITreeView::MetadataForIndex& UITreeView::getIndexMetadata( const ModelIndex& in return *ref; } -UIWidget* UITreeView::getIndexWidget( const int& column, void* data ) { - auto it = mWidgets[column].find( data ); - if ( it != mWidgets[column].end() ) - return it->second; - return nullptr; -} - template void UITreeView::traverseTree( Callback callback ) const { eeASSERT( getModel() ); auto& model = *getModel(); int indentLevel = 1; Float yOffset = getHeaderHeight(); + int rowIndex = -1; std::function traverseIndex = [&]( const ModelIndex& index ) { if ( index.isValid() ) { auto& metadata = getIndexMetadata( index ); - IterationDecision decision = callback( index, indentLevel, yOffset ); + rowIndex++; + IterationDecision decision = callback( rowIndex, index, indentLevel, yOffset ); if ( decision == IterationDecision::Break || decision == IterationDecision::Stop ) return decision; yOffset += getRowHeight(); @@ -94,7 +89,7 @@ void UITreeView::createOrUpdateColumns() { size_t UITreeView::getItemCount() const { size_t count = 0; - traverseTree( [&]( const ModelIndex&, const size_t&, const Float& ) { + traverseTree( [&]( const int&, const ModelIndex&, const size_t&, const Float& ) { count++; return IterationDecision::Continue; } ); @@ -105,26 +100,65 @@ void UITreeView::onColumnSizeChange( const size_t& ) { updateContentSize(); } -UIWidget* UITreeView::updateRow( const ModelIndex& index, const Float& yOffset ) { - UIWidget* rowWidget = nullptr; - auto it = mRows.find( index.data() ); - if ( it == mRows.end() ) { - rowWidget = UIWidget::NewWithTag( "table::row" ); +class UITableRow : public UIWidget { + public: + UITableRow( const std::string& tag ) : UIWidget( tag ) {} + + ModelIndex getCurIndex() const { return mCurIndex; } + + void setCurIndex( const ModelIndex& curIndex ) { mCurIndex = curIndex; } + + protected: + virtual Uint32 onMessage( const NodeMessage* msg ) { + if ( msg->getMsg() == NodeMessage::MouseDown && ( msg->getFlags() & EE_BUTTON_LMASK ) && + ( !getEventDispatcher()->getMouseDownNode() || + getEventDispatcher()->getMouseDownNode() == this || + isParentOf( getEventDispatcher()->getMouseDownNode() ) ) && + getEventDispatcher()->getNodeDragging() == nullptr ) { + sendMouseEvent( Event::MouseDown, getEventDispatcher()->getMousePos(), + msg->getFlags() ); + } + return 0; + } + + ModelIndex mCurIndex; +}; + +UIWidget* UITreeView::updateRow( const int& rowIndex, const ModelIndex& index, + const Float& yOffset ) { + if ( rowIndex >= (int)mRows.size() ) + mRows.resize( rowIndex + 1, nullptr ); + UITableRow* rowWidget = nullptr; + if ( mRows[rowIndex] == nullptr ) { + rowWidget = eeNew( UITableRow, ( "table::row" ) ); rowWidget->clipEnable(); rowWidget->setParent( this ); rowWidget->setLayoutSizePolicy( SizePolicy::Fixed, SizePolicy::Fixed ); rowWidget->reloadStyle( true, true, true ); - mRows.insert( {index.data(), rowWidget} ); + rowWidget->addEventListener( Event::MouseDown, [&]( const Event* event ) { + if ( ( !getEventDispatcher()->getMouseDownNode() || + getEventDispatcher()->getMouseDownNode() == this || + isParentOf( getEventDispatcher()->getMouseDownNode() ) ) && + getEventDispatcher()->getNodeDragging() == nullptr ) { + getSelection().set( event->getNode()->asType()->getCurIndex() ); + } + } ); + mRows[rowIndex] = rowWidget; } else { - rowWidget = it->second; + rowWidget = mRows[rowIndex]; } + rowWidget->setCurIndex( index ); rowWidget->setPixelsSize( getContentSize().getWidth(), getRowHeight() ); rowWidget->setPixelsPosition( {-mScrollOffset.x, yOffset - mScrollOffset.y} ); + if ( getSelection().first() == index ) { + rowWidget->pushState( UIState::StateSelected ); + } else { + rowWidget->popState( UIState::StateSelected ); + } return rowWidget; } -UIWidget* UITreeView::createCell( UIWidget* rowWidget, const ModelIndex& index, - const size_t& col ) { +UIWidget* UITreeView::createCell( UIWidget* rowWidget, const ModelIndex&, const size_t& col ) { UIPushButton* widget = UIPushButton::NewWithTag( "table::cell" ); widget->setParent( rowWidget ); widget->unsetFlags( UI_AUTO_SIZE ); @@ -132,12 +166,33 @@ UIWidget* UITreeView::createCell( UIWidget* rowWidget, const ModelIndex& index, widget->setLayoutSizePolicy( SizePolicy::Fixed, SizePolicy::Fixed ); widget->asType()->setTextAlign( UI_HALIGN_LEFT ); if ( col == getModel()->treeColumn() ) { - widget->addEventListener( Event::MouseDoubleClick, [&, index]( const Event* event ) { + widget->addEventListener( Event::MouseDoubleClick, [&]( const Event* event ) { auto mouseEvent = static_cast( event ); + auto idx = mouseEvent->getNode()->getParent()->asType()->getCurIndex(); if ( mouseEvent->getFlags() & EE_BUTTON_LMASK ) { - auto& data = getIndexMetadata( index ); - data.open = !data.open; - createOrUpdateColumns(); + if ( getModel()->rowCount( idx ) ) { + auto& data = getIndexMetadata( idx ); + data.open = !data.open; + createOrUpdateColumns(); + } else { + onOpenModelIndex( idx ); + } + } + } ); + widget->addEventListener( Event::MouseClick, [&]( const Event* event ) { + auto mouseEvent = static_cast( event ); + UIImage* icon = mouseEvent->getNode()->asType()->getIcon(); + if ( icon ) { + Vector2f pos( icon->convertToNodeSpace( mouseEvent->getPosition().asFloat() ) ); + if ( pos >= Vector2f::Zero && pos <= icon->getPixelsSize() ) { + auto idx = + mouseEvent->getNode()->getParent()->asType()->getCurIndex(); + auto& data = getIndexMetadata( idx ); + if ( getModel()->rowCount( idx ) ) { + data.open = !data.open; + createOrUpdateColumns(); + } + } } } ); } @@ -156,14 +211,15 @@ void UITreeView::updateContentSize() { onContentSizeChange(); } -UIWidget* UITreeView::updateCell( const ModelIndex& index, const size_t& col, +UIWidget* UITreeView::updateCell( const int& rowIndex, const ModelIndex& index, const size_t& col, const size_t& indentLevel, const Float& yOffset ) { - auto* widget = getIndexWidget( col, index.data() ); + if ( rowIndex >= (int)mWidgets.size() ) + mWidgets.resize( rowIndex + 1 ); + auto* widget = mWidgets[rowIndex][col]; if ( !widget ) { - UIWidget* rowWidget = updateRow( - getModel()->index( index.row(), getModel()->treeColumn(), index.parent() ), yOffset ); + UIWidget* rowWidget = updateRow( rowIndex, index, yOffset ); widget = createCell( rowWidget, index, col ); - mWidgets[col].insert( {index.data(), widget} ); + mWidgets[rowIndex][col] = widget; } widget->setPixelsSize( columnData( col ).width, getRowHeight() ); widget->setPixelsPosition( {getColumnPosition( col ).x, 0} ); @@ -173,18 +229,22 @@ UIWidget* UITreeView::updateCell( const ModelIndex& index, const size_t& col, ModelIndex idx( getModel()->index( index.row(), col, index.parent() ) ); - Variant variant( getModel()->data( idx, Model::Role::Display ) ); + Variant txt( getModel()->data( idx, Model::Role::Display ) ); if ( widget->isType( UI_TYPE_PUSHBUTTON ) ) { UIPushButton* pushButton = widget->asType(); - if ( variant.isValid() ) { - if ( variant.is( Variant::Type::String ) ) - pushButton->setText( variant.asString() ); - else if ( variant.is( Variant::Type::cstr ) ) - pushButton->setText( variant.asCStr() ); + if ( txt.isValid() ) { + if ( txt.is( Variant::Type::String ) ) + pushButton->setText( txt.asString() ); + else if ( txt.is( Variant::Type::cstr ) ) + pushButton->setText( txt.asCStr() ); } - if ( col == getModel()->treeColumn() && getModel()->rowCount( index ) > 0 ) + pushButton->setIcon( nullptr ); + if ( col == getModel()->treeColumn() ) pushButton->setIcon( getIndexMetadata( index ).open ? mExpandIcon : mContractIcon ); + Variant icon( getModel()->data( idx, Model::Role::Icon ) ); + if ( icon.is( Variant::Type::Drawable ) ) + pushButton->setIcon( icon.asDrawable() ); } return widget; @@ -206,16 +266,15 @@ Sizef UITreeView::getContentSize() const { } void UITreeView::drawChilds() { - traverseTree( [&]( const ModelIndex& index, const size_t& indentLevel, const Float& yOffset ) { + traverseTree( [&]( const int& rowIndex, const ModelIndex& index, const size_t& indentLevel, + const Float& yOffset ) { if ( yOffset - mScrollOffset.y > mSize.getHeight() ) return IterationDecision::Stop; if ( yOffset - mScrollOffset.y + getRowHeight() < 0 ) return IterationDecision::Continue; for ( size_t colIndex = 0; colIndex < getModel()->columnCount(); colIndex++ ) - updateCell( index, colIndex, indentLevel, yOffset ); - updateRow( getModel()->index( index.row(), getModel()->treeColumn(), index.parent() ), - yOffset ) - ->nodeDraw(); + updateCell( rowIndex, index, colIndex, indentLevel, yOffset ); + updateRow( rowIndex, index, yOffset )->nodeDraw(); return IterationDecision::Continue; } ); if ( mHeader && mHeader->isVisible() ) @@ -239,20 +298,17 @@ Node* UITreeView::overFind( const Vector2f& point ) { return pOver; if ( mHeader && ( pOver = mHeader->overFind( point ) ) ) return pOver; - traverseTree( - [&, point]( const ModelIndex& index, const size_t&, const Float& yOffset ) { - if ( yOffset - mScrollOffset.y > mSize.getHeight() ) - return IterationDecision::Stop; - if ( yOffset - mScrollOffset.y + getRowHeight() < 0 ) - return IterationDecision::Continue; - pOver = updateRow( getModel()->index( index.row(), getModel()->treeColumn(), - index.parent() ), - yOffset ) - ->overFind( point ); - if ( pOver ) - return IterationDecision::Stop; + traverseTree( [&, point]( int rowIndex, const ModelIndex& index, const size_t&, + const Float& yOffset ) { + if ( yOffset - mScrollOffset.y > mSize.getHeight() ) + return IterationDecision::Stop; + if ( yOffset - mScrollOffset.y + getRowHeight() < 0 ) return IterationDecision::Continue; - } ); + pOver = updateRow( rowIndex, index, yOffset )->overFind( point ); + if ( pOver ) + return IterationDecision::Stop; + return IterationDecision::Continue; + } ); if ( !pOver ) pOver = this; } @@ -290,18 +346,18 @@ void UITreeView::onColumnResizeToContent( const size_t& colIndex ) { UIWidget* lWidget = nullptr; Float lWidth = 0; getUISceneNode()->setIsLoading( true ); - traverseTree( - [&, colIndex]( const ModelIndex& index, const size_t& indentLevel, const Float& yOffset ) { - UIWidget* widget = updateCell( index, colIndex, indentLevel, yOffset ); - if ( widget->isType( UI_TYPE_PUSHBUTTON ) ) { - Float w = widget->asType()->getContentSize().getWidth(); - if ( w > lWidth ) { - lWidget = widget; - lWidth = w; - } + traverseTree( [&, colIndex]( const int& rowIndex, const ModelIndex& index, + const size_t& indentLevel, const Float& yOffset ) { + UIWidget* widget = updateCell( rowIndex, index, colIndex, indentLevel, yOffset ); + if ( widget->isType( UI_TYPE_PUSHBUTTON ) ) { + Float w = widget->asType()->getContentSize().getWidth(); + if ( w > lWidth ) { + lWidget = widget; + lWidth = w; } - return IterationDecision::Continue; - } ); + } + return IterationDecision::Continue; + } ); getUISceneNode()->setIsLoading( false ); if ( lWidget ) { columnData( colIndex ).width = lWidth; @@ -309,4 +365,136 @@ void UITreeView::onColumnResizeToContent( const size_t& colIndex ) { } } +void UITreeView::onModelSelectionChange() { + UIAbstractTableView::onModelSelectionChange(); + invalidateDraw(); +} + +Uint32 UITreeView::onKeyDown( const KeyEvent& event ) { + if ( event.getMod() != 0 ) + return 0; + + auto curIndex = getSelection().first(); + + switch ( event.getKeyCode() ) { + case KEY_UP: { + ModelIndex prevIndex; + ModelIndex foundIndex; + Float curY; + traverseTree( + [&]( const int&, const ModelIndex& index, const size_t&, const Float& offsetY ) { + if ( index == curIndex ) { + foundIndex = prevIndex; + curY = offsetY; + return IterationDecision::Break; + } + prevIndex = index; + return IterationDecision::Continue; + } ); + if ( foundIndex.isValid() ) { + getSelection().set( foundIndex ); + if ( curY < mScrollOffset.y + getHeaderHeight() + getRowHeight() || + curY > mScrollOffset.y + getPixelsSize().getHeight() - mRealPadding.Top - + mRealPadding.Bottom - getRowHeight() ) { + curY -= getHeaderHeight() + getRowHeight(); + mVScroll->setValue( eemin( + 1.f, eemax( 0.f, curY / getScrollableArea().getHeight() ) ) ); + } + } + break; + } + case KEY_DOWN: { + ModelIndex prevIndex; + ModelIndex foundIndex; + Float curY; + traverseTree( + [&]( const int&, const ModelIndex& index, const size_t&, const Float& offsetY ) { + if ( prevIndex == curIndex ) { + foundIndex = index; + curY = offsetY; + return IterationDecision::Break; + } + prevIndex = index; + return IterationDecision::Continue; + } ); + if ( foundIndex.isValid() ) { + getSelection().set( foundIndex ); + if ( curY < mScrollOffset.y || + curY > mScrollOffset.y + getPixelsSize().getHeight() - mRealPadding.Top - + mRealPadding.Bottom - getRowHeight() ) { + curY -= + eefloor( getVisibleArea().getHeight() / getRowHeight() ) * getRowHeight() - + getRowHeight(); + mVScroll->setValue( + eemin( 1.f, curY / getScrollableArea().getHeight() ) ); + } + break; + } + } + case KEY_END: { + scrollToBottom(); + ModelIndex lastIndex; + traverseTree( [&]( const int&, const ModelIndex& index, const size_t&, const Float& ) { + lastIndex = index; + return IterationDecision::Continue; + } ); + getSelection().set( lastIndex ); + break; + } + case KEY_HOME: { + scrollToTop(); + getSelection().set( getModel()->index( 0, 0 ) ); + break; + } + case KEY_RIGHT: { + if ( curIndex.isValid() && getModel()->rowCount( curIndex ) ) { + auto& metadata = getIndexMetadata( curIndex ); + if ( !metadata.open ) { + metadata.open = true; + createOrUpdateColumns(); + return 0; + } + getSelection().set( getModel()->index( 0, getModel()->treeColumn(), curIndex ) ); + } + break; + } + case KEY_LEFT: { + if ( curIndex.isValid() && getModel()->rowCount( curIndex ) ) { + auto& metadata = getIndexMetadata( curIndex ); + if ( metadata.open ) { + metadata.open = false; + createOrUpdateColumns(); + return 0; + } + } + if ( curIndex.isValid() && curIndex.parent().isValid() ) { + getSelection().set( curIndex.parent() ); + return 0; + } + break; + } + case KEY_RETURN: + case KEY_SPACE: { + if ( curIndex.isValid() ) { + if ( getModel()->rowCount( curIndex ) ) { + auto& metadata = getIndexMetadata( curIndex ); + metadata.open = !metadata.open; + createOrUpdateColumns(); + } else { + onOpenModelIndex( curIndex ); + } + } + break; + } + default: + break; + } + return 0; +} + +void UITreeView::onOpenModelIndex( const ModelIndex& index ) { + ModelEvent event( getModel(), index, this ); + sendEvent( &event ); +} + }} // namespace EE::UI diff --git a/src/tests/ui_perf_test/ui_perf_test.cpp b/src/tests/ui_perf_test/ui_perf_test.cpp index e626cf8b6..860b01701 100644 --- a/src/tests/ui_perf_test/ui_perf_test.cpp +++ b/src/tests/ui_perf_test/ui_perf_test.cpp @@ -83,13 +83,12 @@ class TestModel : public Model { return Variant( String::format( "Test %lld-%lld", index.row(), index.column() ) ); } case Role::Icon: { - if ( index.column() == 0 ) { - return Variant( - SceneManager::instance() - ->getUISceneNode() - ->getUIIconThemeManager() - ->getCurrentTheme() - ->getIcon( node( index ).children.size() ? "folder-open" : "folder" ) ); + if ( index.column() == 0 && rowCount( index ) == 0 ) { + return Variant( SceneManager::instance() + ->getUISceneNode() + ->getUIIconThemeManager() + ->getCurrentTheme() + ->getIcon( "file" ) ); } } default: { @@ -167,6 +166,7 @@ EE_MAIN_FUNC int main( int argc, char* argv[] ) { Drawable* open = addIcon( "folder-open", 0xed70, 16 ); addIcon( "tree-expanded", 0xea50, 24 ); addIcon( "tree-contracted", 0xea54, 24 ); + addIcon( "file", 0xecc3, 24 ); UISceneNode* uiSceneNode = UISceneNode::New(); SceneManager::instance()->add( uiSceneNode ); uiSceneNode->getUIThemeManager()->setDefaultFont( font );