diff --git a/.vscode/settings.json b/.vscode/settings.json index 2c89588..787a758 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,7 @@ { "files.associations": { - "utility": "cpp" + "utility": "cpp", + "__locale": "cpp", + "*.inc": "cpp" } } \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 566448f..5539d97 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,13 +18,15 @@ set(CMAKE_CXX_STANDARD 17) add_executable(${PROJECT_NAME} app/main.cpp + app/AppDelegate.cpp ) +setup_project() + target_link_libraries(${PROJECT_NAME} PRIVATE UIKit ) -setup_project() #list(APPEND google_angle_libs # ${EXTERN_PATH}/angle/mac/libEGL.dylib diff --git a/Submodules/UIKit/CMakeLists.txt b/Submodules/UIKit/CMakeLists.txt index 35c1870..3395fe2 100644 --- a/Submodules/UIKit/CMakeLists.txt +++ b/Submodules/UIKit/CMakeLists.txt @@ -5,12 +5,25 @@ add_definitions( ) add_library(UIKit + lib/ContentsGravityTransformation.cpp lib/platforms/SkiaCtx.cpp lib/Application.cpp + lib/DispatchQueue.cpp + lib/Timer.cpp lib/Geometry.cpp - lib/NXLayer.cpp - lib/NXColor.cpp + lib/CGImage.cpp + lib/CADisplayLink.cpp + lib/CALayer.cpp + lib/UIApplication.cpp + lib/UIApplicationDelegateImpl.cpp + lib/UIApplicationMain.cpp + lib/UIColor.cpp + lib/UIImage.cpp lib/UIView.cpp + lib/UIWindow.cpp + lib/NXAffineTransform.cpp + lib/NXTransform3D.cpp + lib/NXData.cpp ) # APPLE @@ -55,6 +68,7 @@ endif () target_include_directories(UIKit PUBLIC ${EXTERN_PATH}/SDL/include ${EXTERN_PATH}/skia + ${EXTERN_PATH}/libromfs/lib/include include lib ) diff --git a/Submodules/UIKit/include/CADisplayLink.h b/Submodules/UIKit/include/CADisplayLink.h new file mode 100644 index 0000000..3f62b11 --- /dev/null +++ b/Submodules/UIKit/include/CADisplayLink.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include +#include + +namespace NXKit { + + class CADisplayLink { + public: + explicit CADisplayLink(std::function func); + ~CADisplayLink(); + + void invalidate(); + private: + bool isRunning = true; + std::function func; + static std::vector activeLinks; + friend class DispatchQueue; + }; + +} \ No newline at end of file diff --git a/Submodules/UIKit/include/CALayer.h b/Submodules/UIKit/include/CALayer.h new file mode 100644 index 0000000..d922393 --- /dev/null +++ b/Submodules/UIKit/include/CALayer.h @@ -0,0 +1,110 @@ +#pragma once + +#include "include/core/SkCanvas.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace NXKit { + +class CALayer: public enable_shared_from_this { +public: + CALayer(); + ~CALayer() {} + + // Getter Setters + void setContentsGravity(CALayerContentsGravity contentsGravity) { _contentsGravity = contentsGravity; } + [[nodiscard]] CALayerContentsGravity contentsGravity() const { return _contentsGravity; } + + void setContentsScale(NXFloat contentsScale) { _contentsScale = contentsScale; } + [[nodiscard]] NXFloat contentsScale() const { return _contentsScale; } + + void setAnchorPoint(NXPoint anchorPoint); + [[nodiscard]] NXPoint anchorPoint() const { return _anchorPoint; } + + void setOpacity(NXFloat opacity); + [[nodiscard]] NXFloat opacity() const { return _opacity; } + + void setBounds(NXRect bounds); + [[nodiscard]] NXRect bounds() const { return _bounds; } + + void setPosition(NXPoint position); + [[nodiscard]] NXPoint position() const { return _position; } + + void setZPosition(NXFloat position); + [[nodiscard]] NXFloat zPosition() { return _zPosition; } + + void setBackgroundColor(std::optional backgroundColor); + [[nodiscard]] std::optional backgroundColor() const { return _backgroundColor; } + + void setCornerRadius(NXFloat cornerRadius); + [[nodiscard]] NXFloat cornerRadius() const { return _cornerRadius; } + + void setTransform(NXTransform3D transform); + [[nodiscard]] NXTransform3D transform() const { return _transform; } + + void setMask(std::shared_ptr mask); + [[nodiscard]] std::shared_ptr mask() const { return _mask; } + + void setContents(std::shared_ptr contents); + [[nodiscard]] std::shared_ptr contents() { return _contents; } + + NXRect getFrame(); + void setFrame(NXRect frame); + + // Layers + [[nodiscard]] std::vector> sublayers() { return _sublayers; } + + void addSublayer(const std::shared_ptr& layer); + void insertSublayerAt(const std::shared_ptr& layer, int index); + void insertSublayerAbove(const std::shared_ptr& layer, const std::shared_ptr& sibling); + void insertSublayerBelow(const std::shared_ptr& layer, const std::shared_ptr& sibling); + + void removeFromSuperlayer(); + + void onWillSet(std::string keyPath); + + void skiaRender(SkCanvas* canvas); +private: + + /// Defaults to 1.0 but if the layer is associated with a view, + /// the view sets this value to match the screen. + NXFloat _contentsScale = 1.0f; + CALayerContentsGravity _contentsGravity = CALayerContentsGravity::resize; + + std::weak_ptr _superlayer; + std::vector> _sublayers; + std::shared_ptr _mask; + + std::shared_ptr _contents; + + // Animatable + NXPoint _anchorPoint = NXPoint(0.5, 0.5); + NXPoint _position; + NXFloat _opacity = 1; + NXFloat _zPosition = 0.0; + NXRect _bounds; + NXFloat _cornerRadius = 0; + std::optional _backgroundColor; + NXTransform3D _transform = NXTransform3D::identity; + + /** + Indicates whether a layer somewhere has changed since the last render pass. + + The current implementation of this is quite simple and doesn't check whether the layer is actually in + the layer hierarchy or not. In theory this means that we're wasting render passes if users frequently + update layers that aren't in the tree. In practice it's not expected that UIKit users would do that + often enough for us to care about it. + **/ + static bool layerTreeIsDirty; +}; + +} diff --git a/Submodules/UIKit/include/CGImage.h b/Submodules/UIKit/include/CGImage.h new file mode 100644 index 0000000..1f86bb9 --- /dev/null +++ b/Submodules/UIKit/include/CGImage.h @@ -0,0 +1,29 @@ +#pragma once + +#include +//#include +#include +#include +#include +#include + +#include "include/core/SkImage.h" + +namespace NXKit { + +class CGImage { +public: + sk_sp pointee; + +// CGImage(NXSize size); + CGImage(const NXData& sourceData); +// CGImage(SDL_Surface* surface); + CGImage(sk_sp image, std::optional sourceData); + CGImage(sk_sp image): CGImage(image, std::nullopt) {} + ~CGImage(); + + [[nodiscard]] NXSize size() const; +private: + std::optional sourceData; +}; +} diff --git a/Submodules/UIKit/include/ContentsGravityTransformation.h b/Submodules/UIKit/include/ContentsGravityTransformation.h new file mode 100644 index 0000000..2cd3ee1 --- /dev/null +++ b/Submodules/UIKit/include/ContentsGravityTransformation.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +namespace NXKit { + +enum class CALayerContentsGravity { + bottom, bottomLeft, bottomRight, + center, left, right, + top, topLeft, topRight, + resize, resizeAspect, resizeAspectFill +}; + +class CALayer; +struct ContentsGravityTransformation { + NXPoint offset; + NXSize scale; + + ContentsGravityTransformation(CALayer* layer); + +private: + NXSize defaultScale = NXSize(1.0, 1.0); +}; + +} + diff --git a/Submodules/UIKit/include/DispatchQueue.h b/Submodules/UIKit/include/DispatchQueue.h new file mode 100644 index 0000000..fb23ea4 --- /dev/null +++ b/Submodules/UIKit/include/DispatchQueue.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include +#include +#include +#include + +#ifdef main +#undef main +#endif + +namespace NXKit { + +class DispatchQueue { +public: + DispatchQueue(const std::string& tag); + ~DispatchQueue(); + + static std::shared_ptr main(); + static std::shared_ptr global(); + + std::string tag() const { return _tag; } + void async(const std::function& task); + + bool isActive() { return _task_loop_active; } +private: + static std::shared_ptr _main; + static std::shared_ptr _global; + std::queue> _queue; + std::string _tag; + + pthread_t _task_loop_thread = nullptr; + std::mutex _m_async_mutex; + volatile bool _task_loop_active = true; + static void* task_loop(void* a); + + void performAll(); + + friend bool applicationRunLoop(); +}; + +} diff --git a/Submodules/UIKit/include/Geometry.h b/Submodules/UIKit/include/Geometry.h index 0a83f28..e70f799 100644 --- a/Submodules/UIKit/include/Geometry.h +++ b/Submodules/UIKit/include/Geometry.h @@ -4,6 +4,9 @@ namespace NXKit { typedef double NXFloat; +struct NXAffineTransform; +struct NXTransform3D; + struct NXPoint { NXFloat x, y; @@ -18,10 +21,12 @@ struct NXPoint { NXPoint operator/(const NXFloat& rhs); NXPoint operator*(const NXFloat& rhs); -// Point applying(const NXAffineTransform& t) const; + NXPoint applying(const NXAffineTransform& t) const; bool valid(); NXFloat magnitude(); + + static NXPoint zero; }; struct NXSize { @@ -71,8 +76,8 @@ struct NXRect { NXRect& offsetBy(const NXPoint& offset); NXRect& offsetBy(const NXFloat& offsetX, const NXFloat& offsetY); // NXRect& insetBy(const UIEdgeInsets& insets); -// NXRect applying(const NXAffineTransform& t) const; -// NXRect applying(const NXTransform3D& t) const; + NXRect applying(const NXAffineTransform& t) const; + NXRect applying(const NXTransform3D& t) const; bool operator==(const NXRect& rhs); @@ -82,4 +87,4 @@ struct NXRect { static NXRect null; }; -} \ No newline at end of file +} diff --git a/Submodules/UIKit/include/NXAffineTransform.h b/Submodules/UIKit/include/NXAffineTransform.h new file mode 100644 index 0000000..8e877af --- /dev/null +++ b/Submodules/UIKit/include/NXAffineTransform.h @@ -0,0 +1,36 @@ +// +// NXAffineTransform.h +// NXKit +// +// Created by Даниил Виноградов on 19.07.2022. +// + +#pragma once + +#include "Geometry.h" +#include + +namespace NXKit { + +struct NXAffineTransform { +public: + float m11, m12, m21, m22, tX, tY; + + NXAffineTransform(); + NXAffineTransform(NXFloat m11, NXFloat m12, NXFloat m21, NXFloat m22, NXFloat tX, NXFloat tY); + + static NXAffineTransform translationBy(NXFloat x, NXFloat y); + static NXAffineTransform scaleBy(NXFloat x, NXFloat y); + static NXAffineTransform scale(NXFloat factor); + static NXAffineTransform rotationBy(NXFloat angle); + + std::optional inverted() const; + bool isIdentity() const; + + static const NXAffineTransform identity; + + bool operator==(const NXAffineTransform& rhs) const; + NXAffineTransform operator*(const NXAffineTransform& rhs) const; +}; + +} \ No newline at end of file diff --git a/Submodules/UIKit/include/NXColor.h b/Submodules/UIKit/include/NXColor.h deleted file mode 100644 index 0cf2af6..0000000 --- a/Submodules/UIKit/include/NXColor.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -namespace NXKit { - -class NXColor { -public: - NXColor(); - NXColor(unsigned char red, unsigned char green, unsigned char blue); - NXColor(unsigned char red, unsigned char green, unsigned char blue, unsigned char alpha); - - unsigned char r(); - unsigned char g(); - unsigned char b(); - unsigned char a(); - -private: - friend class NXLayer; - int color; -}; - -} \ No newline at end of file diff --git a/Submodules/UIKit/include/NXData.h b/Submodules/UIKit/include/NXData.h new file mode 100644 index 0000000..24e6b59 --- /dev/null +++ b/Submodules/UIKit/include/NXData.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include +#include + +namespace NXKit { + +class NXData { +public: + int count() const; + uint8_t* data() const; + + NXData(uint8_t* bytes, int count, bool freeSource = false); + ~NXData(); + + static std::optional fromPath(const std::string& path); +private: + std::vector _data; +}; + +} diff --git a/Submodules/UIKit/include/NXLayer.h b/Submodules/UIKit/include/NXLayer.h deleted file mode 100644 index 91e645c..0000000 --- a/Submodules/UIKit/include/NXLayer.h +++ /dev/null @@ -1,70 +0,0 @@ -#pragma once - -#include "include/core/SkCanvas.h" - -#include -#include - -#include -#include -#include - -namespace NXKit { - -class NXLayer: public enable_shared_from_this { -public: - NXLayer(); - ~NXLayer() {} - - // Getter Setters - void setAnchorPoint(NXPoint anchorPoint); - [[nodiscard]] NXPoint anchorPoint() const { return _anchorPoint; } - - void setBounds(NXRect bounds); - [[nodiscard]] NXRect bounds() const { return _bounds; } - - void setPosition(NXPoint position); - [[nodiscard]] NXPoint position() const { return _position; } - - void setBackgroundColor(std::optional backgroundColor); - [[nodiscard]] std::optional backgroundColor() const { return _backgroundColor; } - - void setCornerRadius(NXFloat cornerRadius); - [[nodiscard]] NXFloat cornerRadius() const { return _cornerRadius; } - - - // Layers - [[nodiscard]] std::vector> sublayers() { return _sublayers; } - - void addSublayer(const std::shared_ptr& layer); - void insertSublayerAt(const std::shared_ptr& layer, int index); - void insertSublayerAbove(const std::shared_ptr& layer, const std::shared_ptr& sibling); - void insertSublayerBelow(const std::shared_ptr& layer, const std::shared_ptr& sibling); - - void removeFromSuperlayer(); - - void skiaRender(SkCanvas* canvas); -private: - - std::weak_ptr superlayer; - std::vector> _sublayers; - - // Animatable - NXPoint _anchorPoint = NXPoint(0.5f, 0.5f); - NXPoint _position; - NXRect _bounds; - NXFloat _cornerRadius = 0; - std::optional _backgroundColor; - - /** - Indicates whether a layer somewhere has changed since the last render pass. - - The current implementation of this is quite simple and doesn't check whether the layer is actually in - the layer hierarchy or not. In theory this means that we're wasting render passes if users frequently - update layers that aren't in the tree. In practice it's not expected that UIKit users would do that - often enough for us to care about it. - **/ - static bool layerTreeIsDirty; -}; - -} diff --git a/Submodules/UIKit/include/NXSize.h b/Submodules/UIKit/include/NXSize.h deleted file mode 100644 index e69de29..0000000 diff --git a/Submodules/UIKit/include/NXTransform3D.h b/Submodules/UIKit/include/NXTransform3D.h new file mode 100644 index 0000000..0127089 --- /dev/null +++ b/Submodules/UIKit/include/NXTransform3D.h @@ -0,0 +1,70 @@ +// +// NXTransform3D.hpp +// NXKit +// +// Created by Даниил Виноградов on 19.08.2022. +// + +#pragma once + +#include "Geometry.h" +#include +#include "include/core/SkM44.h" + +namespace NXKit { + +struct Vector3 { + NXFloat x, y, z; +}; + +struct NXTransform3D { +public: + NXFloat m11; NXFloat m12; NXFloat m13; NXFloat m14; + NXFloat m21; NXFloat m22; NXFloat m23; NXFloat m24; + NXFloat m31; NXFloat m32; NXFloat m33; NXFloat m34; + NXFloat m41; NXFloat m42; NXFloat m43; NXFloat m44; + + NXTransform3D(NXFloat m11, NXFloat m12, NXFloat m13, NXFloat m14, + NXFloat m21, NXFloat m22, NXFloat m23, NXFloat m24, + NXFloat m31, NXFloat m32, NXFloat m33, NXFloat m34, + NXFloat m41, NXFloat m42, NXFloat m43, NXFloat m44); + NXTransform3D(); + NXTransform3D(NXFloat* unsafePointer); + + NXTransform3D translationBy(NXFloat x, NXFloat y, NXFloat z) const; + NXTransform3D scaleBy(NXFloat x, NXFloat y, NXFloat z) const; + NXTransform3D scale(NXFloat factor) const; + NXTransform3D rotationBy(NXFloat angle, NXFloat x, NXFloat y, NXFloat z) const; + + NXTransform3D concat(const NXTransform3D& other) const; + bool operator==(const NXTransform3D& rhs) const; + NXTransform3D operator+(const NXTransform3D& rhs) const; + NXTransform3D operator-(const NXTransform3D& rhs) const; + NXTransform3D operator*(const NXTransform3D& first) const; + NXTransform3D operator*(const NXFloat& first) const; + NXTransform3D interpolateTo(const NXTransform3D& matrix, const NXFloat& progress) const; + + Vector3 transformingVector(NXFloat x, NXFloat y, NXFloat z) const; + + static const NXTransform3D identity; + + void setAsSDLgpuMatrix() const; + + SkM44 toSkM44() const; +}; + +const NXTransform3D NXTransform3DIdentity = NXTransform3D(1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); + +bool NXTransform3DEqualToTransform(const NXTransform3D& a, const NXTransform3D& b); + +NXTransform3D NXTransform3DMakeAffineTransform(NXAffineTransform transform); +NXTransform3D CATransform3DMakeTranslation(NXFloat tx, NXFloat ty, NXFloat tz); +NXTransform3D CATransform3DMakeScale(NXFloat sx, NXFloat sy, NXFloat sz); +NXTransform3D CATransform3DMakeRotation(NXFloat angle, NXFloat x, NXFloat y, NXFloat z); +NXTransform3D CATransform3DConcat(const NXTransform3D& a, const NXTransform3D& b); +NXAffineTransform NXTransform3DGetAffineTransform(NXTransform3D t); + +} diff --git a/Submodules/UIKit/include/SkiaCtx.h b/Submodules/UIKit/include/SkiaCtx.h index ffba3b6..3f7b98c 100644 --- a/Submodules/UIKit/include/SkiaCtx.h +++ b/Submodules/UIKit/include/SkiaCtx.h @@ -1,7 +1,5 @@ #pragma once -#include - #include #include #include "include/core/SkSurface.h" @@ -25,9 +23,14 @@ class SkiaCtx { virtual float getScaleFactor() { return 1; } virtual sk_sp getFontMgr() { return fontMgr; } + static std::shared_ptr main() { return _main; } + static std::shared_ptr _main; protected: NXSize _size; sk_sp fontMgr; + +private: + friend class UIApplication; }; std::unique_ptr MakeSkiaCtx(); diff --git a/Submodules/UIKit/include/Timer.h b/Submodules/UIKit/include/Timer.h new file mode 100644 index 0000000..13285ef --- /dev/null +++ b/Submodules/UIKit/include/Timer.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +namespace NXKit { + +double timevalInMilliseconds(timeval time); +double timevalInSeconds(timeval time); + +struct Timer { + timeval startTime; + + Timer(double startingAt = 0); + + double getElapsedTimeInMilliseconds(); + double getElapsedTimeInSeconds(); + + double operator-(const Timer& rhs) const; + + friend bool operator== (const Timer& c1, const Timer& c2); + friend bool operator!= (const Timer& c1, const Timer& c2); + + friend bool operator< (const Timer& c1, const Timer& c2); + friend bool operator> (const Timer& c1, const Timer& c2); + + friend bool operator<= (const Timer& c1, const Timer& c2); + friend bool operator>= (const Timer& c1, const Timer& c2); +}; + +} diff --git a/Submodules/UIKit/include/UIApplication.h b/Submodules/UIKit/include/UIApplication.h new file mode 100644 index 0000000..17c9059 --- /dev/null +++ b/Submodules/UIKit/include/UIApplication.h @@ -0,0 +1,36 @@ +#pragma once + +#include + +#include +#include + +namespace NXKit { + +class UIApplication { +public: + static std::shared_ptr shared; + + UIApplication(); + + std::shared_ptr delegate; + std::weak_ptr keyWindow; + + void handleEventsIfNeeded(); + void handleSDLQuit(); + + // void sendEvent(std::shared_ptr event); + +// TODO: Need to remove +// static GPU_Target* currentRenderer; + +private: + static void onWillEnterForeground(); + static void onDidEnterForeground(); + static void onWillEnterBackground(); + static void onDidEnterBackground(); + + void handleSDLEvent(SDL_Event e); +}; + +} \ No newline at end of file diff --git a/Submodules/UIKit/include/UIApplicationDelegate.h b/Submodules/UIKit/include/UIApplicationDelegate.h new file mode 100644 index 0000000..69fe290 --- /dev/null +++ b/Submodules/UIKit/include/UIApplicationDelegate.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include +#include + +#define REGISTER_NIB(n) UINib::registerXIB(#n, n::init); + +namespace NXKit { + +class UIApplication; +class UIApplicationDelegate { +public: + std::shared_ptr window; + + virtual bool applicationDidFinishLaunchingWithOptions(UIApplication* application, std::map launchOptions); + virtual void applicationWillTerminate(UIApplication* application) {} + + virtual void applicationWillEnterForeground(UIApplication* application) {} + virtual void applicationDidBecomeActive(UIApplication* application) {} + + virtual void applicationWillResignActive(UIApplication* application) {} + virtual void applicationDidEnterBackground(UIApplication* application) {} + + // virtual void applicationNeedsXIBRegistration(UIApplication* application); +}; + +} diff --git a/Submodules/UIKit/include/UIApplicationMain.h b/Submodules/UIKit/include/UIApplicationMain.h new file mode 100644 index 0000000..b234213 --- /dev/null +++ b/Submodules/UIKit/include/UIApplicationMain.h @@ -0,0 +1,10 @@ +#pragma once +#include +#include + +namespace NXKit { + +class UIApplicationDelegate; +int UIApplicationMain(std::shared_ptr appDelegate = new_shared()); + +} diff --git a/Submodules/UIKit/include/UIColor.h b/Submodules/UIKit/include/UIColor.h new file mode 100644 index 0000000..4d1d961 --- /dev/null +++ b/Submodules/UIKit/include/UIColor.h @@ -0,0 +1,39 @@ +#pragma once + +namespace NXKit { + +class UIColor { +public: + UIColor(); + UIColor(unsigned char red, unsigned char green, unsigned char blue); + UIColor(unsigned char red, unsigned char green, unsigned char blue, unsigned char alpha); + + unsigned char r(); + unsigned char g(); + unsigned char b(); + unsigned char a(); + + bool operator==(const UIColor& rhs) const; + + static UIColor clear; + static UIColor red; + static UIColor green; + static UIColor blue; + static UIColor orange; + static UIColor cyan; + static UIColor white; + static UIColor black; + static UIColor gray; + + static UIColor separator; + static UIColor systemBackground; + static UIColor secondarySystemBackground; + static UIColor tetriarySystemBackground; + static UIColor tint; + +private: + friend class CALayer; + int color; +}; + +} diff --git a/Submodules/UIKit/include/UIImage.h b/Submodules/UIKit/include/UIImage.h new file mode 100644 index 0000000..7956640 --- /dev/null +++ b/Submodules/UIKit/include/UIImage.h @@ -0,0 +1,11 @@ +#pragma once + +namespace NXKit { + +class UIImage { +public: + UIImage(); +private: +}; + +} \ No newline at end of file diff --git a/Submodules/UIKit/include/UIView.h b/Submodules/UIKit/include/UIView.h index 6ccbc20..27c3932 100644 --- a/Submodules/UIKit/include/UIView.h +++ b/Submodules/UIKit/include/UIView.h @@ -1,6 +1,6 @@ #pragma once -#include "NXLayer.h" +#include "CALayer.h" namespace NXKit { @@ -18,11 +18,11 @@ class UIView: public enable_shared_from_this { const std::vector>& subviews() const { return _subviews; } std::weak_ptr superview() const { return _superview; } - std::shared_ptr layer() const { return _layer; }; + std::shared_ptr layer() const { return _layer; }; private: std::vector> _subviews; std::weak_ptr _superview; - std::shared_ptr _layer; + std::shared_ptr _layer; void setSuperview(std::shared_ptr superview); }; diff --git a/Submodules/UIKit/include/UIWindow.h b/Submodules/UIKit/include/UIWindow.h new file mode 100644 index 0000000..db6cee0 --- /dev/null +++ b/Submodules/UIKit/include/UIWindow.h @@ -0,0 +1,13 @@ +#pragma once + +#include "UIView.h" + +namespace NXKit { + +class UIWindow: public UIView { +public: + void makeKeyAndVisible(); +private: +}; + +} \ No newline at end of file diff --git a/Submodules/UIKit/lib/Application.cpp b/Submodules/UIKit/lib/Application.cpp index b2a5f8d..4e7600c 100644 --- a/Submodules/UIKit/lib/Application.cpp +++ b/Submodules/UIKit/lib/Application.cpp @@ -40,15 +40,15 @@ Application::Application() { typeface = skiaCtx->getFontMgr()->matchFamilyStyle(nullptr, style); keyWindow = new_shared(); - keyWindow->layer()->setPosition({ 200, 200 }); - keyWindow->layer()->setBounds({0, 0, 150, 150}); - keyWindow->layer()->setBackgroundColor(NXColor(255, 255, 0)); - - auto sublayer = new_shared(); - sublayer->setPosition({ 220, 170 }); - sublayer->setBounds({0, 0, 250, 190}); - sublayer->setBackgroundColor(NXColor(0, 255, 0)); + keyWindow->layer()->setBackgroundColor(UIColor::systemBackground); + + auto sublayer = new_shared(); + sublayer->setPosition({ 100, 100 }); + sublayer->setBounds({0, 0, 200, 44}); + sublayer->setBackgroundColor(UIColor::tint); sublayer->setCornerRadius(10); + sublayer->setAnchorPoint(NXPoint::zero); +// sublayer->setTransform(NXTransform3D::identity.rotationBy(45, 0, 0, 1)); keyWindow->layer()->addSublayer(sublayer); @@ -73,7 +73,7 @@ void Application::render() { if (!surface) return; auto canvas = surface->getCanvas(); - canvas->clear(SK_ColorCYAN); + canvas->clear(SK_ColorTRANSPARENT); // SkPaint paint; // paint.setColor(SK_ColorRED); @@ -201,7 +201,12 @@ void Application::render() { // //// ----------------------- + canvas->save(); + auto scale = skiaCtx->getScaleFactor(); + canvas->scale(scale, scale); + keyWindow->layer()->setBounds({ NXPoint::zero, skiaCtx->getSize() } ); keyWindow->layer()->skiaRender(canvas); + canvas->restore(); skiaCtx->flushAndSubmit(surface); skiaCtx->swapBuffers(); diff --git a/Submodules/UIKit/lib/CADisplayLink.cpp b/Submodules/UIKit/lib/CADisplayLink.cpp new file mode 100644 index 0000000..26074f0 --- /dev/null +++ b/Submodules/UIKit/lib/CADisplayLink.cpp @@ -0,0 +1,21 @@ +#include +#include + +namespace NXKit { + std::vector CADisplayLink::activeLinks; + + CADisplayLink::CADisplayLink(std::function func): func(std::move(func)) { + activeLinks.push_back(this); + } + + CADisplayLink::~CADisplayLink() { + invalidate(); + } + + void CADisplayLink::invalidate() { + if (!isRunning) return; + + isRunning = false; + activeLinks.erase(std::remove(activeLinks.begin(), activeLinks.end(), this), activeLinks.end()); + } +} \ No newline at end of file diff --git a/Submodules/UIKit/lib/CALayer.cpp b/Submodules/UIKit/lib/CALayer.cpp new file mode 100644 index 0000000..d335612 --- /dev/null +++ b/Submodules/UIKit/lib/CALayer.cpp @@ -0,0 +1,203 @@ +// +// Created by Даниил Виноградов on 07.12.2024. +// + +#include "CALayer.h" +#include "include/core/SkRRect.h" + +using namespace NXKit; + +bool CALayer::layerTreeIsDirty = true; + +CALayer::CALayer() = default; + +void CALayer::setAnchorPoint(NXKit::NXPoint anchorPoint) { + if (_anchorPoint == anchorPoint) return; + onWillSet("anchorPoint"); + _anchorPoint = anchorPoint; +} + +void CALayer::setBackgroundColor(std::optional backgroundColor) { + if (_backgroundColor == backgroundColor) return; + onWillSet("backgroundColor"); + _backgroundColor = backgroundColor; +} + +void CALayer::setOpacity(NXFloat opacity) { + if (_opacity == opacity) return; + onWillSet("opacity"); + _opacity = opacity; +} + +void CALayer::setBounds(NXKit::NXRect bounds) { + if (_bounds == bounds) return; + onWillSet("bounds"); + _bounds = bounds; +} + +void CALayer::setPosition(NXKit::NXPoint position) { + if (_position == position) return; + onWillSet("position"); + _position = position; +} + +void CALayer::setZPosition(NXKit::NXFloat zPosition) { + if (_zPosition == zPosition) return; + onWillSet("zPosition"); + _zPosition = zPosition; +} + +void CALayer::setCornerRadius(NXFloat cornerRadius) { + if (_cornerRadius == cornerRadius) return; + onWillSet("cornerRadius"); + _cornerRadius = cornerRadius; +} + +void CALayer::setTransform(NXTransform3D transform) { + if (_transform == transform) return; + onWillSet("transform"); + _transform = transform; +} + +void CALayer::setContents(std::shared_ptr contents) { + _contents = contents; + CALayer::layerTreeIsDirty = true; +} + +void CALayer::setMask(std::shared_ptr mask) { + if (this->_mask) { + this->_mask->_superlayer.reset(); + } + + this->_mask = mask; + if (mask) _mask->_superlayer = shared_from_this(); +} + +void CALayer::addSublayer(const std::shared_ptr& layer) { + layer->removeFromSuperlayer(); + _sublayers.push_back(layer); + layer->_superlayer = this->shared_from_this(); + CALayer::layerTreeIsDirty = true; +} + +void CALayer::insertSublayerAt(const std::shared_ptr& layer, int index) { + layer->removeFromSuperlayer(); + _sublayers.insert(_sublayers.begin() + index, layer); + layer->_superlayer = this->shared_from_this(); + CALayer::layerTreeIsDirty = true; +} + +void CALayer::insertSublayerAbove(const std::shared_ptr& layer, const std::shared_ptr& sibling) { + // TODO: Need to implement +} + +void CALayer::insertSublayerBelow(const std::shared_ptr& layer, const std::shared_ptr& sibling) { + auto itr = std::find(_sublayers.cbegin(), _sublayers.cend(), sibling); + if (itr == _sublayers.cend()) { return; } + + layer->removeFromSuperlayer(); + _sublayers.insert(itr, layer); + layer->_superlayer = this->shared_from_this(); + CALayer::layerTreeIsDirty = true; +} + +void CALayer::removeFromSuperlayer() { + auto super = _superlayer.lock(); + if (super == nullptr) return; + + // If it's mask - remove + if (super->_mask.get() == this) { + super->_mask = nullptr; + return; + } + + // Find and remove this from superlayer + super->_sublayers.erase(std::remove(super->_sublayers.begin(), super->_sublayers.end(), shared_from_this()), super->_sublayers.end()); + CALayer::layerTreeIsDirty = true; +} + +void CALayer::skiaRender(SkCanvas* canvas) { + canvas->save(); + + auto matrix = CATransform3DMakeTranslation(_position.x, _position.y, _zPosition) + .concat(_transform); + + // Set Origin matrix + canvas->concat(matrix.toSkM44()); + canvas->save(); + + // Set Anchor matrix + canvas->concat(CATransform3DMakeTranslation(-_bounds.width() * _anchorPoint.x, -_bounds.height() * _anchorPoint.y, 0).toSkM44()); + + SkPaint paint; + paint.setAntiAlias(true); + SkRect rect = SkRect::MakeXYWH(0, 0, _bounds.width(), _bounds.height()); + + // Background color + if (_backgroundColor.has_value()) { + paint.setColor(_backgroundColor->color); + + SkRRect rrect; + float radii = _cornerRadius; + SkVector corners[] = {{radii, radii}, {radii, radii}, {radii, radii}, {radii, radii}}; + rrect.setRectRadii(rect, corners); + canvas->drawRRect(rrect, paint); + } + + // Content + if (_contents) { + auto contentsGravity = ContentsGravityTransformation(this); + canvas->drawImageRect(_contents.get()->pointee, rect, SkSamplingOptions()); +// GPU_SetAnchor(_contents->pointee, _anchorPoint.x, _anchorPoint.y); +// GPU_SetRGBA(_contents->pointee, 255, 255, 255, _opacity * 255); +// +// GPU_BlitTransform( +// _contents->pointee, +// NULL, +// renderer, +// contentsGravity.offset.x, +// contentsGravity.offset.y, +// 0, // rotation in degrees +// contentsGravity.scale.width / contentsScale, +// contentsGravity.scale.height / contentsScale +// ); + } + + // Reset Anchor to Origin matrix + canvas->restore(); + for (const auto& sublayer: _sublayers) { + sublayer->skiaRender(canvas); + } + + canvas->restore(); +} + +NXRect CALayer::getFrame() { + // Create a rectangle based on `bounds.size` * `transform` at `position` offset by `anchorPoint` + auto transformedBounds = _bounds.applying(_transform); + + auto anchorPointOffset = NXPoint( + transformedBounds.width() * _anchorPoint.x, + transformedBounds.height() * _anchorPoint.y + ); + + return NXRect(_position.x - anchorPointOffset.x, + _position.y - anchorPointOffset.y, + transformedBounds.width(), + transformedBounds.height()); +} + +void CALayer::onWillSet(std::string keyPath) { + CALayer::layerTreeIsDirty = true; + auto animationKey = keyPath; + +// auto animation = std::static_pointer_cast(actionForKey(animationKey)); +// if (animation && +// (this->hasBeenRenderedInThisPartOfOverallLayerHierarchy +// || animation->wasCreatedInUIAnimateBlock()) && +// !this->isPresentationForAnotherLayer && +// !CATransaction::disableActions()) +// { +// add(animation, animationKey); +// } +} diff --git a/Submodules/UIKit/lib/CGImage.cpp b/Submodules/UIKit/lib/CGImage.cpp new file mode 100644 index 0000000..46a5216 --- /dev/null +++ b/Submodules/UIKit/lib/CGImage.cpp @@ -0,0 +1,48 @@ +#include "CGImage.h" +#include "include/core/SkData.h" + +using namespace NXKit; + +//#include +//#include + +//CGImage::CGImage(NXSize size) { +// pointee = SkImages:: +//// GPU_SetAnchor(pointee, 0, 0); +//// GPU_SetBlendMode(pointee, GPU_BLEND_NORMAL_FACTOR_ALPHA); +//} + +CGImage::CGImage(sk_sp image, std::optional sourceData) { + this->sourceData = std::move(sourceData); + pointee = image; + +// GPU_SetSnapMode(pointee, GPU_SNAP_POSITION_AND_DIMENSIONS); +// GPU_SetBlendMode(pointee, GPU_BLEND_NORMAL_FACTOR_ALPHA); +// GPU_SetImageFilter(pointee, GPU_FILTER_LINEAR); +} + +CGImage::CGImage(const NXData& sourceData) { + auto data = sourceData; + auto dataCount = data.count(); + + auto skData = SkData::MakeFromMalloc(data.data(), data.count()); + auto image = SkImages::DeferredFromEncodedData(skData); + + new (this) CGImage(image, data); +} + +//CGImage::CGImage(SDL_Surface* surface) { +// auto pointer = GPU_CopyImageFromSurface(surface); +// if (!pointer) { return; } +// +// new (this) CGImage(pointer, std::nullopt); +//} + +CGImage::~CGImage() { +// GPU_FreeTarget(pointee->target); +// GPU_FreeImage(pointee); +} + +NXSize CGImage::size() const { + return NXSize(pointee->width(), pointee->height()); +} diff --git a/Submodules/UIKit/lib/ContentsGravityTransformation.cpp b/Submodules/UIKit/lib/ContentsGravityTransformation.cpp new file mode 100644 index 0000000..1de6a65 --- /dev/null +++ b/Submodules/UIKit/lib/ContentsGravityTransformation.cpp @@ -0,0 +1,92 @@ +// +// ContentsGravityTransformation.cpp +// SDLTest +// +// Created by Даниил Виноградов on 26.02.2023. +// + +#include +#include + +namespace NXKit { + +ContentsGravityTransformation::ContentsGravityTransformation(CALayer* layer) { + auto scaledContents = NXSize( + layer->contents()->size().width / layer->contentsScale(), + layer->contents()->size().height / layer->contentsScale() + ); + + auto bounds = layer->bounds(); + + auto distanceToMinX = -((bounds.width() - scaledContents.width) * layer->anchorPoint().x); + auto distanceToMinY = -((bounds.height() - scaledContents.height) * layer->anchorPoint().y); + auto distanceToMaxX = (bounds.width() - scaledContents.width) * (1 - layer->anchorPoint().x); + auto distanceToMaxY = (bounds.height() - scaledContents.height) * (1 - layer->anchorPoint().y); + + switch (layer->contentsGravity()) { + case CALayerContentsGravity::resize: { + this->offset = NXPoint::zero; + scale = NXSize(bounds.width() / scaledContents.width, bounds.height() / scaledContents.height); + break; + } + case CALayerContentsGravity::resizeAspectFill: { + offset = NXPoint::zero; + auto maxScale = std::max(bounds.width() / scaledContents.width, bounds.height() / scaledContents.height); + scale = NXSize(maxScale, maxScale); + break; + } + case CALayerContentsGravity::resizeAspect: { + offset = NXPoint::zero; + auto minScale = std::min(bounds.width() / scaledContents.width, bounds.height() / scaledContents.height); + scale = NXSize(minScale, minScale); + break; + } + case CALayerContentsGravity::center: { + offset = NXPoint::zero; + scale = defaultScale; + break; + } + case CALayerContentsGravity::left: { + offset = NXPoint(distanceToMinX, 0.0); + scale = defaultScale; + break; + } + case CALayerContentsGravity::right: { + offset = NXPoint(distanceToMaxX, 0.0); + scale = defaultScale; + break; + } + case CALayerContentsGravity::top: { + offset = NXPoint(0.0, distanceToMinY); + scale = defaultScale; + break; + } + case CALayerContentsGravity::bottom: { + offset = NXPoint(0.0, distanceToMaxY); + scale = defaultScale; + break; + } + case CALayerContentsGravity::topLeft: { + offset = NXPoint(distanceToMinX, distanceToMinY); + scale = defaultScale; + break; + } + case CALayerContentsGravity::topRight: { + offset = NXPoint(distanceToMaxX, distanceToMinY); + scale = defaultScale; + break; + } + case CALayerContentsGravity::bottomLeft: { + offset = NXPoint(distanceToMinX, distanceToMaxY); + scale = defaultScale; + break; + } + case CALayerContentsGravity::bottomRight: { + offset = NXPoint(distanceToMaxX, distanceToMaxY); + scale = defaultScale; + break; + } + } +} + +} diff --git a/Submodules/UIKit/lib/DispatchQueue.cpp b/Submodules/UIKit/lib/DispatchQueue.cpp new file mode 100644 index 0000000..3d23bbc --- /dev/null +++ b/Submodules/UIKit/lib/DispatchQueue.cpp @@ -0,0 +1,76 @@ +// +// DispatchQueue.cpp +// SDLTest +// +// Created by Даниил Виноградов on 10.03.2023. +// + +#include +#include +#include + #include + #include + +namespace NXKit { + +std::shared_ptr DispatchQueue::_main = nullptr; +std::shared_ptr DispatchQueue::_global = nullptr; + +std::shared_ptr DispatchQueue::main() { + if (!_main) _main = new_shared("main"); + return _main; +} + +std::shared_ptr DispatchQueue::global() { + if (!_global) _global = new_shared("global"); + return _global; +} + +DispatchQueue::DispatchQueue(const std::string& tag): _tag(tag) { + if (tag != "main") { + pthread_create(&_task_loop_thread, nullptr, task_loop, this); + } +} + +DispatchQueue::~DispatchQueue() { + _task_loop_active = false; + if (_tag != "main") { + pthread_join(_task_loop_thread, nullptr); + } +} + +void DispatchQueue::async(const std::function& task) { + std::lock_guard guard(_m_async_mutex); + _queue.push(task); +} + +void DispatchQueue::performAll() { + std::vector linksCopy = CADisplayLink::activeLinks; + for (auto link: linksCopy) { + if (link->isRunning) + link->func(); + } + + std::queue> copy; + { + std::lock_guard guard(_m_async_mutex); + std::swap( _queue, copy ); + } + + while (!copy.empty()) { + auto task = copy.front(); + copy.pop(); + task(); + } +} + +void* DispatchQueue::task_loop(void* a) { + auto self = (DispatchQueue*) a; + while (self->_task_loop_active) { + self->performAll(); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } + return nullptr; +} + +} diff --git a/Submodules/UIKit/lib/Geometry.cpp b/Submodules/UIKit/lib/Geometry.cpp index 67822a4..6c8533d 100644 --- a/Submodules/UIKit/lib/Geometry.cpp +++ b/Submodules/UIKit/lib/Geometry.cpp @@ -1,9 +1,35 @@ #include #include +#include "NXAffineTransform.h" +#include "NXTransform3D.h" + using namespace NXKit; +// MARK: - PRIVATE - +float min(NXFloat a, NXFloat b, NXFloat c, NXFloat d) { + auto minValue = (a < b) ? a : b; + minValue = (minValue < c) ? minValue : c; + minValue = (minValue < d) ? minValue : d; + return minValue; +} + +float max(NXFloat a, NXFloat b, NXFloat c, NXFloat d) { + auto maxValue = (a > b) ? a : b; + maxValue = (maxValue > c) ? maxValue : c; + maxValue = (maxValue > d) ? maxValue : d; + return maxValue; +} + +float isEqual(NXFloat val1, NXFloat val2) { + if (isnan(val1) && isnan(val2)) + return true; + return val1 == val2; +} + // MARK: - POINT - +NXPoint NXPoint::zero = NXPoint(); + NXPoint::NXPoint(): x(0), y(0) { } NXPoint::NXPoint(NXFloat x, NXFloat y): x(x), y(y) { } @@ -45,12 +71,12 @@ NXPoint NXPoint::operator*(const NXFloat& rhs) { return res; } -//NXPoint NXPoint::applying(const NXAffineTransform& t) const { -// return NXPoint( -// x * t.m11 + y * t.m21 + t.tX, -// x * t.m12 + y * t.m22 + t.tY -// ); -//} +NXPoint NXPoint::applying(const NXAffineTransform& t) const { + return NXPoint( + x * t.m11 + y * t.m21 + t.tX, + x * t.m12 + y * t.m22 + t.tY + ); +} bool NXPoint::valid() { return !isnan(this->x) && !isnan(this->y); @@ -65,7 +91,7 @@ NXSize::NXSize(): NXSize(0, 0) {} NXSize::NXSize(NXFloat width, NXFloat height): width(width), height(height) {} bool NXSize::operator==(const NXSize& rhs) const { - return this->width == rhs.width && this->height == rhs.height; + return isEqual(this->width, rhs.width) && isEqual(this->height, rhs.height); } NXSize NXSize::operator+(const NXSize& first) const { @@ -176,49 +202,49 @@ NXRect& NXRect::offsetBy(const NXFloat& offsetX, const NXFloat& offsetY) { // return *this; //} -//NXRect NXRect::applying(const NXAffineTransform& t) const { -// if (t.isIdentity()) { return *this; } -// -// auto newTopLeft = NXPoint(minX(), minY()).applying(t); -// auto newTopRight = NXPoint(maxX(), minY()).applying(t); -// auto newBottomLeft = NXPoint(minX(), maxY()).applying(t); -// auto newBottomRight = NXPoint(maxX(), maxY()).applying(t); -// -// -// auto newMinX = min(newTopLeft.x, newTopRight.x, newBottomLeft.x, newBottomRight.x); -// auto newMaxX = max(newTopLeft.x, newTopRight.x, newBottomLeft.x, newBottomRight.x); -// -// auto newMinY = min(newTopLeft.y, newTopRight.y, newBottomLeft.y, newBottomRight.y); -// auto newMaxY = max(newTopLeft.y, newTopRight.y, newBottomLeft.y, newBottomRight.y); -// -// // XXX: What happens if the point that was furthest left is now on the right (because of a rotation)? -// // i.e. Should do we return a normalised rect or one with a negative width? -// return NXRect( -// newMinX, -// newMinY, -// newMaxX - newMinX, -// newMaxY - newMinY); -//} +NXRect NXRect::applying(const NXAffineTransform& t) const { + if (t.isIdentity()) { return *this; } -//NXRect NXRect::applying(const NXTransform3D& t) const { -// if (t == NXTransform3DIdentity) { return *this; } -// -// auto topLeft = t.transformingVector(minX(), minY(), 0); -// auto topRight = t.transformingVector(maxX(), minY(), 0); -// auto bottomLeft = t.transformingVector(minX(), maxY(), 0); -// auto bottomRight = t.transformingVector(maxX(), maxY(), 0); -// -// auto newMinX = min(topLeft.x, topRight.x, bottomLeft.x, bottomRight.x); -// auto newMaxX = max(topLeft.x, topRight.x, bottomLeft.x, bottomRight.x); -// -// auto newMinY = min(topLeft.y, topRight.y, bottomLeft.y, bottomRight.y); -// auto newMaxY = max(topLeft.y, topRight.y, bottomLeft.y, bottomRight.y); -// -// return NXRect(newMinX, -// newMinY, -// newMaxX - newMinX, -// newMaxY - newMinY); -//} + auto newTopLeft = NXPoint(minX(), minY()).applying(t); + auto newTopRight = NXPoint(maxX(), minY()).applying(t); + auto newBottomLeft = NXPoint(minX(), maxY()).applying(t); + auto newBottomRight = NXPoint(maxX(), maxY()).applying(t); + + + auto newMinX = min(newTopLeft.x, newTopRight.x, newBottomLeft.x, newBottomRight.x); + auto newMaxX = max(newTopLeft.x, newTopRight.x, newBottomLeft.x, newBottomRight.x); + + auto newMinY = min(newTopLeft.y, newTopRight.y, newBottomLeft.y, newBottomRight.y); + auto newMaxY = max(newTopLeft.y, newTopRight.y, newBottomLeft.y, newBottomRight.y); + + // XXX: What happens if the point that was furthest left is now on the right (because of a rotation)? + // i.e. Should do we return a normalised rect or one with a negative width? + return NXRect( + newMinX, + newMinY, + newMaxX - newMinX, + newMaxY - newMinY); +} + +NXRect NXRect::applying(const NXTransform3D& t) const { + if (t == NXTransform3DIdentity) { return *this; } + + auto topLeft = t.transformingVector(minX(), minY(), 0); + auto topRight = t.transformingVector(maxX(), minY(), 0); + auto bottomLeft = t.transformingVector(minX(), maxY(), 0); + auto bottomRight = t.transformingVector(maxX(), maxY(), 0); + + auto newMinX = min(topLeft.x, topRight.x, bottomLeft.x, bottomRight.x); + auto newMaxX = max(topLeft.x, topRight.x, bottomLeft.x, bottomRight.x); + + auto newMinY = min(topLeft.y, topRight.y, bottomLeft.y, bottomRight.y); + auto newMaxY = max(topLeft.y, topRight.y, bottomLeft.y, bottomRight.y); + + return NXRect(newMinX, + newMinY, + newMaxX - newMinX, + newMaxY - newMinY); +} bool NXRect::operator==(const NXRect& rhs) { return diff --git a/Submodules/UIKit/lib/NXAffineTransform.cpp b/Submodules/UIKit/lib/NXAffineTransform.cpp new file mode 100644 index 0000000..6aecda4 --- /dev/null +++ b/Submodules/UIKit/lib/NXAffineTransform.cpp @@ -0,0 +1,78 @@ +#include "NXAffineTransform.h" +#include "NXTransform3D.h" + +using namespace NXKit; + +#define RAD_PER_DEG 0.017453293f + +const NXAffineTransform NXAffineTransform::identity = NXAffineTransform(1, 0, 0, 1, 0, 0); + +NXAffineTransform::NXAffineTransform() {} + +NXAffineTransform::NXAffineTransform(NXFloat m11, NXFloat m12, NXFloat m21, NXFloat m22, NXFloat tX, NXFloat tY): + m11(m11), m12(m12), m21(m21), m22(m22), tX(tX), tY(tY) +{ } + +NXAffineTransform NXAffineTransform::translationBy(NXFloat x, NXFloat y) { + return NXAffineTransform(1, 0, + 0, 1, + x, y); +} + +NXAffineTransform NXAffineTransform::scaleBy(NXFloat x, NXFloat y) { + return NXAffineTransform(x, 0, + 0, y, + 0, 0); +} + +NXAffineTransform NXAffineTransform::scale(NXFloat f) { + return NXAffineTransform(f, 0, + 0, f, + 0, 0); +} + +NXAffineTransform NXAffineTransform::rotationBy(NXFloat angle) { + NXFloat radians = angle * RAD_PER_DEG; + NXFloat c = cosf(radians); + NXFloat s = sinf(radians); + + return NXAffineTransform(c, s, + -s, c, + 0, 0); +} + +std::optional NXAffineTransform::inverted() const { + auto d = m11 * m22 - m12 * m21; + if (d < 0) return std::nullopt; + + NXAffineTransform transform; + NXFloat multiplyer = 1 / d; + + transform.m11 = m22 * multiplyer; + transform.m12 = -m12 * multiplyer; + transform.m21 = -m21 * multiplyer; + transform.m22 = m11 * multiplyer; + transform.tX = tX; + transform.tY = tY; + + return transform; +} + +bool NXAffineTransform::isIdentity() const { + return *this == identity; +} + +bool NXAffineTransform::operator==(const NXAffineTransform& rhs) const { + return m11 == rhs.m11 && + m12 == rhs.m12 && + m21 == rhs.m21 && + m22 == rhs.m22 && + tX == rhs.tX && + tY == rhs.tY; +} + +NXAffineTransform NXAffineTransform::operator*(const NXAffineTransform& rhb) const { + auto a = NXTransform3DMakeAffineTransform(*this); + auto b = NXTransform3DMakeAffineTransform(rhb); + return NXTransform3DGetAffineTransform(a * b); +} \ No newline at end of file diff --git a/Submodules/UIKit/lib/NXColor.cpp b/Submodules/UIKit/lib/NXColor.cpp deleted file mode 100644 index 3267eb1..0000000 --- a/Submodules/UIKit/lib/NXColor.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#include "NXColor.h" - -using namespace NXKit; - -NXColor::NXColor(unsigned char r, unsigned char g, unsigned char b, unsigned char a) { - color = (a & 0xff) << 24 | (r & 0xff) << 16 | (g & 0xff) << 8 | (b & 0xff); -} -NXColor::NXColor(unsigned char r, unsigned char g, unsigned char b): NXColor(r, g, b, 255) {} -NXColor::NXColor(): NXColor(0, 0, 0) {} - - -unsigned char NXColor::r() { - return static_cast((color >> 16) & 0xff); -} - -unsigned char NXColor::g() { - return static_cast((color >> 8) & 0xff); -} - -unsigned char NXColor::b() { - return static_cast(color & 0xff); -} - -unsigned char NXColor::a() { - return static_cast((color >> 24) & 0xff); -} \ No newline at end of file diff --git a/Submodules/UIKit/lib/NXData.cpp b/Submodules/UIKit/lib/NXData.cpp new file mode 100644 index 0000000..c07a71c --- /dev/null +++ b/Submodules/UIKit/lib/NXData.cpp @@ -0,0 +1,53 @@ +#include +//#include +#include + +//#ifdef USE_LIBROMFS +#include +//#endif + +namespace NXKit { + +NXData::NXData(uint8_t bytes[], int count, bool freeSource) { + for (int i = 0; i < count; i++) + _data.push_back(bytes[i]); + + if (freeSource) + delete[] bytes; +} + +NXData::~NXData() = default; + +int NXData::count() const { + return (int) _data.size(); +} + +uint8_t* NXData::data() const { + return (uint8_t*) _data.data(); +} + +std::optional NXData::fromPath(const std::string& path) { +//#ifdef USE_LIBROMFS +// auto file = romfs::get(path); +// auto fileReader = SDL_RWFromConstMem(file.data(), (int) file.size()); +//#else + auto fileReader = SDL_RWFromFile(path.c_str(), "r"); +//#endif + + auto fileSize = int(fileReader->size(fileReader)); + + auto buffer = new uint8_t[fileSize]; + + auto bytesRead = int(fileReader->read(fileReader, buffer, 1, fileSize)); + + fileReader->close(fileReader); + + if (bytesRead == fileSize) { + return NXData(buffer, fileSize, true); + } else { + delete[] buffer; + return std::nullopt; + } +} + +} diff --git a/Submodules/UIKit/lib/NXLayer.cpp b/Submodules/UIKit/lib/NXLayer.cpp deleted file mode 100644 index e924df1..0000000 --- a/Submodules/UIKit/lib/NXLayer.cpp +++ /dev/null @@ -1,92 +0,0 @@ -// -// Created by Даниил Виноградов on 07.12.2024. -// - -#include "NXLayer.h" -#include "include/core/SkRRect.h" - -using namespace NXKit; - -bool NXLayer::layerTreeIsDirty = true; - -NXLayer::NXLayer() = default; - -void NXLayer::setAnchorPoint(NXKit::NXPoint anchorPoint) { - _anchorPoint = anchorPoint; -} - -void NXLayer::setBackgroundColor(std::optional backgroundColor) { - _backgroundColor = backgroundColor; -} - -void NXLayer::setBounds(NXKit::NXRect bounds) { - _bounds = bounds; -} - -void NXLayer::setPosition(NXKit::NXPoint position) { - _position = position; -} - -void NXLayer::setCornerRadius(NXFloat cornerRadius) { - _cornerRadius = cornerRadius; -} - -void NXLayer::addSublayer(const std::shared_ptr& layer) { - layer->removeFromSuperlayer(); - _sublayers.push_back(layer); - layer->superlayer = this->shared_from_this(); - NXLayer::layerTreeIsDirty = true; -} - -void NXLayer::insertSublayerAt(const std::shared_ptr& layer, int index) { - layer->removeFromSuperlayer(); - _sublayers.insert(_sublayers.begin() + index, layer); - layer->superlayer = this->shared_from_this(); - NXLayer::layerTreeIsDirty = true; -} - -void NXLayer::insertSublayerAbove(const std::shared_ptr& layer, const std::shared_ptr& sibling) { - // TODO: Need to implement -} - -void NXLayer::insertSublayerBelow(const std::shared_ptr& layer, const std::shared_ptr& sibling) { - auto itr = std::find(_sublayers.cbegin(), _sublayers.cend(), sibling); - if (itr == _sublayers.cend()) { return; } - - layer->removeFromSuperlayer(); - _sublayers.insert(itr, layer); - layer->superlayer = this->shared_from_this(); - NXLayer::layerTreeIsDirty = true; -} - -void NXLayer::removeFromSuperlayer() { - auto super = superlayer.lock(); - if (super == nullptr) return; - -// // If it's mask - remove -// if (super->mask.get() == this) { -// super->mask = nullptr; -// return; -// } - - // Find and remove this from superlayer - super->_sublayers.erase(std::remove(super->_sublayers.begin(), super->_sublayers.end(), shared_from_this()), super->_sublayers.end()); - NXLayer::layerTreeIsDirty = true; -} - -void NXLayer::skiaRender(SkCanvas* canvas) { - SkPaint paint; - paint.setColor(_backgroundColor->color); - paint.setAntiAlias(true); - - SkRect rect = SkRect::MakeXYWH(_position.x, _position.y, _bounds.width(), _bounds.height()); - SkRRect rrect; - float radii = _cornerRadius; - SkVector corners[] = {{radii, radii}, {radii, radii}, {radii, radii}, {radii, radii}}; - rrect.setRectRadii(rect, corners); - canvas->drawRRect(rrect, paint); - - for (const auto& sublayer: _sublayers) { - sublayer->skiaRender(canvas); - } -} \ No newline at end of file diff --git a/Submodules/UIKit/lib/NXTransform3D.cpp b/Submodules/UIKit/lib/NXTransform3D.cpp new file mode 100644 index 0000000..188c8e8 --- /dev/null +++ b/Submodules/UIKit/lib/NXTransform3D.cpp @@ -0,0 +1,259 @@ +// +// NXTransform3D.cpp +// NXKit +// +// Created by Даниил Виноградов on 19.08.2022. +// + +#include +#include + +#define RAD_PER_DEG 0.017453293f + +namespace NXKit { + +const NXTransform3D NXTransform3D::identity = NXTransform3DIdentity; + +NXTransform3D::NXTransform3D( + NXFloat m11, NXFloat m12, NXFloat m13, NXFloat m14, + NXFloat m21, NXFloat m22, NXFloat m23, NXFloat m24, + NXFloat m31, NXFloat m32, NXFloat m33, NXFloat m34, + NXFloat m41, NXFloat m42, NXFloat m43, NXFloat m44) + : + m11(m11), m12(m12), m13(m13), m14(m14), + m21(m21), m22(m22), m23(m23), m24(m24), + m31(m31), m32(m32), m33(m33), m34(m34), + m41(m41), m42(m42), m43(m43), m44(m44) +{ } + +NXTransform3D::NXTransform3D() : + m11(0), m12(0), m13(0), m14(0), + m21(0), m22(0), m23(0), m24(0), + m31(0), m32(0), m33(0), m34(0), + m41(0), m42(0), m43(0), m44(0) +{ } + +NXTransform3D::NXTransform3D(NXFloat* buffer) : + m11(buffer[0]), m12(buffer[1]), m13(buffer[2]), m14(buffer[3]), + m21(buffer[4]), m22(buffer[5]), m23(buffer[6]), m24(buffer[7]), + m31(buffer[8]), m32(buffer[9]), m33(buffer[10]), m34(buffer[11]), + m41(buffer[12]), m42(buffer[13]), m43(buffer[14]), m44(buffer[15]) +{ } + + +NXTransform3D NXTransform3D::concat(const NXTransform3D& other) const { + return CATransform3DConcat(*this, other); +} + +bool NXTransform3D::operator==(const NXTransform3D& rhs) const { + return NXTransform3DEqualToTransform(*this, rhs); +} + +NXTransform3D NXTransform3D::operator+(const NXTransform3D& b) const { + NXTransform3D a = *this; + return NXTransform3D( + a.m11 + b.m11, a.m12 + b.m12, a.m13 + b.m13, a.m14 + b.m14, + a.m21 + b.m21, a.m22 + b.m22, a.m23 + b.m23, a.m24 + b.m24, + a.m31 + b.m31, a.m32 + b.m32, a.m33 + b.m33, a.m34 + b.m34, + a.m41 + b.m41, a.m42 + b.m42, a.m43 + b.m43, a.m44 + b.m44 + ); +} + +NXTransform3D NXTransform3D::operator-(const NXTransform3D& b) const { + NXTransform3D a = *this; + return NXTransform3D( + a.m11 - b.m11, a.m12 - b.m12, a.m13 - b.m13, a.m14 - b.m14, + a.m21 - b.m21, a.m22 - b.m22, a.m23 - b.m23, a.m24 - b.m24, + a.m31 - b.m31, a.m32 - b.m32, a.m33 - b.m33, a.m34 - b.m34, + a.m41 - b.m41, a.m42 - b.m42, a.m43 - b.m43, a.m44 - b.m44 + ); +} + +NXTransform3D NXTransform3D::operator*(const NXTransform3D& first) const { + return (*this).concat(first); +} + +NXTransform3D NXTransform3D::operator*(const NXFloat& b) const { + NXTransform3D a = *this; + return NXTransform3D( + a.m11 * b, a.m12 * b, a.m13 * b, a.m14 * b, + a.m21 * b, a.m22 * b, a.m23 * b, a.m24 * b, + a.m31 * b, a.m32 * b, a.m33 * b, a.m34 * b, + a.m41 * b, a.m42 * b, a.m43 * b, a.m44 * b + ); +} + +void getPartsFromMatrix(const NXAffineTransform& matrix, NXFloat* angle, NXPoint* translation, NXPoint* scale) { + auto a = NXFloat(matrix.m11); + auto b = NXFloat(matrix.m12); + auto c = NXFloat(matrix.m21); + auto d = NXFloat(matrix.m22); + auto e = NXFloat(matrix.tX); + auto f = NXFloat(matrix.tY); + + auto delta = a * d - b * c; + + *angle = 0; + *translation = NXPoint(e, f); + *scale = NXPoint(1, 1); + + // Apply the QR-like decomposition. + if (a != 0 || b != 0) { + auto r = sqrtf(a * a + b * b); + *angle = b > 0 ? acosf(a / r) : -acosf(a / r); + *scale = NXPoint(r, delta / r); + } else if (c != 0 || d != 0) { + auto s = sqrtf(c * c + d * d); + *angle = M_PI / 2 - (d > 0 ? acosf(-c / s) : -acosf(c / s)); + *scale = NXPoint(delta / s, s); + } else { } + + *angle = *angle / RAD_PER_DEG; +} + +NXTransform3D NXTransform3D::interpolateTo(const NXTransform3D& matrix, const NXFloat& progress) const { + auto currentM = NXTransform3DGetAffineTransform(*this); + auto newM = NXTransform3DGetAffineTransform(matrix); + + NXFloat angle; + NXPoint translation, scale; + + NXFloat dAngle; + NXPoint dTranslation, dScale; + + getPartsFromMatrix(currentM, &angle, &translation, &scale); + getPartsFromMatrix(newM, &dAngle, &dTranslation, &dScale); +// + angle = angle + (dAngle - angle) * progress; + scale = scale + (dScale - scale) * progress;// (scale - NXPoint(1, 1)) * progress + NXPoint(1, 1); + translation = translation + (dTranslation - translation) * progress; + + return NXTransform3D::translationBy(translation.x, translation.y, 0) * NXTransform3D::scaleBy(scale.x, scale.y, 1) * NXTransform3D::rotationBy(angle, 0, 0, 1); +} + +Vector3 NXTransform3D::transformingVector(NXFloat x, NXFloat y, NXFloat z) const { + auto newX = m11 * x + m21 * y + m31 * z + m41; + auto newY = m12 * x + m22 * y + m32 * z + m42; + auto newZ = m13 * x + m23 * y + m33 * z + m43; + auto newW = m14 * x + m24 * y + m34 * z + m44; + + return { newX / newW, newY / newW, newZ / newW }; +} + +bool NXTransform3DEqualToTransform(const NXTransform3D& a, const NXTransform3D& b) { + return + a.m11 == b.m11 && a.m12 == b.m12 && a.m13 == b.m13 && a.m14 == b.m14 && + a.m21 == b.m21 && a.m22 == b.m22 && a.m23 == b.m23 && a.m24 == b.m24 && + a.m31 == b.m31 && a.m32 == b.m32 && a.m33 == b.m33 && a.m34 == b.m34 && + a.m41 == b.m41 && a.m42 == b.m42 && a.m43 == b.m43 && a.m44 == b.m44; +} + +NXTransform3D NXTransform3DMakeAffineTransform(NXAffineTransform m) { + return NXTransform3D( + m.m11, m.m12, 0, 0, + m.m21, m.m22, 0, 0, + 0, 0, 1, 0, + m.tX, m.tY, 0, 1); +} + +NXTransform3D CATransform3DMakeTranslation(NXFloat tx, NXFloat ty, NXFloat tz) { + return NXTransform3D( + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + tx, ty, tz, 1); +} + +NXTransform3D CATransform3DMakeScale(NXFloat tx, NXFloat ty, NXFloat tz) { + return NXTransform3D( + tx, 0, 0, 0, + 0, ty, 0, 0, + 0, 0, tz, 0, + 0, 0, 0, 1); +} + +NXTransform3D CATransform3DMakeRotation(NXFloat angle, NXFloat x, NXFloat y, NXFloat z) { + NXFloat p, radians, c, s, c_, zc_, yc_, xzc_, xyc_, yzc_, xs, ys, zs; + + p = 1/sqrtf(x*x + y*y + z*z); + x *= p; y *= p; z *= p; + radians = angle * RAD_PER_DEG; + c = cosf(radians); + s = sinf(radians); + c_ = 1 - c; + zc_ = z*c_; + yc_ = y*c_; + xzc_ = x*zc_; + xyc_ = x*y*c_; + yzc_ = y*zc_; + xs = x*s; + ys = y*s; + zs = z*s; + + return NXTransform3D( + x*x*c_ + c, xyc_ + zs, xzc_ - ys, 0, + xyc_ - zs, y*yc_ + c, yzc_ + xs, 0, + xzc_ + ys, yzc_ - xs, z*zc_ + c, 0, + 0, 0, 0, 1); +} + +NXTransform3D CATransform3DConcat(const NXTransform3D& a, const NXTransform3D& b) { + if (a == NXTransform3DIdentity) { return b; } + if (b == NXTransform3DIdentity) { return a; } + + auto result = NXTransform3D(); + + result.m11 = a.m11 * b.m11 + a.m21 * b.m12 + a.m31 * b.m13 + a.m41 * b.m14; + result.m12 = a.m12 * b.m11 + a.m22 * b.m12 + a.m32 * b.m13 + a.m42 * b.m14; + result.m13 = a.m13 * b.m11 + a.m23 * b.m12 + a.m33 * b.m13 + a.m43 * b.m14; + result.m14 = a.m14 * b.m11 + a.m24 * b.m12 + a.m34 * b.m13 + a.m44 * b.m14; + + result.m21 = a.m11 * b.m21 + a.m21 * b.m22 + a.m31 * b.m23 + a.m41 * b.m24; + result.m22 = a.m12 * b.m21 + a.m22 * b.m22 + a.m32 * b.m23 + a.m42 * b.m24; + result.m23 = a.m13 * b.m21 + a.m23 * b.m22 + a.m33 * b.m23 + a.m43 * b.m24; + result.m24 = a.m14 * b.m21 + a.m24 * b.m22 + a.m34 * b.m23 + a.m44 * b.m24; + + result.m31 = a.m11 * b.m31 + a.m21 * b.m32 + a.m31 * b.m33 + a.m41 * b.m34; + result.m32 = a.m12 * b.m31 + a.m22 * b.m32 + a.m32 * b.m33 + a.m42 * b.m34; + result.m33 = a.m13 * b.m31 + a.m23 * b.m32 + a.m33 * b.m33 + a.m43 * b.m34; + result.m34 = a.m14 * b.m31 + a.m24 * b.m32 + a.m34 * b.m33 + a.m44 * b.m34; + + result.m41 = a.m11 * b.m41 + a.m21 * b.m42 + a.m31 * b.m43 + a.m41 * b.m44; + result.m42 = a.m12 * b.m41 + a.m22 * b.m42 + a.m32 * b.m43 + a.m42 * b.m44; + result.m43 = a.m13 * b.m41 + a.m23 * b.m42 + a.m33 * b.m43 + a.m43 * b.m44; + result.m44 = a.m14 * b.m41 + a.m24 * b.m42 + a.m34 * b.m43 + a.m44 * b.m44; + + return result; +} + +NXAffineTransform NXTransform3DGetAffineTransform(NXTransform3D t) { + return NXAffineTransform( + t.m11, t.m12, + t.m21, t.m22, + t.m41, t.m42); +} + +NXTransform3D NXTransform3D::translationBy(NXFloat x, NXFloat y, NXFloat z) const { + return concat(CATransform3DMakeTranslation(x, y, z)); +} + +NXTransform3D NXTransform3D::scaleBy(NXFloat x, NXFloat y, NXFloat z) const { + return concat(CATransform3DMakeScale(x, y, z)); +} + +NXTransform3D NXTransform3D::scale(NXFloat factor) const { + return concat(CATransform3DMakeScale(factor, factor, factor)); +} + +NXTransform3D NXTransform3D::rotationBy(NXFloat angle, NXFloat x, NXFloat y, NXFloat z) const { + return concat(CATransform3DMakeRotation(angle, x, y, z)); +} + +SkM44 NXTransform3D::toSkM44() const { + return SkM44(m11, m21, m31, m41, + m12, m22, m32, m42, + m13, m23, m33, m43, + m14, m24, m34, m44); +} + +} diff --git a/Submodules/UIKit/lib/Timer.cpp b/Submodules/UIKit/lib/Timer.cpp new file mode 100644 index 0000000..83aa3dd --- /dev/null +++ b/Submodules/UIKit/lib/Timer.cpp @@ -0,0 +1,70 @@ +#include +#include + +namespace NXKit { + +double truncatingRemainderFor(double value, double dividingBy) { + return value - dividingBy * floor(value / dividingBy); +} + +double timevalInMilliseconds(timeval time) { + return (double(time.tv_sec) * 1000.0) + (double(time.tv_usec) / 1000.0); +} + +double timevalInSeconds(timeval time) { + return (double(time.tv_sec) + double(time.tv_usec) / 1000000.0); +} + +Timer::Timer(double startingAtInMilliseconds) { + auto startTime = timeval(); + gettimeofday(&startTime, nullptr); + + auto seconds = floor(startingAtInMilliseconds / 1000.0); + auto milliseconds = truncatingRemainderFor(startingAtInMilliseconds, 1000.0); + startTime.tv_sec += seconds; + startTime.tv_usec += milliseconds * 1000; + + this->startTime = startTime; +} + +double Timer::getElapsedTimeInMilliseconds() { + timeval currentTime = {0, 0}; + gettimeofday(¤tTime, nullptr); + return std::fmax(0.001, timevalInMilliseconds(currentTime) - timevalInMilliseconds(startTime)); +} + +double Timer::getElapsedTimeInSeconds() { + timeval currentTime = {0, 0}; + gettimeofday(¤tTime, nullptr); + return std::fmax(0.000001, timevalInSeconds(currentTime) - timevalInSeconds(startTime)); +} + +double Timer::operator-(const Timer &rhs) const { + return timevalInMilliseconds(this->startTime) - timevalInMilliseconds(rhs.startTime); +} + +bool operator==(const Timer &c1, const Timer &c2) { + return c1.startTime.tv_sec == c2.startTime.tv_sec; +} + +bool operator!=(const Timer &c1, const Timer &c2) { + return c1.startTime.tv_sec != c2.startTime.tv_sec; +} + +bool operator>(const Timer &c1, const Timer &c2) { + return c1.startTime.tv_sec > c2.startTime.tv_sec; +} + +bool operator<(const Timer &c1, const Timer &c2) { + return c1.startTime.tv_sec < c2.startTime.tv_sec; +} + +bool operator<=(const Timer &c1, const Timer &c2) { + return c1.startTime.tv_sec <= c2.startTime.tv_sec; +} + +bool operator>=(const Timer &c1, const Timer &c2) { + return c1.startTime.tv_sec >= c2.startTime.tv_sec; +} + +} diff --git a/Submodules/UIKit/lib/UIApplication.cpp b/Submodules/UIKit/lib/UIApplication.cpp new file mode 100644 index 0000000..bdcdc7f --- /dev/null +++ b/Submodules/UIKit/lib/UIApplication.cpp @@ -0,0 +1,297 @@ +#include + +using namespace NXKit; + +std::shared_ptr UIApplication::shared = nullptr; + +UIApplication::UIApplication() { +// // TODO: Replace with Bunbles +//#ifdef USE_LIBROMFS +// Utils::resourcePath = ""; +//#elif __SWITCH__ +// Utils::resourcePath = "romfs:/"; +//#elif __APPLE__ +//#include +//#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE +// Utils::resourcePath = std::string(SDL_GetBasePath()) + "/assets/"; +//#endif +//#endif + +// UIFont.loadSystemFonts(); +} + +void UIApplication::handleEventsIfNeeded() { + auto e = SDL_Event(); + + while (SDL_PollEvent(&e)) { + handleSDLEvent(e); + } +} + +void UIApplication::handleSDLEvent(SDL_Event e) { + switch (e.type) { + case SDL_QUIT: { + handleSDLQuit(); + return; + } + case SDL_FINGERDOWN: { +// auto renderSize = UIRenderer::_main->bounds().size; +// auto fingerPoint = Point(renderSize.width * e.tfinger.x, renderSize.height * e.tfinger.y); +//// printf("Touch id: %lld Begin, X:%f - Y:%f\n", e.tfinger.fingerId, fingerPoint.x, fingerPoint.y); +// auto touch = new_shared(e.tfinger.fingerId, fingerPoint, getCPUTimeUsec()); +// auto event = std::shared_ptr(new UIEvent(touch)); +// UIEvent::activeEvents.push_back(event); +// sendEvent(event); + break; + } + case SDL_FINGERMOTION: { +// auto renderSize = UIRenderer::_main->bounds().size; +// auto fingerPoint = Point(renderSize.width * e.tfinger.x, renderSize.height * e.tfinger.y); +//// printf("Touch id: %lld Moved, X:%f - Y:%f\n", e.tfinger.fingerId, fingerPoint.x, fingerPoint.y); +// +// std::shared_ptr event; +// std::shared_ptr touch; +// +// for (auto& levent: UIEvent::activeEvents) { +// for (auto& ltouch: levent->allTouches()) { +// if (ltouch->touchId() == e.tfinger.fingerId) { +// event = levent; +// touch = ltouch; +// } +// } +// } +// +// if (!event || !touch) return; +// +// auto previousTimestamp = touch->timestamp(); +// auto newTimestamp = getCPUTimeUsec(); +// +// touch->updateAbsoluteLocation(fingerPoint); +// touch->_timestamp = newTimestamp; +// touch->_phase = UITouchPhase::moved; +// +// // SDL adds timestamps on send which could be quite different to when the event actually occurred. +// // It's common to get two events with an unrealistically small time between them; don't send those. +// if ((newTimestamp - previousTimestamp) > (5 / 1000)) { +// sendEvent(event); +// } + + break; + } + case SDL_FINGERUP: { +// printf("Touch id: %lld Ended\n", e.tfinger.fingerId); + +// std::shared_ptr event; +// std::shared_ptr touch; +// +// for (auto& levent: UIEvent::activeEvents) { +// for (auto& ltouch: levent->allTouches()) { +// if (ltouch->touchId() == e.tfinger.fingerId) { +// event = levent; +// touch = ltouch; +// } +// } +// } +// +// if (!event || !touch) return; +// +// touch->_timestamp = getCPUTimeUsec(); +// touch->_phase = UITouchPhase::ended; +// +// sendEvent(event); +// UIEvent::activeEvents.erase(std::remove(UIEvent::activeEvents.begin(), UIEvent::activeEvents.end(), event), UIEvent::activeEvents.end()); + + break; + } + case SDL_MOUSEBUTTONDOWN: { + // Simulate touch +// auto touchEvent = SDL_Event(); +// touchEvent.type = SDL_FINGERDOWN; +// +// auto renderSize = UIRenderer::_main->bounds().size; +// touchEvent.tfinger.x = float(e.button.x) / renderSize.width; +// touchEvent.tfinger.y = float(e.button.y) / renderSize.height; +// touchEvent.tfinger.fingerId = -1; +// +// handleSDLEvent(touchEvent); + break; + } + case SDL_MOUSEMOTION: { + // Simulate touch +// auto touchEvent = SDL_Event(); +// touchEvent.type = SDL_FINGERMOTION; +// +// auto renderSize = UIRenderer::_main->bounds().size; +// touchEvent.tfinger.x = float(e.button.x) / renderSize.width; +// touchEvent.tfinger.y = float(e.button.y) / renderSize.height; +// touchEvent.tfinger.fingerId = -1; +// +// handleSDLEvent(touchEvent); + break; + } + case SDL_MOUSEBUTTONUP: { + // Simulate touch +// auto touchEvent = SDL_Event(); +// touchEvent.type = SDL_FINGERUP; +// +// auto renderSize = UIRenderer::_main->bounds().size; +// touchEvent.tfinger.x = float(e.button.x) / renderSize.width; +// touchEvent.tfinger.y = float(e.button.y) / renderSize.height; +// touchEvent.tfinger.fingerId = -1; +// +// handleSDLEvent(touchEvent); + break; + } + case SDL_CONTROLLERDEVICEADDED: { + printf("Controller added\n"); + break; + } + case SDL_CONTROLLERBUTTONDOWN: { +// if (e.cbutton.button == SDL_CONTROLLER_BUTTON_START) { +// handleSDLQuit(); +// } + break; + } + case SDL_KEYDOWN: { +// if (e.key.keysym.sym == SDLK_q) { +// handleSDLQuit(); +// } +// +// auto press = new_shared(); +// auto key = UIKey(); +// key._keyCode = (UIKeyboardHIDUsage) e.key.keysym.scancode; +// key._modifierFlags = OptionSet(e.key.keysym.mod); +// press->_key = key; +// press->_phase = UIPressPhase::began; +// press->setForWindow(delegate->window); +// +// auto event = std::shared_ptr(new UIPressesEvent(press)); +// UIPressesEvent::activePressesEvents.push_back(event); +// sendEvent(event); + + break; + } + case SDL_KEYUP: { +// std::shared_ptr event; +// std::shared_ptr press; +// +// for (auto& levent: UIPressesEvent::activePressesEvents) { +// for (auto& lpress: levent->allPresses()) { +// if (!lpress->_key.has_value()) continue; +// +// if ((int) lpress->_key->_keyCode == (int) e.key.keysym.scancode) { +// event = levent; +// press = lpress; +// } +// } +// } +// +// if (!event || !press) return; +// +// press->_timestamp = getCPUTimeUsec(); +// press->_phase = UIPressPhase::ended; +// +// sendEvent(event); +// UIPressesEvent::activePressesEvents.erase(std::remove(UIPressesEvent::activePressesEvents.begin(), UIPressesEvent::activePressesEvents.end(), event), UIPressesEvent::activePressesEvents.end()); + + break; + +//#if DEBUG +// let keyModifier = SDL_Keymod(UInt32(e.key.keysym.mod)) +// if keyModifier.contains(KMOD_LSHIFT) || keyModifier.contains(KMOD_RSHIFT) { +// switch e.key.keysym.sym { +// case 43, 61: // +/*, +/= keys. TODO send key events via UIEvent +// break +// case 45: break // -/_ key +// case 118: // "V" +// keyWindow?.printViewHierarchy() +// default: +// print(e.key.keysym.sym) +// } +// } +// +// if keyModifier.contains(KMOD_LGUI) || keyModifier.contains(KMOD_RGUI) { +// if e.key.keysym.sym == 114 { // CMD-R +// UIScreen.main = nil +// UIScreen.main = UIScreen() +// } +// } +//#endif +// +// let scancode = e.key.keysym.scancode +// if scancode == .androidHardwareBackButton || scancode == .escapeKey { +// keyWindow?.deepestPresentedView().handleHardwareBackButtonPress() +// } + } + case SDL_APP_WILLENTERBACKGROUND: { + UIApplication::onWillEnterBackground(); + break; + } + case SDL_APP_DIDENTERBACKGROUND: { + UIApplication::onDidEnterBackground(); + break; + } + case SDL_APP_WILLENTERFOREGROUND: { + UIApplication::onWillEnterForeground(); + break; + } + case SDL_APP_DIDENTERFOREGROUND: { + UIApplication::onDidEnterForeground(); + break; + } + case SDL_WINDOWEVENT: { + switch (e.window.event) + { + case SDL_WINDOWEVENT_RESIZED: +// UIRenderer::main()->refreshScreenResolution(e.window.data1, e.window.data2); + break; + default: + break; + } + break; + } + default: + break; + } +} + +void UIApplication::handleSDLQuit() { + if (delegate) delegate->applicationWillTerminate(this); + + // UIEvent::activeEvents.clear(); + UIApplication::delegate->window = nullptr; + UIApplication::shared = nullptr; + // UIRenderer::_main = nullptr; + + SDL_Quit(); + exit(0); +} + +// void UIApplication::sendEvent(std::shared_ptr event) { +// for (auto& touch: event->allTouches()) { +// touch->_window = keyWindow; +// } + +// if (!keyWindow.expired()) +// keyWindow.lock()->sendEvent(event); +// } + +void UIApplication::onWillEnterForeground() { + UIApplication::shared->delegate->applicationDidEnterBackground(UIApplication::shared.get()); +// UIApplication.post(willEnterForegroundNotification) +} + +void UIApplication::onDidEnterForeground() { + UIApplication::shared->delegate->applicationDidBecomeActive(UIApplication::shared.get()); +// UIApplication.post(didBecomeActiveNotification) +} + +void UIApplication::onWillEnterBackground() { + UIApplication::shared->delegate->applicationWillResignActive(UIApplication::shared.get()); +// UIApplication.post(willResignActiveNotification) +} + +void UIApplication::onDidEnterBackground() { + UIApplication::shared->delegate->applicationDidEnterBackground(UIApplication::shared.get()); +// UIApplication.post(didEnterBackgroundNotification) +} diff --git a/Submodules/UIKit/lib/UIApplicationDelegateImpl.cpp b/Submodules/UIKit/lib/UIApplicationDelegateImpl.cpp new file mode 100644 index 0000000..214ea03 --- /dev/null +++ b/Submodules/UIKit/lib/UIApplicationDelegateImpl.cpp @@ -0,0 +1,4 @@ +#include + +using namespace NXKit; + diff --git a/Submodules/UIKit/lib/UIApplicationMain.cpp b/Submodules/UIKit/lib/UIApplicationMain.cpp new file mode 100644 index 0000000..bd51d0b --- /dev/null +++ b/Submodules/UIKit/lib/UIApplicationMain.cpp @@ -0,0 +1,64 @@ +#include +#include +#include +// #include +#include +#include +#include + +namespace NXKit { + +bool applicationRunLoop() { + auto currentTime = Timer(); + UIApplication::shared->handleEventsIfNeeded(); + DispatchQueue::main()->performAll(); // TODO: May be need to be after rendering loop +// UIRenderer::main()->render(UIApplication::shared->keyWindow.lock(), currentTime); + + + + // Move to UIRenderer + auto surface = SkiaCtx::_main->getBackbufferSurface(); + if (!surface) return true; + + auto canvas = surface->getCanvas(); + canvas->clear(SK_ColorTRANSPARENT); + + canvas->save(); + auto scale = SkiaCtx::_main->getScaleFactor(); + canvas->scale(scale, scale); + auto keyWindow = UIApplication::shared->keyWindow.lock(); + + keyWindow->layer()->setBounds({ NXPoint::zero, SkiaCtx::_main->getSize() } ); + keyWindow->layer()->skiaRender(canvas); + canvas->restore(); + + SkiaCtx::_main->flushAndSubmit(surface); + SkiaCtx::_main->swapBuffers(); + + return true; +} + +void setupRenderAndRunLoop() { + +} + +int UIApplicationMain(std::shared_ptr appDelegate) { + UIApplication::shared = new_shared(); + SkiaCtx::_main = MakeSkiaCtx(); + + UIApplication::shared->delegate = appDelegate; + +// appDelegate->applicationNeedsXIBRegistration(UIApplication::shared.get()); + if (!appDelegate->applicationDidFinishLaunchingWithOptions(UIApplication::shared.get(), {})) { + return 1; + } + + while(platformRunLoop([]() { return applicationRunLoop(); })); + + UIApplication::shared = nullptr; + SkiaCtx::_main = nullptr; + + return 0; +}; + +} diff --git a/Submodules/UIKit/lib/UIColor.cpp b/Submodules/UIKit/lib/UIColor.cpp new file mode 100644 index 0000000..ace8bb7 --- /dev/null +++ b/Submodules/UIKit/lib/UIColor.cpp @@ -0,0 +1,46 @@ +#include "UIColor.h" + +using namespace NXKit; + +UIColor UIColor::clear = UIColor(0, 0, 0, 0); +UIColor UIColor::red = UIColor(255, 0, 0); +UIColor UIColor::green = UIColor(0, 255, 0); +UIColor UIColor::blue = UIColor(0, 0, 255); +UIColor UIColor::orange = UIColor(255, 150, 0); +UIColor UIColor::cyan = UIColor(0, 150, 255); +UIColor UIColor::white = UIColor(255, 255, 255); +UIColor UIColor::black = UIColor(0, 0, 0); +UIColor UIColor::gray = UIColor(155, 155, 155); + +UIColor UIColor::separator = UIColor(208, 208, 208); +UIColor UIColor::systemBackground = UIColor(235, 235, 235); +UIColor UIColor::secondarySystemBackground = UIColor(240, 240, 240); +UIColor UIColor::tetriarySystemBackground = UIColor(252, 255, 248); +UIColor UIColor::tint = UIColor(49, 79, 235); + +UIColor::UIColor(unsigned char r, unsigned char g, unsigned char b, unsigned char a) { + color = (a & 0xff) << 24 | (r & 0xff) << 16 | (g & 0xff) << 8 | (b & 0xff); +} +UIColor::UIColor(unsigned char r, unsigned char g, unsigned char b): UIColor(r, g, b, 255) {} +UIColor::UIColor(): UIColor(0, 0, 0) {} + + +unsigned char UIColor::r() { + return static_cast((color >> 16) & 0xff); +} + +unsigned char UIColor::g() { + return static_cast((color >> 8) & 0xff); +} + +unsigned char UIColor::b() { + return static_cast(color & 0xff); +} + +unsigned char UIColor::a() { + return static_cast((color >> 24) & 0xff); +} + +bool UIColor::operator==(const UIColor& rhs) const { + return this->color == rhs.color; +} diff --git a/Submodules/UIKit/lib/UIImage.cpp b/Submodules/UIKit/lib/UIImage.cpp new file mode 100644 index 0000000..8838a9b --- /dev/null +++ b/Submodules/UIKit/lib/UIImage.cpp @@ -0,0 +1,7 @@ +#include "UIImage.h" + +using namespace NXKit; + +UIImage::UIImage() { + +} diff --git a/Submodules/UIKit/lib/UIView.cpp b/Submodules/UIKit/lib/UIView.cpp index 4b365d6..989e3b8 100644 --- a/Submodules/UIKit/lib/UIView.cpp +++ b/Submodules/UIKit/lib/UIView.cpp @@ -3,7 +3,8 @@ using namespace NXKit; UIView::UIView() { - _layer = new_shared(); + _layer = new_shared(); + _layer->setAnchorPoint(NXPoint::zero); } void UIView::addSubview(std::shared_ptr view) { diff --git a/Submodules/UIKit/lib/UIWindow.cpp b/Submodules/UIKit/lib/UIWindow.cpp new file mode 100644 index 0000000..87f434b --- /dev/null +++ b/Submodules/UIKit/lib/UIWindow.cpp @@ -0,0 +1,21 @@ +#include +#include + +using namespace NXKit; + +void UIWindow::makeKeyAndVisible() { +// self.safeAreaInsets = UIWindow.getSafeAreaInsets() + auto window = std::static_pointer_cast(shared_from_this()); + // window->setBounds(UIRenderer::main()->bounds()); + UIApplication::shared->keyWindow = window; + + // auto viewController = _rootViewController; + // if (viewController) { + // viewController->loadViewIfNeeded(); + // viewController->view()->setFrame(this->bounds()); + // viewController->viewWillAppear(false); + // addSubview(viewController->view()); + // viewController->viewDidAppear(false); + // updateFocus(); + // } +} \ No newline at end of file diff --git a/Submodules/UIKit/lib/platforms/SkiaCtx.cpp b/Submodules/UIKit/lib/platforms/SkiaCtx.cpp index cac76bb..b8d1d92 100644 --- a/Submodules/UIKit/lib/platforms/SkiaCtx.cpp +++ b/Submodules/UIKit/lib/platforms/SkiaCtx.cpp @@ -3,6 +3,8 @@ using namespace NXKit; +std::shared_ptr SkiaCtx::_main; + void SkiaCtx::flushAndSubmit(sk_sp surface) { if (auto dContext = directContext()) { dContext->flushAndSubmit(surface.get(), GrSyncCpu::kYes); diff --git a/app/AppDelegate.cpp b/app/AppDelegate.cpp new file mode 100644 index 0000000..b7382c9 --- /dev/null +++ b/app/AppDelegate.cpp @@ -0,0 +1,34 @@ +#include +// #include +// #include +// #include +// #include + +namespace NXKit { + +bool UIApplicationDelegate::applicationDidFinishLaunchingWithOptions(UIApplication* application, std::map launchOptions) { + window = new_shared(); + +// auto vc = new_shared(); +// auto vc = new_shared(); + // auto vc = new_shared(); +// auto vc = new_shared(); + // window->setRootViewController(vc); + window->makeKeyAndVisible(); + window->layer()->setBackgroundColor(UIColor::systemBackground); + + auto sublayer = new_shared(); + sublayer->setPosition({ 100, 100 }); + sublayer->setBounds({0, 0, 200, 44}); + sublayer->setBackgroundColor(UIColor::tint); + sublayer->setCornerRadius(10); + sublayer->setAnchorPoint(NXPoint::zero); +// sublayer->setTransform(NXTransform3D::identity.rotationBy(45, 0, 0, 1)); + + window->layer()->addSublayer(sublayer); + + + return true; +} + +} diff --git a/app/main.cpp b/app/main.cpp index a7ba10a..10ce2d3 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -1,7 +1,9 @@ #include #include +#include int main(int argc, char *argv[]) { - std::make_unique(); +// std::make_unique(); + return NXKit::UIApplicationMain(); return 0; }