diff --git a/CMakeLists.txt b/CMakeLists.txt index 873aa83..aff565e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,7 +24,8 @@ add_executable(${PROJECT_NAME} app/Screens/TestViewController/TestViewController.cpp app/Screens/YogaTestViewController/YogaTestViewController.cpp app/Screens/IBTestController/IBTestController.cpp - app/Screens/NXTextScreen/NXTestScreen.cpp + app/Screens/NXTextScreen/NXNavigationController.cpp + app/Screens/NXTextScreen/NXTabBarController.cpp ) target_include_directories(${PROJECT_NAME} PUBLIC diff --git a/Submodules/UIKit/CMakeLists.txt b/Submodules/UIKit/CMakeLists.txt index d1f5491..80d9305 100644 --- a/Submodules/UIKit/CMakeLists.txt +++ b/Submodules/UIKit/CMakeLists.txt @@ -23,6 +23,7 @@ add_library(UIKit lib/DispatchQueue.cpp lib/Timer.cpp lib/Geometry.cpp + lib/IndexPath.cpp lib/CGImage.cpp lib/CADisplayLink.cpp lib/CALayer.cpp diff --git a/Submodules/UIKit/include/CABlurLayer.h b/Submodules/UIKit/include/CABlurLayer.h index a999fa7..b556df9 100644 --- a/Submodules/UIKit/include/CABlurLayer.h +++ b/Submodules/UIKit/include/CABlurLayer.h @@ -13,8 +13,8 @@ public: void draw(SkCanvas* context) override; std::shared_ptr copy() override; - void setBlurValue(NXFloat blurValue); - [[nodiscard]] NXFloat blurValue() const { return _blurValue; } + void setBlurRadius(NXFloat blurRadius); + [[nodiscard]] NXFloat blurRadius() const { return _blurRadius; } void setBackgroundTintColor(const UIColor& backgroundTintColor) { _backgroundTintColor = backgroundTintColor; } [[nodiscard]] UIColor backgroundTintColor() const { return _backgroundTintColor; } @@ -23,10 +23,10 @@ public: protected: void update(std::shared_ptr presentation, std::shared_ptr animation, float progress) override; - UIColor _backgroundTintColor; + UIColor _backgroundTintColor = UIColor::clear; private: - NXFloat _blurValue = 16; // 10; + NXFloat _blurRadius = 16; // 10; }; } diff --git a/Submodules/UIKit/include/CALayer.h b/Submodules/UIKit/include/CALayer.h index 1103e43..17a9e90 100644 --- a/Submodules/UIKit/include/CALayer.h +++ b/Submodules/UIKit/include/CALayer.h @@ -22,6 +22,7 @@ public: virtual std::shared_ptr actionForKey(std::string event) = 0; virtual void display(std::shared_ptr layer) = 0; virtual void updateCurrentEnvironment() = 0; + virtual bool isHierarchyRoot() { return false; } }; class CALayer: public enable_shared_from_this { @@ -36,9 +37,12 @@ public: void setContentsGravity(CALayerContentsGravity contentsGravity) { _contentsGravity = contentsGravity; } [[nodiscard]] CALayerContentsGravity contentsGravity() const { return _contentsGravity; } - void setContentsScale(NXFloat contentsScale) { _contentsScale = contentsScale; } + void setContentsScale(NXFloat contentsScale); [[nodiscard]] NXFloat contentsScale() const { return _contentsScale; } + void setScaleModifier(NXFloat scaleModifier); + [[nodiscard]] NXFloat scaleModifier() const { return _scaleModifier; } + void setAnchorPoint(NXPoint anchorPoint); [[nodiscard]] NXPoint anchorPoint() const { return _anchorPoint; } @@ -149,9 +153,12 @@ private: friend class UIView; friend bool applicationRunLoop(); + void setLayerTreeDirtyIfNeeded() const; + /// 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; + NXFloat _scaleModifier = 1.0f; CALayerContentsGravity _contentsGravity = CALayerContentsGravity::resize; std::weak_ptr _superlayer; @@ -202,6 +209,11 @@ private: /// This is both a performance optimization (avoids lots of animations at the start) /// as well as a correctness fix (matches iOS behaviour). Maybe there's a better way though? bool hasBeenRenderedInThisPartOfOverallLayerHierarchy = false; + + /// Prohibit layers from making layout dirty if they are not in visible hierarchy + void updateIsPartOfPresentedHierarchy(bool value); + bool isPartOfPresentedHierarchy() const; + bool _isPartOfPresentedHierarchy = false; }; } diff --git a/Submodules/UIKit/include/IndexPath.h b/Submodules/UIKit/include/IndexPath.h new file mode 100644 index 0000000..3ae32ed --- /dev/null +++ b/Submodules/UIKit/include/IndexPath.h @@ -0,0 +1,21 @@ +// +// Created by Даниил Виноградов on 23.02.2025. +// + +#pragma once + +#include + +namespace NXKit { + +struct IndexPath { + IndexPath(int item, int section); + IndexPath(): IndexPath(0, 0) {} + + int section() { return _indexes[0]; } + int item() { return _indexes[1]; } +private: + std::vector _indexes; +}; + +} \ No newline at end of file diff --git a/Submodules/UIKit/include/SkiaCtx.h b/Submodules/UIKit/include/SkiaCtx.h index d18ed25..3021929 100644 --- a/Submodules/UIKit/include/SkiaCtx.h +++ b/Submodules/UIKit/include/SkiaCtx.h @@ -22,16 +22,21 @@ public: virtual void swapBuffers() = 0; virtual float getScaleFactor() { return 1; } + virtual float getExtraScaleFactor() { return 1; } virtual sk_sp getFontMgr() { return fontMgr; } virtual UIUserInterfaceStyle getThemeMode() { return UIUserInterfaceStyle::light; } + void setExtraScaleFactor(NXFloat extraScaleFactor) { _extraScaleFactor = extraScaleFactor; } + static std::shared_ptr main() { return _main; } static std::shared_ptr _main; protected: NXSize _size; + NXFloat _extraScaleFactor = 1; sk_sp fontMgr; + private: friend class UIApplication; }; diff --git a/Submodules/UIKit/include/UIAction.h b/Submodules/UIKit/include/UIAction.h index 2c3c426..16f8ea0 100644 --- a/Submodules/UIKit/include/UIAction.h +++ b/Submodules/UIKit/include/UIAction.h @@ -18,6 +18,7 @@ public: void setTitle(std::string title) { _title = std::move(title); } // uint identifier() { return _id; } + void perform() { _handler(); } private: friend class UIControl; diff --git a/Submodules/UIKit/include/UIBlurView.h b/Submodules/UIKit/include/UIBlurView.h index 3973e0c..e3ca0e9 100644 --- a/Submodules/UIKit/include/UIBlurView.h +++ b/Submodules/UIKit/include/UIBlurView.h @@ -9,8 +9,8 @@ class UIBlurView: public UIView { public: UIBlurView(); - void setBlurValue(NXFloat blurValue) { _blurLayer()->setBlurValue(blurValue); } - [[nodiscard]] NXFloat blurValue() const { return _blurLayer()->blurValue(); } + void setBlurRadius(NXFloat blurValue) { _blurLayer()->setBlurRadius(blurValue); } + [[nodiscard]] NXFloat blurRadius() const { return _blurLayer()->blurRadius(); } void setBackgroundTintColor(const UIColor& backgroundTintColor) { _blurLayer()->setBackgroundTintColor(backgroundTintColor); } [[nodiscard]] UIColor backgroundTintColor() const { return _blurLayer()->backgroundTintColor(); } diff --git a/Submodules/UIKit/include/UIControl.h b/Submodules/UIKit/include/UIControl.h index 9311706..249da31 100644 --- a/Submodules/UIKit/include/UIControl.h +++ b/Submodules/UIKit/include/UIControl.h @@ -58,7 +58,6 @@ public: void pressesBegan(std::set> pressees, std::shared_ptr event) override; void pressesEnded(std::set> pressees, std::shared_ptr event) override; - bool isEnabled() const; virtual void setEnabled(bool enabled); @@ -70,6 +69,10 @@ public: virtual void willGainFocus(); virtual void willLoseFocus(); + virtual void willChangeFocusHighlight(bool highlighted); + + virtual void willChangeHighlight(bool highlighted); + std::shared_ptr hitTest(NXPoint point, UIEvent *withEvent) override; void setBaseScaleMultiplier(NXFloat baseScaleMultiplier); [[nodiscard]] NXFloat baseScaleMultiplier() const { return _baseScaleMultiplier; } diff --git a/Submodules/UIKit/include/UIKit.h b/Submodules/UIKit/include/UIKit.h index 7faacaa..d52d144 100644 --- a/Submodules/UIKit/include/UIKit.h +++ b/Submodules/UIKit/include/UIKit.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include #include #include diff --git a/Submodules/UIKit/include/UILabel.h b/Submodules/UIKit/include/UILabel.h index 5762092..de9a393 100644 --- a/Submodules/UIKit/include/UILabel.h +++ b/Submodules/UIKit/include/UILabel.h @@ -28,15 +28,13 @@ public: void setFontWeight(NXFloat fontWeight); [[nodiscard]] NXFloat fontWeight() const { return _fontWeight; } - void setBaseScaleMultiplier(NXFloat baseScaleMultiplier); - [[nodiscard]] NXFloat baseScaleMultiplier() const { return _baseScaleMultiplier; } + void setScaleModifier(NXFloat scaleModifier); NXSize sizeThatFits(NXSize size) override; void traitCollectionDidChange(std::shared_ptr previousTraitCollection) override; bool applyXMLAttribute(const std::string& name, const std::string& value) override; private: - NXFloat _baseScaleMultiplier = 1; int _numberOfLines = 1; NXFloat _fontSize = 17; NXFloat _fontWeight = SkFontStyle::kNormal_Weight; diff --git a/Submodules/UIKit/include/UIViewController.h b/Submodules/UIKit/include/UIViewController.h index eeb7aa1..5a2bd6c 100644 --- a/Submodules/UIKit/include/UIViewController.h +++ b/Submodules/UIKit/include/UIViewController.h @@ -2,6 +2,8 @@ #include +#include + namespace NXKit { class UIViewController: public UIResponder, public UITraitEnvironment, public UIFocusEnvironment, public enable_shared_from_this { @@ -29,6 +31,9 @@ public: std::vector> children() { return _children; } std::weak_ptr parent() { return _parent; } + std::string title() { return _title; } + void setTitle(std::string title) { _title = std::move(title); } + void addChild(const std::shared_ptr& child); virtual void willMoveToParent(const std::shared_ptr& parent); virtual void didMoveToParent(std::shared_ptr parent); @@ -39,7 +44,7 @@ public: UIEdgeInsets systemMinimumLayoutMargins() { return _systemMinimumLayoutMargins; } - bool viewRespectsSystemMinimumLayoutMargins() { return _viewRespectsSystemMinimumLayoutMargins; } + bool viewRespectsSystemMinimumLayoutMargins() const { return _viewRespectsSystemMinimumLayoutMargins; } void setViewRespectsSystemMinimumLayoutMargins(bool viewRespectsSystemMinimumLayoutMargins); void present(const std::shared_ptr& otherViewController, bool animated, const std::function& completion = [](){}); @@ -49,7 +54,7 @@ public: void traitCollectionDidChange(std::shared_ptr previousTraitCollection) override; // Focus - virtual std::shared_ptr parentFocusEnvironment() override; + std::shared_ptr parentFocusEnvironment() override; protected: virtual void makeViewAppear(bool animated, std::shared_ptr presentingViewController, std::function completion = [](){}); @@ -63,6 +68,7 @@ private: UIEdgeInsets _systemMinimumLayoutMargins = UIEdgeInsets(0, 16, 0, 16); bool _viewRespectsSystemMinimumLayoutMargins = true; float _animationTime = 0.5; + std::string _title = ""; std::shared_ptr _presentedViewController; std::weak_ptr _presentingViewController; diff --git a/Submodules/UIKit/include/UIWindow.h b/Submodules/UIKit/include/UIWindow.h index a6b6a67..f6b2e2f 100644 --- a/Submodules/UIKit/include/UIWindow.h +++ b/Submodules/UIKit/include/UIWindow.h @@ -16,6 +16,8 @@ public: UIWindow(); ~UIWindow() override; + bool isHierarchyRoot() override { return true; } + std::shared_ptr window() override; std::shared_ptr focusSystem() { return _focusSystem; } diff --git a/Submodules/UIKit/include/platforms/switch/SkiaCtx_switch.h b/Submodules/UIKit/include/platforms/switch/SkiaCtx_switch.h index 9c432f8..70e1e42 100644 --- a/Submodules/UIKit/include/platforms/switch/SkiaCtx_switch.h +++ b/Submodules/UIKit/include/platforms/switch/SkiaCtx_switch.h @@ -16,7 +16,7 @@ public: // float getScaleFactor() override; sk_sp directContext() override { return context; } - UIUserInterfaceStyle getThemeMode() override { return UIUserInterfaceStyle::light; } + UIUserInterfaceStyle getThemeMode() override; virtual void swapBuffers() override; diff --git a/Submodules/UIKit/lib/CABlurLayer.cpp b/Submodules/UIKit/lib/CABlurLayer.cpp index a34f1db..1961531 100644 --- a/Submodules/UIKit/lib/CABlurLayer.cpp +++ b/Submodules/UIKit/lib/CABlurLayer.cpp @@ -10,7 +10,7 @@ CABlurLayer::CABlurLayer(): CALayer() { } CABlurLayer::CABlurLayer(CABlurLayer* layer): CALayer(layer) { - _blurValue = layer->_blurValue; + _blurRadius = layer->_blurRadius; } std::shared_ptr CABlurLayer::copy() { @@ -24,7 +24,7 @@ void CABlurLayer::draw(SkCanvas* context) { blurPaint.setStyle(SkPaint::kFill_Style); // blurPaint.setStrokeWidth(10); - sk_sp newBlurFilter = SkImageFilters::Blur(_blurValue, _blurValue, SkTileMode::kClamp, nullptr); + sk_sp newBlurFilter = SkImageFilters::Blur(_blurRadius, _blurRadius, SkTileMode::kClamp, nullptr); blurPaint.setImageFilter(std::move(newBlurFilter)); // context->save(); @@ -44,14 +44,14 @@ void CABlurLayer::draw(SkCanvas* context) { context->restore(); } -void CABlurLayer::setBlurValue(NXFloat blurValue) { - if (_blurValue == blurValue) return; - onWillSet("blurValue"); - _blurValue = blurValue; +void CABlurLayer::setBlurRadius(NXFloat blurRadius) { + if (_blurRadius == blurRadius) return; + onWillSet("blurRadius"); + _blurRadius = blurRadius; } std::optional CABlurLayer::value(std::string forKeyPath) { - if (forKeyPath == "blurValue") return _blurValue; + if (forKeyPath == "blurRadius") return _blurRadius; return CALayer::value(forKeyPath); } @@ -61,14 +61,14 @@ void CABlurLayer::update(std::shared_ptr presentation, std::shared_ptr< auto keyPath = animation->keyPath.value(); auto fromValue = animation->fromValue.value(); - if (keyPath == "blurValue") { + if (keyPath == "blurRadius") { auto start = any_optional_cast(fromValue); if (!start.has_value()) { return; } auto end = any_optional_cast(animation->toValue); - if (!end.has_value()) end = this->_blurValue; + if (!end.has_value()) end = this->_blurRadius; - std::static_pointer_cast(presentation)->setBlurValue(start.value() + (end.value() - start.value()) * progress); + std::static_pointer_cast(presentation)->setBlurRadius(start.value() + (end.value() - start.value()) * progress); } CALayer::update(presentation, animation, progress); diff --git a/Submodules/UIKit/lib/CALayer.cpp b/Submodules/UIKit/lib/CALayer.cpp index f3e4a98..d59d7c7 100644 --- a/Submodules/UIKit/lib/CALayer.cpp +++ b/Submodules/UIKit/lib/CALayer.cpp @@ -21,6 +21,22 @@ void CALayer::setLayerTreeIsDirty() { layerTreeIsDirty = true; } +bool CALayer::isPartOfPresentedHierarchy() const { + return _isPartOfPresentedHierarchy || (!delegate.expired() && delegate.lock()->isHierarchyRoot()); +} + +void CALayer::setLayerTreeDirtyIfNeeded() const { + if (isPartOfPresentedHierarchy()) + CALayer::setLayerTreeIsDirty(); +} + +void CALayer::updateIsPartOfPresentedHierarchy(bool value) { + _isPartOfPresentedHierarchy = value; + for (auto sublayer : _sublayers) { + sublayer->updateIsPartOfPresentedHierarchy(isPartOfPresentedHierarchy()); + } +} + NXFloat CALayer::defaultAnimationDuration = 0.3f; CALayer::CALayer() = default; @@ -51,6 +67,18 @@ CALayer::CALayer(CALayer* layer) { _contentsGravity = layer->_contentsGravity; } +void CALayer::setContentsScale(NXFloat contentsScale) { + if (_contentsScale == contentsScale) return; + onWillSet("contentsScale"); + _contentsScale = contentsScale; +} + +void CALayer::setScaleModifier(NXFloat scaleModifier) { + if (_scaleModifier == scaleModifier) return; + onWillSet("scaleModifier"); + _scaleModifier = scaleModifier; +} + void CALayer::setAnchorPoint(NXKit::NXPoint anchorPoint) { if (_anchorPoint == anchorPoint) return; onWillSet("anchorPoint"); @@ -145,12 +173,12 @@ NXAffineTransform CALayer::affineTransform() { void CALayer::setHidden(bool hidden) { _isHidden = hidden; - CALayer::setLayerTreeIsDirty(); + setLayerTreeDirtyIfNeeded(); } void CALayer::setContents(std::shared_ptr contents) { _contents = std::move(contents); - CALayer::setLayerTreeIsDirty(); + setLayerTreeDirtyIfNeeded(); } void CALayer::setMask(const std::shared_ptr& mask) { @@ -166,14 +194,16 @@ void CALayer::addSublayer(const std::shared_ptr& layer) { layer->removeFromSuperlayer(); _sublayers.push_back(layer); layer->_superlayer = this->shared_from_this(); - CALayer::setLayerTreeIsDirty(); + layer->updateIsPartOfPresentedHierarchy(isPartOfPresentedHierarchy()); + setLayerTreeDirtyIfNeeded(); } 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::setLayerTreeIsDirty(); + layer->updateIsPartOfPresentedHierarchy(isPartOfPresentedHierarchy()); + setLayerTreeDirtyIfNeeded(); } void CALayer::insertSublayerAbove(const std::shared_ptr& layer, const std::shared_ptr& sibling) { @@ -187,7 +217,8 @@ void CALayer::insertSublayerBelow(const std::shared_ptr& layer, const s layer->removeFromSuperlayer(); _sublayers.insert(itr, layer); layer->_superlayer = this->shared_from_this(); - CALayer::setLayerTreeIsDirty(); + layer->updateIsPartOfPresentedHierarchy(isPartOfPresentedHierarchy()); + setLayerTreeDirtyIfNeeded(); } void CALayer::removeFromSuperlayer() { @@ -203,7 +234,8 @@ void CALayer::removeFromSuperlayer() { // Find and remove this from superlayer super->_sublayers.erase(std::remove(super->_sublayers.begin(), super->_sublayers.end(), shared_from_this()), super->_sublayers.end()); - CALayer::setLayerTreeIsDirty(); + updateIsPartOfPresentedHierarchy(false); + setLayerTreeDirtyIfNeeded(); } void CALayer::draw(SkCanvas* context) {} @@ -455,7 +487,7 @@ void CALayer::removeAnimation(const std::string& forKey) { } void CALayer::onWillSet(const std::string& keyPath) { - CALayer::setLayerTreeIsDirty(); + setLayerTreeDirtyIfNeeded(); const auto& animationKey = keyPath; auto animation = std::static_pointer_cast(actionForKey(animationKey)); @@ -491,6 +523,8 @@ std::optional CALayer::value(std::string forKeyPath) { if (forKeyPath == "transform") return _transform; if (forKeyPath == "position") return _position; if (forKeyPath == "anchorPoint") return _anchorPoint; + if (forKeyPath == "contentsScale") return _contentsScale; + if (forKeyPath == "scaleModifier") return _scaleModifier; if (forKeyPath == "cornerRadius") return _cornerRadius; return std::nullopt; } @@ -516,7 +550,7 @@ void CALayer::animateAt(Timer currentTime) { } this->_presentation = animations.empty() ? nullptr : presentation; - CALayer::setLayerTreeIsDirty(); + setLayerTreeDirtyIfNeeded(); } // Writing into `presentation->_...` cause we don't need onWillSet to be triggered @@ -534,6 +568,7 @@ void CALayer::update(std::shared_ptr presentation, std::shared_ptr>(animation->toValue); if (!end.has_value()) end = this->_backgroundColor; if (!end.has_value()) end = UIColor::clear; + if (!end.value().has_value()) end = UIColor::clear; presentation->setBackgroundColor(start.value()->interpolationTo(end.value().value(), progress)); } @@ -545,6 +580,7 @@ void CALayer::update(std::shared_ptr presentation, std::shared_ptr>(animation->toValue); if (!end.has_value()) end = this->_borderColor; if (!end.has_value()) end = UIColor::clear; + if (!end.value().has_value()) end = UIColor::clear; presentation->setBorderColor(start.value()->interpolationTo(end.value().value(), progress)); } @@ -565,6 +601,7 @@ void CALayer::update(std::shared_ptr presentation, std::shared_ptr>(animation->toValue); if (!end.has_value()) end = this->_shadowColor; if (!end.has_value()) end = UIColor::clear; + if (!end.value().has_value()) end = UIColor::clear; presentation->setShadowColor(start.value()->interpolationTo(end.value().value(), progress)); } @@ -604,6 +641,24 @@ void CALayer::update(std::shared_ptr presentation, std::shared_ptrsetPosition(start.value() + (end.value() - start.value()) * progress); } + if (keyPath == "contentsScale") { + auto start = any_optional_cast(fromValue); + if (!start.has_value()) { return; } + + auto end = any_optional_cast(animation->toValue); + if (!end.has_value()) end = this->_contentsScale; + + presentation->setContentsScale(start.value() + (end.value() - start.value()) * progress); + } + if (keyPath == "scaleModifier") { + auto start = any_optional_cast(fromValue); + if (!start.has_value()) { return; } + + auto end = any_optional_cast(animation->toValue); + if (!end.has_value()) end = this->_scaleModifier; + + presentation->setScaleModifier(start.value() + (end.value() - start.value()) * progress); + } if (keyPath == "anchorPoint") { auto start = any_optional_cast(fromValue); if (!start.has_value()) { return; } diff --git a/Submodules/UIKit/lib/IndexPath.cpp b/Submodules/UIKit/lib/IndexPath.cpp new file mode 100644 index 0000000..a46ad30 --- /dev/null +++ b/Submodules/UIKit/lib/IndexPath.cpp @@ -0,0 +1,12 @@ +// +// Created by Даниил Виноградов on 23.02.2025. +// + +#include + +using namespace NXKit; + +IndexPath::IndexPath(int item, int section) { + _indexes.push_back(section); + _indexes.push_back(item); +} diff --git a/Submodules/UIKit/lib/UIApplication.cpp b/Submodules/UIKit/lib/UIApplication.cpp index 6a52842..7481c0f 100644 --- a/Submodules/UIKit/lib/UIApplication.cpp +++ b/Submodules/UIKit/lib/UIApplication.cpp @@ -111,6 +111,7 @@ void UIApplication::handleSDLEvent(SDL_Event e) { break; } +#ifndef PLATFORM_IOS case SDL_MOUSEBUTTONDOWN: { // Simulate touch auto touchEvent = SDL_Event(); @@ -150,6 +151,7 @@ void UIApplication::handleSDLEvent(SDL_Event e) { handleSDLEvent(touchEvent); break; } +#endif case SDL_CONTROLLERDEVICEADDED: { printf("Controller added\n"); diff --git a/Submodules/UIKit/lib/UIApplicationMain.cpp b/Submodules/UIKit/lib/UIApplicationMain.cpp index a0ccfce..5898d95 100644 --- a/Submodules/UIKit/lib/UIApplicationMain.cpp +++ b/Submodules/UIKit/lib/UIApplicationMain.cpp @@ -29,7 +29,7 @@ bool applicationRunLoop() { keyWindow->_traitCollection->_displayScale = scale; keyWindow->_traitCollection->_userInterfaceStyle = SkiaCtx::main()->getThemeMode(); keyWindow->traitCollectionDidChange(oldCollection); - CALayer::layerTreeIsDirty = true; + CALayer::setLayerTreeIsDirty(); } UIView::animateIfNeeded(currentTime); @@ -77,6 +77,10 @@ int UIApplicationMain(const std::shared_ptr& appDelegate) UIApplication::shared = new_shared(); SkiaCtx::_main = MakeSkiaCtx(); +#ifdef PLATFORM_IOS + SkiaCtx::_main->setExtraScaleFactor(1.3f); +#endif + UIApplication::shared->delegate = appDelegate; appDelegate->applicationNeedsXIBRegistration(UIApplication::shared.get()); diff --git a/Submodules/UIKit/lib/UIButton.cpp b/Submodules/UIKit/lib/UIButton.cpp index db024a8..15fd3a7 100644 --- a/Submodules/UIKit/lib/UIButton.cpp +++ b/Submodules/UIKit/lib/UIButton.cpp @@ -12,6 +12,7 @@ UIButton::UIButton(UIButtonStyle style) { _titleLabel->setHidden(true); _titleLabel->setFontSize(17); _titleLabel->setFontWeight(600); +// _titleLabel->setContentMode(UIViewContentMode::redraw); _imageView->setHidden(true); _imageView->setContentMode(UIViewContentMode::scaleAspectFit); @@ -52,7 +53,7 @@ void UIButton::setImage(const std::shared_ptr& image) { } void UIButton::baseScaleMultiplierDidChange() { - _titleLabel->setBaseScaleMultiplier(baseScaleMultiplier()); + _titleLabel->setScaleModifier(baseScaleMultiplier()); } void UIButton::applyStyle(UIButtonStyle style) { @@ -91,4 +92,4 @@ bool UIButton::applyXMLAttribute(const std::string& name, const std::string& val return false; } -} \ No newline at end of file +} diff --git a/Submodules/UIKit/lib/UIControl.cpp b/Submodules/UIKit/lib/UIControl.cpp index 026ac08..d023563 100644 --- a/Submodules/UIKit/lib/UIControl.cpp +++ b/Submodules/UIKit/lib/UIControl.cpp @@ -60,16 +60,12 @@ void UIControl::didUpdateFocusIn(UIFocusUpdateContext context, UIFocusAnimationC void UIControl::willGainFocus() { setTransform(NXAffineTransform::scale(1.06f)); - setBaseScaleMultiplier(1.06f); + setBaseScaleMultiplier(1.1f); layer()->setShadowColor(UIColor::black); layer()->setShadowOpacity(0.4); layer()->setShadowOffset({0, 6}); layer()->setShadowRadius(18); layer()->setZPosition(1); - -// UIView::performWithoutAnimation([this]() { -// layer()->setBorderWidth(3); -// }); } void UIControl::willLoseFocus() { @@ -78,9 +74,24 @@ void UIControl::willLoseFocus() { layer()->setShadowOpacity(0); layer()->setShadowRadius(0); layer()->setZPosition(0); -// UIView::performWithoutAnimation([this]() { -// layer()->setBorderWidth(0); -// }); +} + +void UIControl::willChangeFocusHighlight(bool highlighted) { + if (highlighted) { + setTransform(NXAffineTransform::scale(1.02f)); + setBaseScaleMultiplier(1.0f); + layer()->setShadowOffset({0, 3}); + layer()->setShadowRadius(4); + } else { + setTransform(NXAffineTransform::scale(1.06f)); + setBaseScaleMultiplier(1.1f); + layer()->setShadowOffset({0, 6}); + layer()->setShadowRadius(18); + } +} + +void UIControl::willChangeHighlight(bool highlighted) { + setAlpha(highlighted ? 0.8 : 1); } bool UIControl::isEnabled() const { @@ -103,24 +114,18 @@ bool UIControl::isHighlighted() const { return _state[uint8_t (UIControlState::highlighted)]; } +std::shared_ptr UIControl::hitTest(NXPoint point, UIEvent *withEvent) { + return UIView::hitTest(point, withEvent); +} + void UIControl::setHighlighted(bool highlighted) { if (UIView::isFocused()) { UIView::animate(0.2, [&]() { - if (highlighted) { - setTransform(NXAffineTransform::scale(1.02f)); - setBaseScaleMultiplier(1.02f); - layer()->setShadowOffset({0, 3}); - layer()->setShadowRadius(4); - } else { - setTransform(NXAffineTransform::scale(1.06f)); - setBaseScaleMultiplier(1.06f); - layer()->setShadowOffset({0, 6}); - layer()->setShadowRadius(18); - } + willChangeFocusHighlight(highlighted); }); } else { UIView::animate(0.2, 0, UIViewAnimationOptions::allowUserInteraction, [&]() { - setAlpha(highlighted ? 0.8 : 1); + willChangeHighlight(highlighted); }); } diff --git a/Submodules/UIKit/lib/UIImageView.cpp b/Submodules/UIKit/lib/UIImageView.cpp index 829a9e8..6313916 100644 --- a/Submodules/UIKit/lib/UIImageView.cpp +++ b/Submodules/UIKit/lib/UIImageView.cpp @@ -34,7 +34,7 @@ void UIImageView::updateTextureFromImage() { setBounds(bounds); } else { layer()->setContents(nullptr); - layer()->setContentsScale(SkiaCtx::main()->getScaleFactor()); + layer()->setContentsScale(UITraitCollection::current()->displayScale()); } } diff --git a/Submodules/UIKit/lib/UILabel.cpp b/Submodules/UIKit/lib/UILabel.cpp index cfe86a2..aa48778 100644 --- a/Submodules/UIKit/lib/UILabel.cpp +++ b/Submodules/UIKit/lib/UILabel.cpp @@ -46,9 +46,9 @@ void UILabel::setFontWeight(NXFloat fontWeight) { setNeedsLayout(); } -void UILabel::setBaseScaleMultiplier(NXFloat baseScaleMultiplier) { - if (_baseScaleMultiplier == baseScaleMultiplier) return; - _baseScaleMultiplier = baseScaleMultiplier; +void UILabel::setScaleModifier(NXFloat scaleModifier) { + if (layer()->scaleModifier() == scaleModifier) return; + layer()->setScaleModifier(scaleModifier); setNeedsDisplay(); } @@ -57,7 +57,9 @@ NXSize UILabel::sizeThatFits(NXSize size) { paragraph->layout(size.width); auto height = paragraph->getHeight(); auto width = paragraph->getMaxIntrinsicWidth(); - auto rWidth = std::ceil(width); + + // Adds extra 1, because of extra content scale could be not enough to fit text line + auto rWidth = std::ceil(width) + 1; return { rWidth, height }; } @@ -70,11 +72,13 @@ void UILabel::draw() { updateParagraph(); SkBitmap bitmap; - auto scale = SkiaCtx::main()->getScaleFactor() * _baseScaleMultiplier; + NXFloat scale; NXSize size; if (contentMode() == UIViewContentMode::redraw) { + scale = traitCollection()->displayScale() * layer()->presentationOrSelf()->scaleModifier(); size = layer()->presentationOrSelf()->bounds().size; } else { + scale = traitCollection()->displayScale() * layer()->scaleModifier(); size = bounds().size; } auto bitmapSize = size * scale; @@ -89,7 +93,6 @@ void UILabel::draw() { paragraph->paint(&canvas, 0, yOffset); - printf("UILabel draw\n"); layer()->setContents(new_shared(bitmap.asImage())); layer()->setContentsScale(scale); } diff --git a/Submodules/UIKit/lib/UIView.cpp b/Submodules/UIKit/lib/UIView.cpp index 7d24c7b..fd3183c 100644 --- a/Submodules/UIKit/lib/UIView.cpp +++ b/Submodules/UIKit/lib/UIView.cpp @@ -274,6 +274,7 @@ void UIView::addSubview(const std::shared_ptr& view) { _layer->addSublayer(view->_layer); _subviews.push_back(view); view->setSuperview(this->shared_from_this()); + view->setNeedsUpdateSafeAreaInsets(); } @@ -285,12 +286,32 @@ std::shared_ptr UIView::window() { void UIView::setSuperview(const std::shared_ptr& superview) { _superview = superview; + if (superview) + traitCollectionDidChange(superview->traitCollection()); + if (!_tintColor.has_value()) tintColorDidChange(); } void UIView::insertSubviewAt(const std::shared_ptr& view, int index) { - // TODO: Need to implement + bool needToNotifyViewController = false; + if (!view->_parentController.expired()) { + auto window = this->window(); + if (window) { + needToNotifyViewController = true; + } + } + + setNeedsLayout(); + view->removeFromSuperview(); + + if (needToNotifyViewController) + view->_parentController.lock()->viewWillAppear(true); + + _layer->insertSublayerAt(view->_layer, index); + _subviews.insert(_subviews.begin() + index, view); + view->setSuperview(this->shared_from_this()); + view->setNeedsUpdateSafeAreaInsets(); } void UIView::insertSubviewBelow(const std::shared_ptr& view, const std::shared_ptr& belowSubview) { @@ -346,7 +367,8 @@ void UIView::drawAndLayoutTreeIfNeeded() { UIColor::_currentTint = tint; if (_contentMode == UIViewContentMode::redraw) { - if (visibleLayer->contents() && visibleLayer->contents()->size() != visibleLayer->bounds().size) { + if (visibleLayer->contents() && ((visibleLayer->contents()->size() / visibleLayer->contentsScale()) != visibleLayer->bounds().size || visibleLayer->scaleModifier() != layer()->scaleModifier())) + { setNeedsDisplay(); } } diff --git a/Submodules/UIKit/lib/UIViewController.cpp b/Submodules/UIKit/lib/UIViewController.cpp index 2155a2a..03bd072 100644 --- a/Submodules/UIKit/lib/UIViewController.cpp +++ b/Submodules/UIKit/lib/UIViewController.cpp @@ -20,6 +20,12 @@ void UIViewController::setView(std::shared_ptr view) { if (_view) _view->_parentController.reset(); _view = std::move(view); _view->_parentController = weak_from_this(); + + if (traitCollection() != nullptr) { + _view->_traitCollection = traitCollection(); + _view->UITraitEnvironment::traitCollectionDidChange(nullptr); + } + viewDidLoad(); } @@ -64,6 +70,9 @@ void UIViewController::viewDidDisappear(bool animated) { void UIViewController::addChild(const std::shared_ptr& child) { _children.push_back(child); child->willMoveToParent(weak_from_this().lock()); + + child->_traitCollection = _traitCollection; + child->traitCollectionDidChange(nullptr); } void UIViewController::willMoveToParent(const std::shared_ptr& parent) { @@ -78,17 +87,17 @@ void UIViewController::didMoveToParent(std::shared_ptr parent) void UIViewController::removeFromParent() { if (auto spt = _parent.lock()) { - spt->_children.erase(std::remove(spt->_children.begin(), spt->_children.end(), shared_from_this())); + spt->_children.erase(std::remove(spt->_children.begin(), spt->_children.end(), shared_from_this()), spt->_children.end()); this->_parent.reset(); viewDidDisappear(true); } } -//void UIViewController::setAdditionalSafeAreaInsets(UIEdgeInsets additionalSafeAreaInsets) { -// if (_additionalSafeAreaInsets == additionalSafeAreaInsets) return; -// _additionalSafeAreaInsets = additionalSafeAreaInsets; -// view()->setNeedsUpdateSafeAreaInsets(); -//} +void UIViewController::setAdditionalSafeAreaInsets(UIEdgeInsets additionalSafeAreaInsets) { + if (_additionalSafeAreaInsets == additionalSafeAreaInsets) return; + _additionalSafeAreaInsets = additionalSafeAreaInsets; + view()->setNeedsUpdateSafeAreaInsets(); +} void UIViewController::setViewRespectsSystemMinimumLayoutMargins(bool viewRespectsSystemMinimumLayoutMargins) { if (_viewRespectsSystemMinimumLayoutMargins == viewRespectsSystemMinimumLayoutMargins) return; @@ -163,7 +172,7 @@ void UIViewController::traitCollectionDidChange(std::shared_ptr_traitCollection = _traitCollection; - child->UITraitEnvironment::traitCollectionDidChange(previousTraitCollection); + child->traitCollectionDidChange(previousTraitCollection); } } diff --git a/Submodules/UIKit/lib/YGLayout.cpp b/Submodules/UIKit/lib/YGLayout.cpp index 434217c..4c67f5e 100644 --- a/Submodules/UIKit/lib/YGLayout.cpp +++ b/Submodules/UIKit/lib/YGLayout.cpp @@ -7,6 +7,7 @@ #include #include +#include namespace NXKit { @@ -183,6 +184,10 @@ void YGLayout::YGApplyLayoutToViewHierarchy(const std::shared_ptr&view, YGApplyLayoutToViewHierarchy(i, false); } } + + if (!view->_parentController.expired()) { + view->_parentController.lock()->viewDidLayoutSubviews(); + } } float YGLayout::YGRoundPixelValue(float value) { diff --git a/Submodules/UIKit/lib/platforms/apple/ios/SkiaCtx_ios.mm b/Submodules/UIKit/lib/platforms/apple/ios/SkiaCtx_ios.mm index bb92c12..f92a467 100644 --- a/Submodules/UIKit/lib/platforms/apple/ios/SkiaCtx_ios.mm +++ b/Submodules/UIKit/lib/platforms/apple/ios/SkiaCtx_ios.mm @@ -43,12 +43,11 @@ NXSize SkiaCtx_ios::getSize() { auto layer = window.layer.presentationLayer; if (layer == NULL) layer = window.layer; NXSize size = { static_cast(layer.bounds.size.width), static_cast(layer.bounds.size.height) }; -// printf("Size: %f | %f\n", layer.bounds.size.width, layer.bounds.size.height); - return size; + return size * _extraScaleFactor; } float SkiaCtx_ios::getScaleFactor() { - return UIApplication.sharedApplication.keyWindow.traitCollection.displayScale; + return UIApplication.sharedApplication.keyWindow.traitCollection.displayScale / _extraScaleFactor; } NXKit::UIUserInterfaceStyle SkiaCtx_ios::getThemeMode() { @@ -61,7 +60,7 @@ NXKit::UIUserInterfaceStyle SkiaCtx_ios::getThemeMode() { } sk_sp SkiaCtx_ios::getBackbufferSurface() { - auto size = getSize(); + auto size = getSize() / _extraScaleFactor; if (_size.width == size.width && _size.height == size.height && surface != nullptr) { return surface; } _size = size; @@ -69,15 +68,15 @@ sk_sp SkiaCtx_ios::getBackbufferSurface() { GrGLFramebufferInfo framebuffer_info; framebuffer_info.fFormat = GL_RGBA8; framebuffer_info.fFBOID = 0; - auto scaleFactor = getScaleFactor(); - GrBackendRenderTarget target = GrBackendRenderTargets::MakeGL(size.width * scaleFactor, - size.height * scaleFactor, + auto pureScaleFactor = getScaleFactor() * _extraScaleFactor; + GrBackendRenderTarget target = GrBackendRenderTargets::MakeGL(size.width * pureScaleFactor, + size.height * pureScaleFactor, 0, 8, framebuffer_info); SkSurfaceProps props; - glViewport(0, 0, _size.width * scaleFactor, _size.height * scaleFactor); + glViewport(0, 0, _size.width * pureScaleFactor, _size.height * pureScaleFactor); surface = SkSurfaces::WrapBackendRenderTarget(context.get(), target, kBottomLeft_GrSurfaceOrigin, kRGBA_8888_SkColorType, nullptr, &props); diff --git a/Submodules/UIKit/lib/platforms/switch/SkiaCtx_switch.cpp b/Submodules/UIKit/lib/platforms/switch/SkiaCtx_switch.cpp index 4d0a63c..24b1572 100644 --- a/Submodules/UIKit/lib/platforms/switch/SkiaCtx_switch.cpp +++ b/Submodules/UIKit/lib/platforms/switch/SkiaCtx_switch.cpp @@ -116,6 +116,17 @@ void SkiaCtx_switch::initContext() { // return NSApplication.sharedApplication.keyWindow.backingScaleFactor; //} + +UIUserInterfaceStyle SkiaCtx_switch::getThemeMode() { + ColorSetId colorSetId; + setsysGetColorSetId(&colorSetId); + + if (colorSetId == ColorSetId_Dark) + return UIUserInterfaceStyle::dark; + else + return UIUserInterfaceStyle::light; +} + sk_sp SkiaCtx_switch::getBackbufferSurface() { auto size = getSize(); if (_size.width == size.width && _size.height == size.height && surface != nullptr) { return surface; } diff --git a/app/AppDelegate.cpp b/app/AppDelegate.cpp index 8609e5c..e2e4962 100644 --- a/app/AppDelegate.cpp +++ b/app/AppDelegate.cpp @@ -5,7 +5,8 @@ #include #include #include -#include +#include +#include // #include // #include #include @@ -16,13 +17,16 @@ bool UIApplicationDelegate::applicationDidFinishLaunchingWithOptions(UIApplicati DEFAULT_ROMFS_REGISTRATION window = new_shared(); -// auto vc = new_shared(); -// auto vc = new_shared(); - auto vc = new_shared(); + auto vc1 = new_shared(); + auto vc = new_shared(); + std::vector> vcs = { vc, vc1 }; + + auto tbvc = new_shared( vcs ); + auto nvc = new_shared(tbvc); // auto vc = new_shared(); // auto vc = new_shared(); // auto vc = new_shared(); - window->setRootViewController(vc); + window->setRootViewController(nvc); window->makeKeyAndVisible(); // window->setBackgroundColor(UIColor::systemBackground); window->setBackgroundColor(UIColor::secondarySystemBackground); diff --git a/app/Screens/IBTestController/IBTestController.cpp b/app/Screens/IBTestController/IBTestController.cpp index ae8b6d2..02b1bbb 100644 --- a/app/Screens/IBTestController/IBTestController.cpp +++ b/app/Screens/IBTestController/IBTestController.cpp @@ -4,6 +4,10 @@ #include "IBTestController.h" +IBTestController::IBTestController() { + setTitle("IB Test VC"); +} + void IBTestController::loadView() { auto nib = UINib::fromRes("Layout/TestScreen.xml"); setView(nib->instantiate(&idStorage)); @@ -15,4 +19,4 @@ void IBTestController::viewDidLoad() { // text()->setTextColor(UIColor::systemOrange); button()->setTintColor(UIColor::systemOrange); button()->applyStyle(NXKit::UIButtonStyle::filled); -} \ No newline at end of file +} diff --git a/app/Screens/IBTestController/IBTestController.h b/app/Screens/IBTestController/IBTestController.h index 473c21a..b175680 100644 --- a/app/Screens/IBTestController/IBTestController.h +++ b/app/Screens/IBTestController/IBTestController.h @@ -9,6 +9,7 @@ using namespace NXKit; class IBTestController: public UIViewController { public: + IBTestController(); void loadView() override; void viewDidLoad() override; diff --git a/app/Screens/NXTextScreen/NXTestScreen.cpp b/app/Screens/NXTextScreen/NXNavigationController.cpp similarity index 74% rename from app/Screens/NXTextScreen/NXTestScreen.cpp rename to app/Screens/NXTextScreen/NXNavigationController.cpp index 2fdfd51..8002b22 100644 --- a/app/Screens/NXTextScreen/NXTestScreen.cpp +++ b/app/Screens/NXTextScreen/NXNavigationController.cpp @@ -2,9 +2,11 @@ // Created by Даниил Виноградов on 22.02.2025. // -#include "NXTestScreen.h" +#include "NXNavigationController.h" -class NXNavigationBar: public UIView { +#include + +class NXNavigationBar: public UIBlurView { public: NXNavigationBar() { auto headerContent = new_shared(); @@ -68,7 +70,7 @@ private: std::shared_ptr _titleLabel; }; -class NXFooterBar: public UIView { +class NXFooterBar: public UIBlurView { public: NXFooterBar() { auto headerContent = new_shared(); @@ -78,7 +80,7 @@ public: _titleLabel = new_shared(); _imageView->setContentMode(UIViewContentMode::center); - _imageView->setImage(UIImage::fromRes("img/sys/battery_back_light.png", 1)); + _imageView->setImage(UIImage::fromRes("img/sys/battery_back_dark.png", 1)); _titleLabel->setFontSize(21.5); @@ -131,6 +133,10 @@ private: std::shared_ptr _titleLabel; }; +NXNavigationController::NXNavigationController(const std::shared_ptr& rootViewController) { + _viewControllers.push_back(rootViewController); +} + void NXNavigationController::loadView() { auto view = new_shared(); view->setBackgroundColor(UIColor::systemBackground); @@ -156,5 +162,42 @@ void NXNavigationController::loadView() { } void NXNavigationController::viewDidLoad() { + UIViewController::viewDidLoad(); + updatePresentedViewController(false); +} -} \ No newline at end of file +void NXNavigationController::viewDidLayoutSubviews() { + UIViewController::viewDidLayoutSubviews(); + setAdditionalSafeAreaInsets({88, 0, 73, 0}); +} + +void NXNavigationController::setViewControllers(std::vector> viewControllers, bool animated) { + _viewControllers = std::move(viewControllers); + updatePresentedViewController(animated); +} + +std::shared_ptr NXNavigationController::topViewController() { + if (_viewControllers.empty()) return nullptr; + return _viewControllers.back(); +} + +void NXNavigationController::updatePresentedViewController(bool animated) { + if (presentedViewController != nullptr) { + presentedViewController->willMoveToParent(nullptr); + presentedViewController->view()->removeFromSuperview(); + presentedViewController->removeFromParent(); + } + + presentedViewController = topViewController(); + if (presentedViewController == nullptr) return; + + addChild(presentedViewController); + view()->insertSubviewAt(presentedViewController->view(), 0); + presentedViewController->didMoveToParent(shared_from_this()); + + presentedViewController->view()->configureLayout([](std::shared_ptr layout) { + layout->setWidth(100_percent); + layout->setHeight(100_percent); + layout->setPositionType(YGPositionTypeAbsolute); + }); +} diff --git a/app/Screens/NXTextScreen/NXNavigationController.h b/app/Screens/NXTextScreen/NXNavigationController.h new file mode 100644 index 0000000..572722d --- /dev/null +++ b/app/Screens/NXTextScreen/NXNavigationController.h @@ -0,0 +1,27 @@ +// +// Created by Даниил Виноградов on 22.02.2025. +// + +#pragma once +#include + +using namespace NXKit; + +class NXNavigationController: public UIViewController { +public: + NXNavigationController(const std::shared_ptr& rootViewController); + + void loadView() override; + void viewDidLoad() override; + + void viewDidLayoutSubviews() override; + + std::vector> viewControllers() const { return _viewControllers; } + void setViewControllers(std::vector> viewControllers, bool animated); + + std::shared_ptr topViewController(); +private: + std::shared_ptr presentedViewController; + std::vector> _viewControllers; + void updatePresentedViewController(bool animated); +}; diff --git a/app/Screens/NXTextScreen/NXTabBarController.cpp b/app/Screens/NXTextScreen/NXTabBarController.cpp new file mode 100644 index 0000000..ab3eff0 --- /dev/null +++ b/app/Screens/NXTextScreen/NXTabBarController.cpp @@ -0,0 +1,237 @@ +// +// Created by Даниил Виноградов on 22.02.2025. +// + +#include "NXTabBarController.h" + +#include + + +class NXTabBarButton: public UIControl { +public: + NXTabBarButton() { + _rectView = new_shared(); + _titleLabel = new_shared(); + + _rectView->setAlpha(0); + _rectView->setBackgroundColor(UIColor::tint); + + _titleLabel->setHidden(true); + _titleLabel->setFontSize(22); +// _titleLabel->setFontWeight(600); + + addSubview(_rectView); + addSubview(_titleLabel); + + _rectView->setAutolayoutEnabled(true); + _titleLabel->setAutolayoutEnabled(true); + + _rectView->configureLayout([](const std::shared_ptr& layout) { + layout->setWidth(4_pt); + layout->setMarginVertical(9_pt); + }); + + configureLayout([](const std::shared_ptr& layout) { + layout->setFlexDirection(YGFlexDirectionRow); + layout->setJustifyContent(YGJustifyFlexStart); + layout->setAlignItems(YGAlignStretch); + layout->setAllGap(8); + layout->setHeight(70_pt); + layout->setPaddingHorizontal(8_pt); + }); + + layer()->setCornerRadius(4); + layer()->setBorderWidth(4); + + std::for_each(subviews().begin(), subviews().end(), [](auto item) { + item->setUserInteractionEnabled(false); + }); + } + + void didUpdateFocusIn(UIFocusUpdateContext context, UIFocusAnimationCoordinator* coordinator) override { + UIControl::didUpdateFocusIn(context, coordinator); + + if (context.nextFocusedItem().lock() == shared_from_this()) { + willGainFocus(); + primaryAction->perform(); + } else { + willLoseFocus(); + } + } + + void willGainFocus() override { + layer()->setShadowColor(UIColor::black); + layer()->setShadowOpacity(0.4); + layer()->setShadowOffset({0, 6}); + layer()->setShadowRadius(8); + layer()->setZPosition(1); + layer()->setBorderColor(UIColor::tint); + setBackgroundColor(UIColor::secondarySystemBackground); + } + + void willLoseFocus() override { + layer()->setShadowOpacity(0); + layer()->setShadowRadius(0); + layer()->setZPosition(0); + layer()->setBorderColor(std::nullopt); + setBackgroundColor(std::nullopt); + } + + void setSelected(bool selected) override { + UIControl::setSelected(selected); + if (selected) { + _titleLabel->setTextColor(UIColor::tint); + _rectView->setAlpha(1); + } else { + _titleLabel->setTextColor(UIColor::label); + _rectView->setAlpha(0); + } + } + + void willChangeFocusHighlight(bool highlighted) override { + + } + + void willChangeHighlight(bool highlighted) override { + setAlpha(highlighted ? 0.8 : 1); + + if (highlighted) { + setBackgroundColor(UIColor::tint.withAlphaComponent(0.2f)); + } else { + setBackgroundColor(UIColor::clear); + } + } + + std::string text() { return _titleLabel->text(); } + void setText(const std::string& text) { + _titleLabel->setText(text); + _titleLabel->setHidden(text.empty()); + } + + std::shared_ptr titleLabel() { return _titleLabel; } + +private: + std::shared_ptr _titleLabel; + std::shared_ptr _rectView; +}; + +NXTabBar::NXTabBar() { + _container = new_shared(); + addSubview(_container); + + _container->configureLayout([](const std::shared_ptr& layout) { + layout->setFlexDirection(YGFlexDirectionColumn); + layout->setPaddingLeft(80_pt); + layout->setPaddingRight(30_pt); + layout->setPaddingTop(32_pt); + layout->setPaddingBottom(47_pt); + }); + + setBackgroundColor(UIColor::quaternarySystemFill); + setBounceVertically(true); +} + +void NXTabBar::setItems(const std::vector>& items) { + // Clear old items + auto _subviews = _container->subviews(); + for (const auto & subview : _subviews) { + subview->removeFromSuperview(); + } + + // Fill new items + _buttons.clear(); + for (int section = 0; section < items.size(); section++) { + std::vector> sectionButtons; + + for (int item = 0; item < items[section].size(); item++) { + auto button = new_shared(); + sectionButtons.push_back(button); + button->setAutolayoutEnabled(true); + _container->addSubview(button); + button->setText(items[section][item]); + button->primaryAction = UIAction("", [this, section, item, button]() { + if (!selectionDidChange.has_value()) return; + + this->_buttons[_selected.section()][_selected.item()]->setSelected(false); + button->setSelected(true); + + _selected = IndexPath(item, section); + this->selectionDidChange.value().perform(); + }); + } + _buttons.push_back(sectionButtons); + } +} + +NXTabBarController::NXTabBarController(const std::vector>& controllers) { + _viewControllers.push_back(controllers); +} + +NXTabBarController::NXTabBarController(const std::vector>>& controllers) { + _viewControllers = controllers; +} + +void NXTabBarController::loadView() { + auto contentView = new_shared(); + + _tabBar = new_shared(); + _tabBar->setAutolayoutEnabled(true); + contentView->addSubview(_tabBar); + + _tabBar->configureLayout([](const std::shared_ptr& layout) { + layout->setWidth(410_pt); + }); + + contentView->configureLayout([](const std::shared_ptr& layout) { + layout->setFlexDirection(YGFlexDirectionRow); + layout->setAlignItems(YGAlignStretch); + }); + + setView(contentView); + + _tabBar->selectionDidChange = UIAction("", [this]() { + this->_selected = _tabBar->selected(); + this->updateTabSelection(); + }); +} + +void NXTabBarController::viewDidLoad() { + std::vector> titles; + titles.reserve(_viewControllers.size()); + + for (const auto & section : _viewControllers) { + std::vector sections; + sections.reserve(section.size()); + + for (const auto & item : section) { + sections.push_back(item->title()); + } + titles.push_back(sections); + } + + _tabBar->setItems(titles); + updateTabSelection(); +} + +void NXTabBarController::updateTabSelection() { + auto newPresentedViewController = _viewControllers[_selected.section()][_selected.item()]; + if (newPresentedViewController == nullptr || _presentedViewController == newPresentedViewController) return; + + if (_presentedViewController != nullptr) { + _presentedViewController->willMoveToParent(nullptr); + _presentedViewController->view()->removeFromSuperview(); + _presentedViewController->removeFromParent(); + } + + _presentedViewController = newPresentedViewController; + newPresentedViewController->view()->setAutolayoutEnabled(true); + + addChild(_presentedViewController); + view()->insertSubviewAt(_presentedViewController->view(), 1); + _presentedViewController->didMoveToParent(shared_from_this()); + + _presentedViewController->view()->configureLayout([](std::shared_ptr layout) { +// layout->setPositionType(YGPositionTypeAbsolute); + layout->setFlexGrow(1); + }); +} diff --git a/app/Screens/NXTextScreen/NXTabBarController.h b/app/Screens/NXTextScreen/NXTabBarController.h new file mode 100644 index 0000000..c39f1e1 --- /dev/null +++ b/app/Screens/NXTextScreen/NXTabBarController.h @@ -0,0 +1,41 @@ +// +// Created by Даниил Виноградов on 22.02.2025. +// + + +#pragma once +#include + +using namespace NXKit; + +class NXTabBarButton; +class NXTabBar: public UIScrollView { +public: + NXTabBar(); + + std::optional selectionDidChange; + void setItems(const std::vector>& items); + + IndexPath selected() { return _selected; } +private: + IndexPath _selected; + std::shared_ptr _container; + std::vector>> _buttons; +}; + +class NXTabBarController: public UIViewController { +public: +// NXTabBarController(std::shared_ptr controller); + NXTabBarController(const std::vector>& controllers); + NXTabBarController(const std::vector>>& controllers); + + void loadView() override; + void viewDidLoad() override; +private: + std::vector>> _viewControllers; + std::shared_ptr _presentedViewController; + IndexPath _selected; + std::shared_ptr _tabBar; + + void updateTabSelection(); +}; diff --git a/app/Screens/NXTextScreen/NXTestScreen.h b/app/Screens/NXTextScreen/NXTestScreen.h deleted file mode 100644 index 4b5bdb3..0000000 --- a/app/Screens/NXTextScreen/NXTestScreen.h +++ /dev/null @@ -1,20 +0,0 @@ -// -// Created by Даниил Виноградов on 22.02.2025. -// - -#pragma once -#include - -using namespace NXKit; - -class NXNavigationController: public UIViewController { -public: - void loadView() override; - void viewDidLoad() override; -}; - -class NXTestScreen: public UIViewController { -public: - void loadView() override; - void viewDidLoad() override; -}; diff --git a/app/Screens/TestViewController/TestViewController.cpp b/app/Screens/TestViewController/TestViewController.cpp index 48bf1e8..cc29948 100644 --- a/app/Screens/TestViewController/TestViewController.cpp +++ b/app/Screens/TestViewController/TestViewController.cpp @@ -23,10 +23,10 @@ void animateCube(std::shared_ptr view) { void animateBlur(std::shared_ptr view) { UIView::animate(2.5, [view]() { - view->setBlurValue(0); + view->setBlurRadius(0); }, [view](bool res) { UIView::animate(2.5, [view]() { - view->setBlurValue(20); + view->setBlurRadius(20); }, [view](bool res) { animateBlur(view); }); @@ -76,7 +76,7 @@ void switchLabelText(std::shared_ptr label) { } TestViewController::TestViewController() { - + setTitle("Test VC"); } void TestViewController::loadView() { @@ -114,14 +114,10 @@ void TestViewController::loadView() { // animateBlur(blur); - bottomBar = new_shared(); - bottomBar->setBackgroundColor(UIColor::secondarySystemBackground); - rootView->addSubview(bottomBar); - label2 = new_shared(); label2->setText("Test text\nTry to fit me!!!"); label2->setBackgroundColor(UIColor::systemCyan); - label2->setFrame({ 100, 200, 0, 0 }); + label2->setFrame({ 150, 200, 0, 0 }); rootView->addSubview(label2); switchLabelText(label2); @@ -206,8 +202,4 @@ void TestViewController::viewDidLayoutSubviews() { dragMeViewLabel->setCenter({ bounds.midX(), bounds.midY() }); label2->sizeToFit(); - - auto frame = view()->frame(); - NXFloat bottomBarHeight = 48 + 35; //83; - bottomBar->setFrame({ 0, frame.size.height - bottomBarHeight, frame.size.width, bottomBarHeight }); } diff --git a/app/Screens/TestViewController/TestViewController.hpp b/app/Screens/TestViewController/TestViewController.hpp index 206a4ce..1538d88 100644 --- a/app/Screens/TestViewController/TestViewController.hpp +++ b/app/Screens/TestViewController/TestViewController.hpp @@ -13,5 +13,4 @@ private: std::shared_ptr dragMeViewLabel; std::shared_ptr label; std::shared_ptr label2; - std::shared_ptr bottomBar; }; diff --git a/app/Screens/YogaTestViewController/YogaTestViewController.cpp b/app/Screens/YogaTestViewController/YogaTestViewController.cpp index 185cbc3..65fdcbc 100644 --- a/app/Screens/YogaTestViewController/YogaTestViewController.cpp +++ b/app/Screens/YogaTestViewController/YogaTestViewController.cpp @@ -4,7 +4,7 @@ using namespace NXKit; YogaTestViewController::YogaTestViewController() { - + setTitle("Yoga test VC"); } void YogaTestViewController::loadView() { diff --git a/resources/Layout/TestScreen.xml b/resources/Layout/TestScreen.xml index 2e209dc..3c62baf 100644 --- a/resources/Layout/TestScreen.xml +++ b/resources/Layout/TestScreen.xml @@ -1,6 +1,4 @@