diff --git a/.hgignore b/.hgignore index 63a143739..56dbef0ce 100644 --- a/.hgignore +++ b/.hgignore @@ -32,6 +32,7 @@ eestrobe* eeiv* eehttp-request* eepp-TextureAtlasEditor* +eepp-UIEditor* ee.tag log.log external_projects.lua diff --git a/premake4.lua b/premake4.lua index fbe46ea2f..e9b832e64 100644 --- a/premake4.lua +++ b/premake4.lua @@ -881,6 +881,32 @@ solution "eepp" files { "src/thirdparty/imageresampler/*.cpp" } build_base_cpp_configuration( "imageresampler" ) + project "efsw-static" + kind "StaticLib" + language "C++" + set_targetdir("libs/" .. os.get_real() .. "/thirdparty/") + includedirs { "src/thirdparty/efsw/include", "src/thirdparty/efsw/src" } + + if os.is("windows") then + osfiles = "src/thirdparty/efsw/src/efsw/platform/win/*.cpp" + else + osfiles = "src/thirdparty/efsw/src/efsw/platform/posix/*.cpp" + end + + files { "src/thirdparty/efsw/src/efsw/*.cpp", osfiles } + + if os.is("windows") then + 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" } + elseif os.is("linux") then + 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" } + elseif os.is("macosx") then + 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" } + elseif os.is("freebsd") then + 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" } + end + + build_base_cpp_configuration( "efsw" ) + project "eepp-main" kind "StaticLib" language "C++" @@ -967,6 +993,19 @@ solution "eepp" files { "src/tools/mapeditor/*.cpp" } build_link_configuration( "eepp-MapEditor", true ) + project "eepp-uieditor" + set_kind() + language "C++" + includedirs { "src/thirdparty/efsw/include" } + + if not os.is("windows") and not os.is("haiku") then + links { "pthread" } + end + + links { "efsw-static" } + files { "src/tools/uieditor/*.cpp" } + build_link_configuration( "eepp-UIEditor", true ) + if os.isfile("external_projects.lua") then dofile("external_projects.lua") end diff --git a/projects/linux/ee.creator.user b/projects/linux/ee.creator.user index 980666474..f5eaa70e1 100644 --- a/projects/linux/ee.creator.user +++ b/projects/linux/ee.creator.user @@ -1,6 +1,6 @@ - + EnvironmentId @@ -72,7 +72,7 @@ {6d057187-158a-4883-8d5b-d470a6b6b025} 10 0 - 0 + 15 ../../make/linux @@ -1365,6 +1365,62 @@ false false + + false + false + 0 + + true + + false + false + false + false + true + 0.01 + 10 + true + 1 + 25 + + 1 + true + false + true + valgrind + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + 2 + + + %{buildDir}../../../bin/eepp-UIEditor-debug + false + %{buildDir}../../../bin/ + Run %{buildDir}../../../bin/eepp-UIEditor-debug + eeuieditor-debug + ProjectExplorer.CustomExecutableRunConfiguration + 3768 + true + false + false + false + false + false false @@ -1813,7 +1869,7 @@ false true - 15 + 16 diff --git a/projects/linux/ee.files b/projects/linux/ee.files index 4b3d68bb8..12f2669d1 100644 --- a/projects/linux/ee.files +++ b/projects/linux/ee.files @@ -1,3 +1,4 @@ +../../src/tools/uieditor/uieditor.cpp ../../include/eepp/scene.hpp ../../include/eepp/scene/actions/close.hpp ../../include/eepp/scene/event.hpp diff --git a/projects/linux/ee.includes b/projects/linux/ee.includes index 630938ff1..c59b18676 100644 --- a/projects/linux/ee.includes +++ b/projects/linux/ee.includes @@ -2,3 +2,4 @@ ../../include/ ../../src/thirdparty ../../include/eepp/thirdparty +../../src/thirdparty/efsw/include diff --git a/src/tools/uieditor/uieditor.cpp b/src/tools/uieditor/uieditor.cpp new file mode 100644 index 000000000..0d79da002 --- /dev/null +++ b/src/tools/uieditor/uieditor.cpp @@ -0,0 +1,321 @@ +#include +#include + +EE::Window::Window * window = NULL; +UIMessageBox * MsgBox = NULL; +efsw::FileWatcher * fileWatcher = NULL; +UITheme * theme = NULL; +UIWidget * uiContainer = NULL; +UIWinMenu * uiWinMenu = NULL; +DirectoryPack * directoryPack = NULL; +UISceneNode * uiSceneNode = NULL; +std::string currentLayout; +bool updateLayout = false; +Clock waitClock; +efsw::WatchID watch = 0; + +class UpdateListener : public efsw::FileWatchListener +{ +public: + UpdateListener() {} + + void handleFileAction( efsw::WatchID watchid, const std::string& dir, const std::string& filename, efsw::Action action, std::string oldFilename = "" ) + { + if ( action == efsw::Actions::Modified ) { + std::cout << "DIR (" << dir << ") FILE (" << filename << ") has event Modified" << std::endl; + + if ( dir + filename == currentLayout ) + { + updateLayout = true; + waitClock.restart(); + } + } + } +}; + +UpdateListener * listener = NULL; + +static bool isImage( const std::string& path ) { + std::string mPath = path; + + if ( path.size() >= 4 ) + { + std::string File = mPath.substr( mPath.find_last_of("/\\") + 1 ); + std::string Ext = File.substr( File.find_last_of(".") + 1 ); + String::toLowerInPlace( Ext ); + + if ( Ext == "png" || Ext == "tga" || Ext == "bmp" || Ext == "jpg" || Ext == "gif" || Ext == "jpeg" || Ext == "dds" || Ext == "psd" || Ext == "hdr" || Ext == "pic" || Ext == "pvr" || Ext == "pkm" ) + return true; + } + + return false; +} + +static bool isFont( const std::string& path ) { + std::string mPath = path; + + if ( path.size() >= 4 ) + { + std::string File = mPath.substr( mPath.find_last_of("/\\") + 1 ); + std::string Ext = File.substr( File.find_last_of(".") + 1 ); + String::toLowerInPlace( Ext ); + + if ( Ext == "ttf" || Ext == "otf" || Ext == "wolff" ) + return true; + } + + return false; +} + +static void loadImage( std::string path ) +{ + std::string filename( FileSystem::fileRemoveExtension( FileSystem::fileNameFromPath( path ) ) ); + + GlobalTextureAtlas::instance()->add( TextureFactory::instance()->loadFromFile( path ), filename ); +} + +static void loadFont( std::string path ) +{ + std::string filename( FileSystem::fileRemoveExtension( FileSystem::fileNameFromPath( path ) ) ); + FontTrueType * font = FontTrueType::New( filename ); + + font->loadFromFile( path ); +} + +static void loadImagesFromFolder( std::string folderPath ) +{ + std::vector files = FileSystem::filesGetInPath( folderPath ); + + FileSystem::dirPathAddSlashAtEnd( folderPath ); + + for ( auto it = files.begin(); it != files.end(); ++it ) + { + if ( isImage( *it ) ) + { + loadImage( folderPath + (*it) ); + } + } +} + +static void loadFontsFromFolder( std::string folderPath ) +{ + std::vector files = FileSystem::filesGetInPath( folderPath ); + + FileSystem::dirPathAddSlashAtEnd( folderPath ); + + for ( auto it = files.begin(); it != files.end(); ++it ) + { + if ( isFont( *it ) ) + { + loadFont( folderPath + (*it) ); + } + } +} + +static void loadLayoutFromFile( std::string file ) +{ + if ( watch != 0 ) + { + fileWatcher->removeWatch( watch ); + } + + std::string folder( FileSystem::fileRemoveFileName( file ) ); + + watch = fileWatcher->addWatch( folder, listener ); + + Float pd = PixelDensity::getPixelDensity(); + uiContainer->childsCloseAll(); + PixelDensity::setPixelDensity(1); + uiSceneNode->loadLayoutFromFile( file, uiContainer ); + PixelDensity::setPixelDensity(pd); + + currentLayout = file; +} + +static void refreshLayout() { + if ( !currentLayout.empty() && FileSystem::fileExists( currentLayout ) && uiContainer != NULL ) { + loadLayoutFromFile( currentLayout ); + } + + updateLayout = false; +} + +bool onCloseRequestCallback( EE::Window::Window * w ) { + MsgBox = UIMessageBox::New( MSGBOX_OKCANCEL, "Do you really want to close the current file?\nAll changes will be lost." ); + MsgBox->addEventListener( Event::MsgBoxConfirmClick, cb::Make1( []( const Event * event ) { window->close(); } ) ); + MsgBox->addEventListener( Event::OnClose, cb::Make1( []( const Event * event ) { MsgBox = NULL; } ) ); + MsgBox->setTitle( "Close Editor?" ); + MsgBox->center(); + MsgBox->show(); + return false; +} + +void mainLoop() { + window->getInput()->update(); + + if ( window->getInput()->isKeyUp( KEY_ESCAPE ) && NULL == MsgBox && onCloseRequestCallback( window ) ) { + window->close(); + } + + if ( updateLayout && waitClock.getElapsedTime().asMilliseconds() > 250.f ) { + refreshLayout(); + } + + SceneManager::instance()->update(); + + if ( SceneManager::instance()->getUISceneNode()->invalidated() ) { + window->clear(); + + SceneManager::instance()->draw(); + + window->display(); + } else { + Sys::sleep( Milliseconds(8) ); + } +} + +void resizeCb(EE::Window::Window * window) +{ + Float scaleW = (Float)window->getSize().getWidth() / (Float)uiContainer->getSize().getWidth(); + Float scaleH = (Float)window->getSize().getHeight() / (Float)uiContainer->getSize().getHeight(); + + uiContainer->setScale( scaleW < scaleH ? scaleW : scaleH ); + uiContainer->center(); +} + +void imagePathOpen( const Event * Event ) { + UICommonDialog * CDL = reinterpret_cast ( Event->getNode() ); + + loadImagesFromFolder( CDL->getFullPath() ); +} + +void fontPathOpen( const Event * Event ) { + UICommonDialog * CDL = reinterpret_cast ( Event->getNode() ); + + loadFontsFromFolder( CDL->getFullPath() ); +} + +void layoutOpen( const Event * Event ) { + UICommonDialog * CDL = reinterpret_cast ( Event->getNode() ); + + Float pd = PixelDensity::getPixelDensity(); + PixelDensity::setPixelDensity(1); + uiSceneNode->loadLayoutFromFile( CDL->getFullPath(), uiContainer ); + PixelDensity::setPixelDensity(pd); +} + +void fileMenuClick( const Event * Event ) { + if ( !Event->getNode()->isType( UI_TYPE_MENUITEM ) ) + return; + + const String& txt = reinterpret_cast ( Event->getNode() )->getText(); + + if ( "Open..." == txt ) { + UICommonDialog * TGDialog = UICommonDialog::New( UI_CDL_DEFAULT_FLAGS, "*.xml" ); + TGDialog->setWinFlags( UI_WIN_DEFAULT_FLAGS | UI_WIN_MAXIMIZE_BUTTON | UI_WIN_MODAL ); + TGDialog->setTitle( "Open layout..." ); + TGDialog->addEventListener( Event::OpenFile, cb::Make1( layoutOpen ) ); + TGDialog->center(); + TGDialog->show(); + } else if ( "Close" == txt ) { + currentLayout = ""; + uiContainer->childsCloseAll(); + } else if ( "Quit" == txt ) { + onCloseRequestCallback( window ); + } else if ( "Load images from path..." == txt ) { + UICommonDialog * TGDialog = UICommonDialog::New( UI_CDL_DEFAULT_FLAGS | CDL_FLAG_ALLOW_FOLDER_SELECT ); + TGDialog->setWinFlags( UI_WIN_DEFAULT_FLAGS | UI_WIN_MAXIMIZE_BUTTON | UI_WIN_MODAL ); + TGDialog->setTitle( "Open images from folder..." ); + TGDialog->addEventListener( Event::OpenFile, cb::Make1( imagePathOpen ) ); + TGDialog->center(); + TGDialog->show(); + } else if ( "Load fonts from path..." == txt ) { + UICommonDialog * TGDialog = UICommonDialog::New( UI_CDL_DEFAULT_FLAGS | CDL_FLAG_ALLOW_FOLDER_SELECT ); + TGDialog->setWinFlags( UI_WIN_DEFAULT_FLAGS | UI_WIN_MAXIMIZE_BUTTON | UI_WIN_MODAL ); + TGDialog->setTitle( "Open fonts from folder..." ); + TGDialog->addEventListener( Event::OpenFile, cb::Make1( fontPathOpen ) ); + TGDialog->center(); + TGDialog->show(); + } +} + +EE_MAIN_FUNC int main (int argc, char * argv []) { + directoryPack = eeNew( DirectoryPack, () ); + directoryPack->create( Sys::getProcessPath() ); + + fileWatcher = new efsw::FileWatcher(); + listener = new UpdateListener(); + fileWatcher->watch(); + + Display * currentDisplay = Engine::instance()->getDisplayManager()->getDisplayIndex(0); + Float pixelDensity = 1; + DisplayMode currentMode = currentDisplay->getCurrentMode(); + + Uint32 width = eemin( currentMode.Width, (Uint32)( 1280 * pixelDensity ) ); + Uint32 height = eemin( currentMode.Height, (Uint32)( 720 * pixelDensity ) ); + + window = Engine::instance()->createWindow( WindowSettings( width, height, "eepp - UI Editor", WindowStyle::Default, WindowBackend::Default, 32, "assets/icon/ee.png" ), ContextSettings( true, GLv_default, true, 24, 1, 0, false ) ); + + if ( window->isOpen() ) { + window->setCloseRequestCallback( cb::Make1( onCloseRequestCallback ) ); + + uiSceneNode = UISceneNode::New(); + SceneManager::instance()->add( uiSceneNode ); + + uiSceneNode->enableDrawInvalidation(); + + { + std::string pd; + if ( PixelDensity::getPixelDensity() >= 1.5f ) pd = "1.5x"; + else if ( PixelDensity::getPixelDensity() >= 2.f ) pd = "2x"; + + TextureAtlasLoader tgl( "assets/ui/uitheme" + pd + ".eta" ); + + theme = UITheme::loadFromTextureAtlas( UIThemeDefault::New( "uitheme" + pd, "uitheme" + pd ), TextureAtlasManager::instance()->getByName( "uitheme" + pd ) ); + + FontTrueType * font = FontTrueType::New( "NotoSans-Regular", "assets/fonts/NotoSans-Regular.ttf" ); + + UIThemeManager::instance()->setDefaultEffectsEnabled( true )->setDefaultTheme( theme )->setDefaultFont( font )->add( theme ); + } + + uiContainer = UIWidget::New(); + uiContainer->setId( "appContainer" )->setSize( 1200, 1920 ); + uiContainer->clipDisable(); + + uiWinMenu = UIWinMenu::New(); + + UIPopUpMenu * uiPopMenu = UIPopUpMenu::New(); + uiPopMenu->add( "Open...", theme->getIconByName( "document-open" ) ); + uiPopMenu->addSeparator(); + uiPopMenu->add( "Close", theme->getIconByName( "document-close" ) ); + uiPopMenu->addSeparator(); + uiPopMenu->add( "Quit", theme->getIconByName( "quit" ) ); + uiWinMenu->addMenuButton( "File", uiPopMenu ); + uiPopMenu->addEventListener( Event::OnItemClicked, cb::Make1( fileMenuClick ) ); + + UIPopUpMenu * uiResourceMenu = UIPopUpMenu::New(); + uiResourceMenu->add( "Load images from path...", theme->getIconByName( "document-open" ) ); + uiResourceMenu->addSeparator(); + uiResourceMenu->add( "Load fonts from path...", theme->getIconByName( "document-open" ) ); + uiWinMenu->addMenuButton( "Resources", uiResourceMenu ); + uiResourceMenu->addEventListener( Event::OnItemClicked, cb::Make1( fileMenuClick ) ); + + resizeCb( window ); + + window->pushResizeCallback( cb::Make1( resizeCb ) ); + + window->runMainLoop( &mainLoop ); + } + + eeSAFE_DELETE( directoryPack ); + + Engine::destroySingleton(); + + MemoryManager::showResults(); + + delete fileWatcher; + + delete listener; + + return EXIT_SUCCESS; +}