Rendering improvements and NX screen example

This commit is contained in:
Daniil Vinogradov
2025-02-23 19:56:34 +01:00
parent 88a3fcd5f7
commit fbfd241521
40 changed files with 632 additions and 127 deletions
+2 -1
View File
@@ -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
+1
View File
@@ -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
+4 -4
View File
@@ -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;
};
}
+13 -1
View File
@@ -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;
};
}
+21
View File
@@ -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;
};
}
+5
View File
@@ -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;
};
+1
View File
@@ -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;
+2 -2
View File
@@ -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(); }
+4 -1
View File
@@ -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; }
+1
View File
@@ -5,6 +5,7 @@
#pragma once
#include <DispatchQueue.h>
#include <IndexPath.h>
#include <UIBlurView.h>
#include <UIButton.h>
#include <UIControl.h>
+1 -3
View File
@@ -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;
+8 -2
View File
@@ -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;
+2
View File
@@ -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 -10
View File
@@ -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);
+63 -8
View File
@@ -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; }
+12
View File
@@ -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);
}
+2
View File
@@ -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");
+5 -1
View File
@@ -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());
+3 -2
View File
@@ -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;
}
}
}
+25 -20
View File
@@ -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);
});
}
+1 -1
View File
@@ -34,7 +34,7 @@ void UIImageView::updateTextureFromImage() {
setBounds(bounds);
} else {
layer()->setContents(nullptr);
layer()->setContentsScale(SkiaCtx::main()->getScaleFactor());
layer()->setContentsScale(UITraitCollection::current()->displayScale());
}
}
+9 -6
View File
@@ -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);
}
+24 -2
View File
@@ -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();
}
}
+16 -7
View File
@@ -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);
}
}
+5
View File
@@ -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
View File
@@ -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;
@@ -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();
};
-20
View File
@@ -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() {
-2
View File
@@ -1,6 +1,4 @@
<UIView
width="100%"
height="100%"
justifyContent="center"
alignItems="center"
gap="16">