mirror of
https://github.com/XITRIX/NXKit.git
synced 2026-05-30 11:46:52 +00:00
Rendering improvements and NX screen example
This commit is contained in:
+2
-1
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -13,8 +13,8 @@ public:
|
||||
void draw(SkCanvas* context) override;
|
||||
std::shared_ptr<CALayer> 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<CALayer> presentation, std::shared_ptr<CABasicAnimation> animation, float progress) override;
|
||||
UIColor _backgroundTintColor;
|
||||
UIColor _backgroundTintColor = UIColor::clear;
|
||||
|
||||
private:
|
||||
NXFloat _blurValue = 16; // 10;
|
||||
NXFloat _blurRadius = 16; // 10;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ public:
|
||||
virtual std::shared_ptr<CABasicAnimation> actionForKey(std::string event) = 0;
|
||||
virtual void display(std::shared_ptr<CALayer> layer) = 0;
|
||||
virtual void updateCurrentEnvironment() = 0;
|
||||
virtual bool isHierarchyRoot() { return false; }
|
||||
};
|
||||
|
||||
class CALayer: public enable_shared_from_this<CALayer> {
|
||||
@@ -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<CALayer> _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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// Created by Даниил Виноградов on 23.02.2025.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Geometry.h>
|
||||
|
||||
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<int> _indexes;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -22,16 +22,21 @@ public:
|
||||
virtual void swapBuffers() = 0;
|
||||
|
||||
virtual float getScaleFactor() { return 1; }
|
||||
virtual float getExtraScaleFactor() { return 1; }
|
||||
virtual sk_sp<SkFontMgr> getFontMgr() { return fontMgr; }
|
||||
|
||||
virtual UIUserInterfaceStyle getThemeMode() { return UIUserInterfaceStyle::light; }
|
||||
|
||||
void setExtraScaleFactor(NXFloat extraScaleFactor) { _extraScaleFactor = extraScaleFactor; }
|
||||
|
||||
static std::shared_ptr<SkiaCtx> main() { return _main; }
|
||||
static std::shared_ptr<SkiaCtx> _main;
|
||||
protected:
|
||||
NXSize _size;
|
||||
NXFloat _extraScaleFactor = 1;
|
||||
sk_sp<SkFontMgr> fontMgr;
|
||||
|
||||
|
||||
private:
|
||||
friend class UIApplication;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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(); }
|
||||
|
||||
@@ -58,7 +58,6 @@ public:
|
||||
void pressesBegan(std::set<std::shared_ptr<UIPress>> pressees, std::shared_ptr<UIPressesEvent> event) override;
|
||||
void pressesEnded(std::set<std::shared_ptr<UIPress>> pressees, std::shared_ptr<UIPressesEvent> 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<UIView> hitTest(NXPoint point, UIEvent *withEvent) override;
|
||||
|
||||
void setBaseScaleMultiplier(NXFloat baseScaleMultiplier);
|
||||
[[nodiscard]] NXFloat baseScaleMultiplier() const { return _baseScaleMultiplier; }
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <DispatchQueue.h>
|
||||
#include <IndexPath.h>
|
||||
#include <UIBlurView.h>
|
||||
#include <UIButton.h>
|
||||
#include <UIControl.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<UITraitCollection> 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;
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include <UIView.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace NXKit {
|
||||
|
||||
class UIViewController: public UIResponder, public UITraitEnvironment, public UIFocusEnvironment, public enable_shared_from_this<UIViewController> {
|
||||
@@ -29,6 +31,9 @@ public:
|
||||
std::vector<std::shared_ptr<UIViewController>> children() { return _children; }
|
||||
std::weak_ptr<UIViewController> parent() { return _parent; }
|
||||
|
||||
std::string title() { return _title; }
|
||||
void setTitle(std::string title) { _title = std::move(title); }
|
||||
|
||||
void addChild(const std::shared_ptr<UIViewController>& child);
|
||||
virtual void willMoveToParent(const std::shared_ptr<UIViewController>& parent);
|
||||
virtual void didMoveToParent(std::shared_ptr<UIViewController> 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<UIViewController>& otherViewController, bool animated, const std::function<void()>& completion = [](){});
|
||||
@@ -49,7 +54,7 @@ public:
|
||||
void traitCollectionDidChange(std::shared_ptr<UITraitCollection> previousTraitCollection) override;
|
||||
|
||||
// Focus
|
||||
virtual std::shared_ptr<UIFocusEnvironment> parentFocusEnvironment() override;
|
||||
std::shared_ptr<UIFocusEnvironment> parentFocusEnvironment() override;
|
||||
|
||||
protected:
|
||||
virtual void makeViewAppear(bool animated, std::shared_ptr<UIViewController> presentingViewController, std::function<void()> 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<UIViewController> _presentedViewController;
|
||||
std::weak_ptr<UIViewController> _presentingViewController;
|
||||
|
||||
@@ -16,6 +16,8 @@ public:
|
||||
UIWindow();
|
||||
~UIWindow() override;
|
||||
|
||||
bool isHierarchyRoot() override { return true; }
|
||||
|
||||
std::shared_ptr<UIWindow> window() override;
|
||||
std::shared_ptr<UIFocusSystem> focusSystem() { return _focusSystem; }
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ public:
|
||||
// float getScaleFactor() override;
|
||||
sk_sp<GrDirectContext> directContext() override { return context; }
|
||||
|
||||
UIUserInterfaceStyle getThemeMode() override { return UIUserInterfaceStyle::light; }
|
||||
UIUserInterfaceStyle getThemeMode() override;
|
||||
|
||||
virtual void swapBuffers() override;
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ CABlurLayer::CABlurLayer(): CALayer() {
|
||||
}
|
||||
|
||||
CABlurLayer::CABlurLayer(CABlurLayer* layer): CALayer(layer) {
|
||||
_blurValue = layer->_blurValue;
|
||||
_blurRadius = layer->_blurRadius;
|
||||
}
|
||||
|
||||
std::shared_ptr<CALayer> CABlurLayer::copy() {
|
||||
@@ -24,7 +24,7 @@ void CABlurLayer::draw(SkCanvas* context) {
|
||||
blurPaint.setStyle(SkPaint::kFill_Style);
|
||||
// blurPaint.setStrokeWidth(10);
|
||||
|
||||
sk_sp<SkImageFilter> newBlurFilter = SkImageFilters::Blur(_blurValue, _blurValue, SkTileMode::kClamp, nullptr);
|
||||
sk_sp<SkImageFilter> 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<AnimatableProperty> 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<CALayer> 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<NXFloat>(fromValue);
|
||||
if (!start.has_value()) { return; }
|
||||
|
||||
auto end = any_optional_cast<NXFloat>(animation->toValue);
|
||||
if (!end.has_value()) end = this->_blurValue;
|
||||
if (!end.has_value()) end = this->_blurRadius;
|
||||
|
||||
std::static_pointer_cast<CABlurLayer>(presentation)->setBlurValue(start.value() + (end.value() - start.value()) * progress);
|
||||
std::static_pointer_cast<CABlurLayer>(presentation)->setBlurRadius(start.value() + (end.value() - start.value()) * progress);
|
||||
}
|
||||
|
||||
CALayer::update(presentation, animation, progress);
|
||||
|
||||
@@ -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<CGImage> contents) {
|
||||
_contents = std::move(contents);
|
||||
CALayer::setLayerTreeIsDirty();
|
||||
setLayerTreeDirtyIfNeeded();
|
||||
}
|
||||
|
||||
void CALayer::setMask(const std::shared_ptr<CALayer>& mask) {
|
||||
@@ -166,14 +194,16 @@ void CALayer::addSublayer(const std::shared_ptr<CALayer>& 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<CALayer>& 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<CALayer>& layer, const std::shared_ptr<CALayer>& sibling) {
|
||||
@@ -187,7 +217,8 @@ void CALayer::insertSublayerBelow(const std::shared_ptr<CALayer>& 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<CABasicAnimation>(actionForKey(animationKey));
|
||||
@@ -491,6 +523,8 @@ std::optional<AnimatableProperty> 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<CALayer> presentation, std::shared_ptr<CABa
|
||||
auto end = any_optional_cast<std::optional<UIColor>>(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<CALayer> presentation, std::shared_ptr<CABa
|
||||
auto end = any_optional_cast<std::optional<UIColor>>(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<CALayer> presentation, std::shared_ptr<CABa
|
||||
auto end = any_optional_cast<std::optional<UIColor>>(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<CALayer> presentation, std::shared_ptr<CABa
|
||||
|
||||
presentation->setPosition(start.value() + (end.value() - start.value()) * progress);
|
||||
}
|
||||
if (keyPath == "contentsScale") {
|
||||
auto start = any_optional_cast<float>(fromValue);
|
||||
if (!start.has_value()) { return; }
|
||||
|
||||
auto end = any_optional_cast<float>(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<float>(fromValue);
|
||||
if (!start.has_value()) { return; }
|
||||
|
||||
auto end = any_optional_cast<float>(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<NXPoint>(fromValue);
|
||||
if (!start.has_value()) { return; }
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
//
|
||||
// Created by Даниил Виноградов on 23.02.2025.
|
||||
//
|
||||
|
||||
#include <IndexPath.h>
|
||||
|
||||
using namespace NXKit;
|
||||
|
||||
IndexPath::IndexPath(int item, int section) {
|
||||
_indexes.push_back(section);
|
||||
_indexes.push_back(item);
|
||||
}
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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<UIApplicationDelegate>& appDelegate)
|
||||
UIApplication::shared = new_shared<UIApplication>();
|
||||
SkiaCtx::_main = MakeSkiaCtx();
|
||||
|
||||
#ifdef PLATFORM_IOS
|
||||
SkiaCtx::_main->setExtraScaleFactor(1.3f);
|
||||
#endif
|
||||
|
||||
UIApplication::shared->delegate = appDelegate;
|
||||
|
||||
appDelegate->applicationNeedsXIBRegistration(UIApplication::shared.get());
|
||||
|
||||
@@ -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<UIImage>& 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<UIView> 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);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ void UIImageView::updateTextureFromImage() {
|
||||
setBounds(bounds);
|
||||
} else {
|
||||
layer()->setContents(nullptr);
|
||||
layer()->setContentsScale(SkiaCtx::main()->getScaleFactor());
|
||||
layer()->setContentsScale(UITraitCollection::current()->displayScale());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<CGImage>(bitmap.asImage()));
|
||||
layer()->setContentsScale(scale);
|
||||
}
|
||||
|
||||
@@ -274,6 +274,7 @@ void UIView::addSubview(const std::shared_ptr<UIView>& view) {
|
||||
_layer->addSublayer(view->_layer);
|
||||
_subviews.push_back(view);
|
||||
view->setSuperview(this->shared_from_this());
|
||||
|
||||
view->setNeedsUpdateSafeAreaInsets();
|
||||
}
|
||||
|
||||
@@ -285,12 +286,32 @@ std::shared_ptr<UIWindow> UIView::window() {
|
||||
void UIView::setSuperview(const std::shared_ptr<UIView>& superview) {
|
||||
_superview = superview;
|
||||
|
||||
if (superview)
|
||||
traitCollectionDidChange(superview->traitCollection());
|
||||
|
||||
if (!_tintColor.has_value())
|
||||
tintColorDidChange();
|
||||
}
|
||||
|
||||
void UIView::insertSubviewAt(const std::shared_ptr<UIView>& 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<UIView>& view, const std::shared_ptr<UIView>& 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,12 @@ void UIViewController::setView(std::shared_ptr<UIView> 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<UIViewController>& child) {
|
||||
_children.push_back(child);
|
||||
child->willMoveToParent(weak_from_this().lock());
|
||||
|
||||
child->_traitCollection = _traitCollection;
|
||||
child->traitCollectionDidChange(nullptr);
|
||||
}
|
||||
|
||||
void UIViewController::willMoveToParent(const std::shared_ptr<UIViewController>& parent) {
|
||||
@@ -78,17 +87,17 @@ void UIViewController::didMoveToParent(std::shared_ptr<UIViewController> 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<UITraitCollectio
|
||||
}
|
||||
for (auto child : _children) {
|
||||
child->_traitCollection = _traitCollection;
|
||||
child->UITraitEnvironment::traitCollectionDidChange(previousTraitCollection);
|
||||
child->traitCollectionDidChange(previousTraitCollection);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <YGLayout.h>
|
||||
#include <UIView.h>
|
||||
#include <UIViewController.h>
|
||||
|
||||
namespace NXKit {
|
||||
|
||||
@@ -183,6 +184,10 @@ void YGLayout::YGApplyLayoutToViewHierarchy(const std::shared_ptr<UIView>&view,
|
||||
YGApplyLayoutToViewHierarchy(i, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (!view->_parentController.expired()) {
|
||||
view->_parentController.lock()->viewDidLayoutSubviews();
|
||||
}
|
||||
}
|
||||
|
||||
float YGLayout::YGRoundPixelValue(float value) {
|
||||
|
||||
@@ -43,12 +43,11 @@ NXSize SkiaCtx_ios::getSize() {
|
||||
auto layer = window.layer.presentationLayer;
|
||||
if (layer == NULL) layer = window.layer;
|
||||
NXSize size = { static_cast<NXFloat>(layer.bounds.size.width), static_cast<NXFloat>(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<SkSurface> 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<SkSurface> 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);
|
||||
|
||||
@@ -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<SkSurface> SkiaCtx_switch::getBackbufferSurface() {
|
||||
auto size = getSize();
|
||||
if (_size.width == size.width && _size.height == size.height && surface != nullptr) { return surface; }
|
||||
|
||||
+9
-5
@@ -5,7 +5,8 @@
|
||||
#include <Screens/TestViewController/TestViewController.hpp>
|
||||
#include <Screens/IBTestController/IBTestController.h>
|
||||
#include <Screens/YogaTestViewController/YogaTestViewController.hpp>
|
||||
#include <Screens/NXTextScreen/NXTestScreen.h>
|
||||
#include <Screens/NXTextScreen/NXNavigationController.h>
|
||||
#include <Screens/NXTextScreen/NXTabBarController.h>
|
||||
// #include <NavigationViewController/NavigationViewController.hpp>
|
||||
// #include <TextViewController/TextViewController.hpp>
|
||||
#include <romfs/romfs.hpp>
|
||||
@@ -16,13 +17,16 @@ bool UIApplicationDelegate::applicationDidFinishLaunchingWithOptions(UIApplicati
|
||||
DEFAULT_ROMFS_REGISTRATION
|
||||
window = new_shared<UIWindow>();
|
||||
|
||||
// auto vc = new_shared<TestViewController>();
|
||||
// auto vc = new_shared<IBTestController>();
|
||||
auto vc = new_shared<NXNavigationController>();
|
||||
auto vc1 = new_shared<TestViewController>();
|
||||
auto vc = new_shared<IBTestController>();
|
||||
std::vector<std::shared_ptr<UIViewController>> vcs = { vc, vc1 };
|
||||
|
||||
auto tbvc = new_shared<NXTabBarController>( vcs );
|
||||
auto nvc = new_shared<NXNavigationController>(tbvc);
|
||||
// auto vc = new_shared<YogaTestViewController>();
|
||||
// auto vc = new_shared<NavigationViewController>();
|
||||
// auto vc = new_shared<TextViewController>();
|
||||
window->setRootViewController(vc);
|
||||
window->setRootViewController(nvc);
|
||||
window->makeKeyAndVisible();
|
||||
// window->setBackgroundColor(UIColor::systemBackground);
|
||||
window->setBackgroundColor(UIColor::secondarySystemBackground);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ using namespace NXKit;
|
||||
|
||||
class IBTestController: public UIViewController {
|
||||
public:
|
||||
IBTestController();
|
||||
void loadView() override;
|
||||
void viewDidLoad() override;
|
||||
|
||||
|
||||
+48
-5
@@ -2,9 +2,11 @@
|
||||
// Created by Даниил Виноградов on 22.02.2025.
|
||||
//
|
||||
|
||||
#include "NXTestScreen.h"
|
||||
#include "NXNavigationController.h"
|
||||
|
||||
class NXNavigationBar: public UIView {
|
||||
#include <utility>
|
||||
|
||||
class NXNavigationBar: public UIBlurView {
|
||||
public:
|
||||
NXNavigationBar() {
|
||||
auto headerContent = new_shared<UIView>();
|
||||
@@ -68,7 +70,7 @@ private:
|
||||
std::shared_ptr<UILabel> _titleLabel;
|
||||
};
|
||||
|
||||
class NXFooterBar: public UIView {
|
||||
class NXFooterBar: public UIBlurView {
|
||||
public:
|
||||
NXFooterBar() {
|
||||
auto headerContent = new_shared<UIView>();
|
||||
@@ -78,7 +80,7 @@ public:
|
||||
_titleLabel = new_shared<UILabel>();
|
||||
|
||||
_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<UILabel> _titleLabel;
|
||||
};
|
||||
|
||||
NXNavigationController::NXNavigationController(const std::shared_ptr<UIViewController>& rootViewController) {
|
||||
_viewControllers.push_back(rootViewController);
|
||||
}
|
||||
|
||||
void NXNavigationController::loadView() {
|
||||
auto view = new_shared<UIView>();
|
||||
view->setBackgroundColor(UIColor::systemBackground);
|
||||
@@ -156,5 +162,42 @@ void NXNavigationController::loadView() {
|
||||
}
|
||||
|
||||
void NXNavigationController::viewDidLoad() {
|
||||
UIViewController::viewDidLoad();
|
||||
updatePresentedViewController(false);
|
||||
}
|
||||
|
||||
}
|
||||
void NXNavigationController::viewDidLayoutSubviews() {
|
||||
UIViewController::viewDidLayoutSubviews();
|
||||
setAdditionalSafeAreaInsets({88, 0, 73, 0});
|
||||
}
|
||||
|
||||
void NXNavigationController::setViewControllers(std::vector<std::shared_ptr<UIViewController>> viewControllers, bool animated) {
|
||||
_viewControllers = std::move(viewControllers);
|
||||
updatePresentedViewController(animated);
|
||||
}
|
||||
|
||||
std::shared_ptr<UIViewController> 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<YGLayout> layout) {
|
||||
layout->setWidth(100_percent);
|
||||
layout->setHeight(100_percent);
|
||||
layout->setPositionType(YGPositionTypeAbsolute);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// Created by Даниил Виноградов on 22.02.2025.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#include <UIKit.h>
|
||||
|
||||
using namespace NXKit;
|
||||
|
||||
class NXNavigationController: public UIViewController {
|
||||
public:
|
||||
NXNavigationController(const std::shared_ptr<UIViewController>& rootViewController);
|
||||
|
||||
void loadView() override;
|
||||
void viewDidLoad() override;
|
||||
|
||||
void viewDidLayoutSubviews() override;
|
||||
|
||||
std::vector<std::shared_ptr<UIViewController>> viewControllers() const { return _viewControllers; }
|
||||
void setViewControllers(std::vector<std::shared_ptr<UIViewController>> viewControllers, bool animated);
|
||||
|
||||
std::shared_ptr<UIViewController> topViewController();
|
||||
private:
|
||||
std::shared_ptr<UIViewController> presentedViewController;
|
||||
std::vector<std::shared_ptr<UIViewController>> _viewControllers;
|
||||
void updatePresentedViewController(bool animated);
|
||||
};
|
||||
@@ -0,0 +1,237 @@
|
||||
//
|
||||
// Created by Даниил Виноградов on 22.02.2025.
|
||||
//
|
||||
|
||||
#include "NXTabBarController.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
|
||||
class NXTabBarButton: public UIControl {
|
||||
public:
|
||||
NXTabBarButton() {
|
||||
_rectView = new_shared<UIView>();
|
||||
_titleLabel = new_shared<UILabel>();
|
||||
|
||||
_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<YGLayout>& layout) {
|
||||
layout->setWidth(4_pt);
|
||||
layout->setMarginVertical(9_pt);
|
||||
});
|
||||
|
||||
configureLayout([](const std::shared_ptr<YGLayout>& 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<UILabel> titleLabel() { return _titleLabel; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<UILabel> _titleLabel;
|
||||
std::shared_ptr<UIView> _rectView;
|
||||
};
|
||||
|
||||
NXTabBar::NXTabBar() {
|
||||
_container = new_shared<UIView>();
|
||||
addSubview(_container);
|
||||
|
||||
_container->configureLayout([](const std::shared_ptr<YGLayout>& 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<std::vector<std::string>>& 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<std::shared_ptr<NXTabBarButton>> sectionButtons;
|
||||
|
||||
for (int item = 0; item < items[section].size(); item++) {
|
||||
auto button = new_shared<NXTabBarButton>();
|
||||
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<std::shared_ptr<UIViewController>>& controllers) {
|
||||
_viewControllers.push_back(controllers);
|
||||
}
|
||||
|
||||
NXTabBarController::NXTabBarController(const std::vector<std::vector<std::shared_ptr<UIViewController>>>& controllers) {
|
||||
_viewControllers = controllers;
|
||||
}
|
||||
|
||||
void NXTabBarController::loadView() {
|
||||
auto contentView = new_shared<UIView>();
|
||||
|
||||
_tabBar = new_shared<NXTabBar>();
|
||||
_tabBar->setAutolayoutEnabled(true);
|
||||
contentView->addSubview(_tabBar);
|
||||
|
||||
_tabBar->configureLayout([](const std::shared_ptr<YGLayout>& layout) {
|
||||
layout->setWidth(410_pt);
|
||||
});
|
||||
|
||||
contentView->configureLayout([](const std::shared_ptr<YGLayout>& layout) {
|
||||
layout->setFlexDirection(YGFlexDirectionRow);
|
||||
layout->setAlignItems(YGAlignStretch);
|
||||
});
|
||||
|
||||
setView(contentView);
|
||||
|
||||
_tabBar->selectionDidChange = UIAction("", [this]() {
|
||||
this->_selected = _tabBar->selected();
|
||||
this->updateTabSelection();
|
||||
});
|
||||
}
|
||||
|
||||
void NXTabBarController::viewDidLoad() {
|
||||
std::vector<std::vector<std::string>> titles;
|
||||
titles.reserve(_viewControllers.size());
|
||||
|
||||
for (const auto & section : _viewControllers) {
|
||||
std::vector<std::string> 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<YGLayout> layout) {
|
||||
// layout->setPositionType(YGPositionTypeAbsolute);
|
||||
layout->setFlexGrow(1);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// Created by Даниил Виноградов on 22.02.2025.
|
||||
//
|
||||
|
||||
|
||||
#pragma once
|
||||
#include <UIKit.h>
|
||||
|
||||
using namespace NXKit;
|
||||
|
||||
class NXTabBarButton;
|
||||
class NXTabBar: public UIScrollView {
|
||||
public:
|
||||
NXTabBar();
|
||||
|
||||
std::optional<UIAction> selectionDidChange;
|
||||
void setItems(const std::vector<std::vector<std::string>>& items);
|
||||
|
||||
IndexPath selected() { return _selected; }
|
||||
private:
|
||||
IndexPath _selected;
|
||||
std::shared_ptr<UIView> _container;
|
||||
std::vector<std::vector<std::shared_ptr<NXTabBarButton>>> _buttons;
|
||||
};
|
||||
|
||||
class NXTabBarController: public UIViewController {
|
||||
public:
|
||||
// NXTabBarController(std::shared_ptr<UIViewController> controller);
|
||||
NXTabBarController(const std::vector<std::shared_ptr<UIViewController>>& controllers);
|
||||
NXTabBarController(const std::vector<std::vector<std::shared_ptr<UIViewController>>>& controllers);
|
||||
|
||||
void loadView() override;
|
||||
void viewDidLoad() override;
|
||||
private:
|
||||
std::vector<std::vector<std::shared_ptr<UIViewController>>> _viewControllers;
|
||||
std::shared_ptr<UIViewController> _presentedViewController;
|
||||
IndexPath _selected;
|
||||
std::shared_ptr<NXTabBar> _tabBar;
|
||||
|
||||
void updateTabSelection();
|
||||
};
|
||||
@@ -1,20 +0,0 @@
|
||||
//
|
||||
// Created by Даниил Виноградов on 22.02.2025.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#include <UIKit.h>
|
||||
|
||||
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;
|
||||
};
|
||||
@@ -23,10 +23,10 @@ void animateCube(std::shared_ptr<UIView> view) {
|
||||
|
||||
void animateBlur(std::shared_ptr<UIBlurView> 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<UILabel> label) {
|
||||
}
|
||||
|
||||
TestViewController::TestViewController() {
|
||||
|
||||
setTitle("Test VC");
|
||||
}
|
||||
|
||||
void TestViewController::loadView() {
|
||||
@@ -114,14 +114,10 @@ void TestViewController::loadView() {
|
||||
|
||||
// animateBlur(blur);
|
||||
|
||||
bottomBar = new_shared<UIView>();
|
||||
bottomBar->setBackgroundColor(UIColor::secondarySystemBackground);
|
||||
rootView->addSubview(bottomBar);
|
||||
|
||||
label2 = new_shared<UILabel>();
|
||||
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 });
|
||||
}
|
||||
|
||||
@@ -13,5 +13,4 @@ private:
|
||||
std::shared_ptr<NXKit::UILabel> dragMeViewLabel;
|
||||
std::shared_ptr<NXKit::UILabel> label;
|
||||
std::shared_ptr<NXKit::UILabel> label2;
|
||||
std::shared_ptr<NXKit::UIView> bottomBar;
|
||||
};
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
using namespace NXKit;
|
||||
|
||||
YogaTestViewController::YogaTestViewController() {
|
||||
|
||||
setTitle("Yoga test VC");
|
||||
}
|
||||
|
||||
void YogaTestViewController::loadView() {
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
<UIView
|
||||
width="100%"
|
||||
height="100%"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
gap="16">
|
||||
|
||||
Reference in New Issue
Block a user