Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 13:58:55 +01:00
parent 4af19165ec
commit 68073add76
12458 changed files with 12350765 additions and 2 deletions

View file

@ -0,0 +1,13 @@
#import "MWMViewController.h"
#include "storage/storage_defines.hpp"
@class MapViewController;
@interface MWMMapDownloadDialog : UIView
+ (instancetype)dialogForController:(MapViewController *)controller;
- (void)processViewportCountryEvent:(storage::CountryId const &)countryId;
@end

View file

@ -0,0 +1,299 @@
#import "MWMMapDownloadDialog.h"
#import <SafariServices/SafariServices.h>
#import "CLLocation+Mercator.h"
#import "MWMCircularProgress.h"
#import "MWMStorage+UI.h"
#import "MapViewController.h"
#import "SwiftBridge.h"
#include <CoreApi/Framework.h>
#include "storage/country_info_getter.hpp"
#include "platform/downloader_defines.hpp"
#include "platform/local_country_file_utils.hpp"
#include "platform/network_policy.hpp"
#include "platform/preferred_languages.hpp"
#include "base/assert.hpp"
namespace {
BOOL canAutoDownload(storage::CountryId const &countryId) {
if (![MWMSettings autoDownloadEnabled])
return NO;
if (GetPlatform().ConnectionStatus() != Platform::EConnectionType::CONNECTION_WIFI)
return NO;
CLLocation *lastLocation = [MWMLocationManager lastLocation];
if (!lastLocation)
return NO;
auto const &countryInfoGetter = GetFramework().GetCountryInfoGetter();
if (countryId != countryInfoGetter.GetRegionCountryId(lastLocation.mercator))
return NO;
return YES;
}
} // namespace
using namespace storage;
@interface MWMMapDownloadDialog () <MWMStorageObserver, MWMCircularProgressProtocol>
@property(strong, nonatomic) IBOutlet UILabel *explanationTitle;
@property(strong, nonatomic) IBOutlet UILabel *explanationText;
@property(strong, nonatomic) IBOutlet UILabel *parentNode;
@property(strong, nonatomic) IBOutlet UILabel *node;
@property(strong, nonatomic) IBOutlet UILabel *nodeSize;
@property(strong, nonatomic) IBOutlet NSLayoutConstraint *nodeTop;
@property(strong, nonatomic) IBOutlet UIButton *downloadButton;
@property(strong, nonatomic) IBOutlet NSLayoutConstraint *explanationHeight;
@property(strong, nonatomic) IBOutlet UIView *progressWrapper;
@property(weak, nonatomic) MapViewController *controller;
@property(nonatomic) MWMCircularProgress *progress;
@property(nonatomic) NSMutableArray<NSDate *> *skipDownloadTimes;
@property(nonatomic) BOOL isAutoDownloadCancelled;
@end
NSString * const kOfflineMapsExplained = @"OfflineMapsExplained";
@implementation MWMMapDownloadDialog {
CountryId m_countryId;
CountryId m_autoDownloadCountryId;
}
+ (instancetype)dialogForController:(MapViewController *)controller {
MWMMapDownloadDialog *dialog = [NSBundle.mainBundle loadNibNamed:[self className] owner:nil options:nil].firstObject;
dialog.controller = controller;
return dialog;
}
- (void)configDialog {
auto &f = GetFramework();
auto const &s = f.GetStorage();
auto const &p = f.GetDownloadingPolicy();
NodeAttrs nodeAttrs;
s.GetNodeAttrs(m_countryId, nodeAttrs);
if (!nodeAttrs.m_present && ![MWMRouter isRoutingActive]) {
BOOL const isMultiParent = nodeAttrs.m_parentInfo.size() > 1;
BOOL const noParrent = (nodeAttrs.m_parentInfo[0].m_id == s.GetRootId());
BOOL const hideParent = (noParrent || isMultiParent);
self.parentNode.hidden = hideParent;
if (hideParent) {
[NSLayoutConstraint activateConstraints:@[self.nodeTop]];
} else {
[NSLayoutConstraint deactivateConstraints:@[self.nodeTop]];
self.parentNode.text = @(nodeAttrs.m_topmostParentInfo[0].m_localName.c_str());
self.parentNode.textColor = [UIColor blackSecondaryText];
}
self.node.text = @(nodeAttrs.m_nodeLocalName.c_str());
self.node.textColor = [UIColor blackPrimaryText];
self.nodeSize.hidden = NO;
self.nodeSize.textColor = [UIColor blackSecondaryText];
self.nodeSize.text = formattedSize(nodeAttrs.m_mwmSize);
self.nodeSize.font = [UIFont medium14].monospaced;
if ([NSUserDefaults.standardUserDefaults integerForKey:kOfflineMapsExplained] > 1) {
[NSLayoutConstraint activateConstraints:@[self.explanationHeight]];
} else {
[NSLayoutConstraint deactivateConstraints:@[self.explanationHeight]];
}
self.explanationTitle.text = L(@"offline_explanation_title");
self.explanationText.text = L(@"offline_explanation_text");
switch (nodeAttrs.m_status) {
case NodeStatus::NotDownloaded:
case NodeStatus::Partly: {
MapViewController *controller = self.controller;
BOOL const isMapVisible = [controller.navigationController.topViewController isEqual:controller];
if (isMapVisible && !self.isAutoDownloadCancelled && canAutoDownload(m_countryId)) {
NSUserDefaults *userDefaults = NSUserDefaults.standardUserDefaults;
NSInteger offlineMapsExplained = [userDefaults integerForKey:kOfflineMapsExplained] + 1;
[userDefaults setInteger:offlineMapsExplained forKey:kOfflineMapsExplained];
m_autoDownloadCountryId = m_countryId;
[[MWMStorage sharedStorage] downloadNode:@(m_countryId.c_str())
onSuccess:^{
[self showInQueue];
}];
} else {
m_autoDownloadCountryId = kInvalidCountryId;
[self showDownloadRequest];
}
[[MWMCarPlayService shared] showNoMapAlert];
break;
}
case NodeStatus::Downloading:
if (nodeAttrs.m_downloadingProgress.m_bytesTotal != 0)
[self showDownloading:(CGFloat)nodeAttrs.m_downloadingProgress.m_bytesDownloaded /
nodeAttrs.m_downloadingProgress.m_bytesTotal];
break;
case NodeStatus::Applying:
case NodeStatus::InQueue:
[self showInQueue];
break;
case NodeStatus::Undefined:
case NodeStatus::Error:
if (p.IsAutoRetryDownloadFailed()) {
[self showError:nodeAttrs.m_error];
} else {
[self showInQueue];
}
break;
case NodeStatus::OnDisk:
case NodeStatus::OnDiskOutOfDate:
[self removeFromSuperview];
break;
}
} else {
[self removeFromSuperview];
}
if (self.superview)
[self setNeedsLayout];
}
- (void)addToSuperview {
if (self.superview)
return;
MapViewController *controller = self.controller;
[controller.view insertSubview:self aboveSubview:controller.controlsView];
[[MWMStorage sharedStorage] addObserver:self];
// Center dialog in the parent view.
[self.centerXAnchor constraintEqualToAnchor:controller.view.centerXAnchor].active = YES;
[self.centerYAnchor constraintEqualToAnchor:controller.view.centerYAnchor].active = YES;
}
- (void)removeFromSuperview {
[[MWMCarPlayService shared] hideNoMapAlert];
self.progress.state = MWMCircularProgressStateNormal;
[[MWMStorage sharedStorage] removeObserver:self];
[super removeFromSuperview];
}
- (void)showError:(NodeErrorCode)errorCode {
if (errorCode == NodeErrorCode::NoError)
return;
self.nodeSize.textColor = [UIColor red];
self.nodeSize.text = L(@"country_status_download_failed");
self.downloadButton.hidden = YES;
self.progressWrapper.hidden = NO;
self.progress.state = MWMCircularProgressStateFailed;
MWMAlertViewController *avc = self.controller.alertController;
[self addToSuperview];
auto const retryBlock = ^{
[self showInQueue];
[[MWMStorage sharedStorage] retryDownloadNode:@(self->m_countryId.c_str())];
};
auto const cancelBlock = ^{
[[MWMStorage sharedStorage] cancelDownloadNode:@(self->m_countryId.c_str())];
};
switch (errorCode) {
case NodeErrorCode::NoError:
break;
case NodeErrorCode::UnknownError:
[avc presentDownloaderInternalErrorAlertWithOkBlock:retryBlock cancelBlock:cancelBlock];
break;
case NodeErrorCode::OutOfMemFailed:
[avc presentDownloaderNotEnoughSpaceAlert];
break;
case NodeErrorCode::NoInetConnection:
[avc presentDownloaderNoConnectionAlertWithOkBlock:retryBlock cancelBlock:cancelBlock];
break;
}
}
- (void)showDownloadRequest {
self.downloadButton.hidden = NO;
self.progressWrapper.hidden = YES;
[self addToSuperview];
}
- (void)showDownloading:(CGFloat)progress {
self.nodeSize.textColor = [UIColor blackSecondaryText];
self.nodeSize.text =
[NSString stringWithFormat:@"%@ %.0f%%", L(@"downloader_downloading"), progress * 100.f];
self.downloadButton.hidden = YES;
self.progressWrapper.hidden = NO;
self.progress.progress = progress;
[self addToSuperview];
}
- (void)showInQueue {
self.nodeSize.textColor = [UIColor blackSecondaryText];
self.nodeSize.text = L(@"downloader_queued");
self.downloadButton.hidden = YES;
self.progressWrapper.hidden = NO;
self.progress.state = MWMCircularProgressStateSpinner;
[self addToSuperview];
}
- (void)processViewportCountryEvent:(CountryId const &)countryId {
m_countryId = countryId;
if (countryId == kInvalidCountryId)
[self removeFromSuperview];
else
[self configDialog];
}
#pragma mark - MWMStorageObserver
- (void)processCountryEvent:(NSString *)countryId {
if (m_countryId != countryId.UTF8String)
return;
if (self.superview)
[self configDialog];
else
[self removeFromSuperview];
}
- (void)processCountry:(NSString *)countryId
downloadedBytes:(uint64_t)downloadedBytes
totalBytes:(uint64_t)totalBytes {
if (self.superview && m_countryId == countryId.UTF8String)
[self showDownloading:(CGFloat)downloadedBytes / totalBytes];
}
#pragma mark - MWMCircularProgressDelegate
- (void)progressButtonPressed:(nonnull MWMCircularProgress *)progress {
if (progress.state == MWMCircularProgressStateFailed) {
[self showInQueue];
[[MWMStorage sharedStorage] retryDownloadNode:@(m_countryId.c_str())];
} else {
if (m_autoDownloadCountryId == m_countryId)
self.isAutoDownloadCancelled = YES;
[[MWMStorage sharedStorage] cancelDownloadNode:@(m_countryId.c_str())];
}
}
#pragma mark - Actions
- (IBAction)downloadAction {
NSUserDefaults *userDefaults = NSUserDefaults.standardUserDefaults;
NSInteger offlineMapsExplained = [userDefaults integerForKey:kOfflineMapsExplained] + 1;
[userDefaults setInteger:offlineMapsExplained forKey:kOfflineMapsExplained];
[[MWMStorage sharedStorage] downloadNode:@(m_countryId.c_str())
onSuccess:^{ [self showInQueue]; }];
}
#pragma mark - Properties
- (MWMCircularProgress *)progress {
if (!_progress) {
_progress = [MWMCircularProgress downloaderProgressForParentView:self.progressWrapper];
_progress.delegate = self;
}
return _progress;
}
- (NSMutableArray<NSDate *> *)skipDownloadTimes {
if (!_skipDownloadTimes)
_skipDownloadTimes = [@[] mutableCopy];
return _skipDownloadTimes;
}
@end

View file

@ -0,0 +1,175 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="24127" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="24063"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="I6H-BQ-1TG" customClass="MWMMapDownloadDialog">
<rect key="frame" x="0.0" y="0.0" width="200" height="361"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="RvB-y5-5iX">
<rect key="frame" x="0.0" y="0.0" width="200" height="81.5"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="750" verticalCompressionResistancePriority="753" text="Title" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3ki-f1-eeZ">
<rect key="frame" x="16" y="19" width="168" height="20.5"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="750" verticalCompressionResistancePriority="753" text="Text" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ls5-nm-zjo">
<rect key="frame" x="16" y="43.5" width="168" height="16"/>
<fontDescription key="fontDescription" type="system" pointSize="13"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="LCO-7E-srL" userLabel="Divider">
<rect key="frame" x="0.0" y="80.5" width="200" height="1"/>
<color key="backgroundColor" systemColor="opaqueSeparatorColor"/>
<constraints>
<constraint firstAttribute="height" constant="1" id="wqE-10-TUx"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="3ki-f1-eeZ" secondAttribute="trailing" constant="16" id="6nX-Re-jqZ"/>
<constraint firstItem="LCO-7E-srL" firstAttribute="leading" secondItem="RvB-y5-5iX" secondAttribute="leading" id="HBn-fD-OpE"/>
<constraint firstItem="ls5-nm-zjo" firstAttribute="top" secondItem="3ki-f1-eeZ" secondAttribute="bottom" constant="4" id="KzG-oR-PAR"/>
<constraint firstAttribute="trailing" secondItem="ls5-nm-zjo" secondAttribute="trailing" constant="16" id="MJj-zB-w2b"/>
<constraint firstAttribute="bottom" secondItem="LCO-7E-srL" secondAttribute="bottom" id="Tfu-HV-8xZ"/>
<constraint firstAttribute="trailing" secondItem="LCO-7E-srL" secondAttribute="trailing" id="VH1-4J-Lb4"/>
<constraint firstAttribute="height" id="drU-cO-jmt"/>
<constraint firstItem="LCO-7E-srL" firstAttribute="top" secondItem="ls5-nm-zjo" secondAttribute="bottom" constant="21" id="huT-bw-r1m"/>
<constraint firstItem="3ki-f1-eeZ" firstAttribute="top" secondItem="RvB-y5-5iX" secondAttribute="top" constant="19" id="l1b-RC-c30"/>
<constraint firstItem="ls5-nm-zjo" firstAttribute="leading" secondItem="RvB-y5-5iX" secondAttribute="leading" constant="16" id="uwk-LS-Oyj"/>
<constraint firstItem="3ki-f1-eeZ" firstAttribute="leading" secondItem="RvB-y5-5iX" secondAttribute="leading" constant="16" id="v49-pB-FIP"/>
</constraints>
<variation key="default">
<mask key="constraints">
<exclude reference="drU-cO-jmt"/>
</mask>
</variation>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="751" text="Россия" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="GQf-xO-gh1">
<rect key="frame" x="14" y="100.5" width="172" height="109.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="0.54000000000000004" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="medium16:blackSecondaryText"/>
</userDefinedRuntimeAttributes>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="260" verticalCompressionResistancePriority="751" text="Доминиканская республика" textAlignment="center" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vMH-GY-Vm2">
<rect key="frame" x="14" y="212" width="172" height="48"/>
<fontDescription key="fontDescription" type="system" pointSize="20"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="medium20:blackPrimaryText"/>
</userDefinedRuntimeAttributes>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="260" text="16 МБ" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vU4-G9-kcd">
<rect key="frame" x="14" y="268" width="172" height="17"/>
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="14"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="0.54000000000000004" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="iyV-Rb-bea">
<rect key="frame" x="20" y="297" width="160" height="44"/>
<color key="backgroundColor" red="0.1176470588" green="0.58823529409999997" blue="0.94117647059999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="44" id="i64-mO-Txs"/>
<constraint firstAttribute="width" constant="160" id="tIM-Zn-N9S"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<state key="normal" title="Скачать карту">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="localizedText" value="country_status_download_without_size"/>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="FlatNormalButton"/>
</userDefinedRuntimeAttributes>
<connections>
<action selector="downloadAction" destination="I6H-BQ-1TG" eventType="touchUpInside" id="gK1-oH-Xye"/>
</connections>
</button>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="usc-Cm-gmg" userLabel="ProgressViewWrapper">
<rect key="frame" x="84" y="303" width="32" height="32"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" constant="32" id="LiD-Zk-ocz"/>
<constraint firstAttribute="height" constant="32" id="jrx-nU-VSG"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="GQf-xO-gh1" firstAttribute="leading" secondItem="iyV-Rb-bea" secondAttribute="leading" constant="-6" id="4vl-GS-oaZ"/>
<constraint firstItem="usc-Cm-gmg" firstAttribute="centerX" secondItem="iyV-Rb-bea" secondAttribute="centerX" id="7DI-KV-B8S"/>
<constraint firstItem="vU4-G9-kcd" firstAttribute="leading" secondItem="iyV-Rb-bea" secondAttribute="leading" constant="-6" id="8nk-Fu-pae"/>
<constraint firstAttribute="trailing" secondItem="RvB-y5-5iX" secondAttribute="trailing" id="9rv-Kg-SEX"/>
<constraint firstAttribute="trailing" secondItem="iyV-Rb-bea" secondAttribute="trailing" constant="20" id="C8t-0i-lpV"/>
<constraint firstItem="vMH-GY-Vm2" firstAttribute="leading" secondItem="iyV-Rb-bea" secondAttribute="leading" constant="-6" id="E4i-Dd-AQA"/>
<constraint firstAttribute="bottom" secondItem="iyV-Rb-bea" secondAttribute="bottom" priority="500" constant="20" id="HSh-rd-XIZ"/>
<constraint firstItem="vMH-GY-Vm2" firstAttribute="top" secondItem="GQf-xO-gh1" secondAttribute="bottom" priority="500" constant="2" id="Kje-IL-qJy"/>
<constraint firstItem="vU4-G9-kcd" firstAttribute="top" secondItem="vMH-GY-Vm2" secondAttribute="bottom" constant="8" id="S4i-o8-W4I"/>
<constraint firstItem="iyV-Rb-bea" firstAttribute="top" secondItem="vU4-G9-kcd" secondAttribute="bottom" constant="12" id="VN6-NK-qgb"/>
<constraint firstItem="vMH-GY-Vm2" firstAttribute="top" secondItem="RvB-y5-5iX" secondAttribute="bottom" priority="800" constant="19" id="XpP-Zw-jAa"/>
<constraint firstItem="iyV-Rb-bea" firstAttribute="leading" secondItem="I6H-BQ-1TG" secondAttribute="leading" constant="20" id="atf-Me-rzF"/>
<constraint firstItem="RvB-y5-5iX" firstAttribute="leading" secondItem="I6H-BQ-1TG" secondAttribute="leading" id="c55-Kg-7X1"/>
<constraint firstItem="GQf-xO-gh1" firstAttribute="top" secondItem="RvB-y5-5iX" secondAttribute="bottom" constant="19" id="dJk-R4-Uwg"/>
<constraint firstItem="RvB-y5-5iX" firstAttribute="top" secondItem="I6H-BQ-1TG" secondAttribute="top" id="duL-BL-ibL"/>
<constraint firstAttribute="trailing" secondItem="iyV-Rb-bea" secondAttribute="trailing" priority="500" constant="20" id="hgA-l8-VzT"/>
<constraint firstItem="vU4-G9-kcd" firstAttribute="trailing" secondItem="iyV-Rb-bea" secondAttribute="trailing" constant="6" id="jZP-5b-Nbs"/>
<constraint firstItem="usc-Cm-gmg" firstAttribute="centerY" secondItem="iyV-Rb-bea" secondAttribute="centerY" id="pxa-11-yUj"/>
<constraint firstItem="GQf-xO-gh1" firstAttribute="trailing" secondItem="iyV-Rb-bea" secondAttribute="trailing" constant="6" id="sMn-Pe-OLX"/>
<constraint firstItem="vMH-GY-Vm2" firstAttribute="trailing" secondItem="iyV-Rb-bea" secondAttribute="trailing" constant="6" id="wqD-4Q-AwL"/>
</constraints>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="styleName" value="DialogView"/>
</userDefinedRuntimeAttributes>
<variation key="default">
<mask key="constraints">
<exclude reference="XpP-Zw-jAa"/>
<exclude reference="hgA-l8-VzT"/>
<exclude reference="C8t-0i-lpV"/>
</mask>
</variation>
<variation key="heightClass=compact">
<mask key="constraints">
<include reference="hgA-l8-VzT"/>
</mask>
</variation>
<variation key="heightClass=regular">
<mask key="constraints">
<include reference="C8t-0i-lpV"/>
<include reference="HSh-rd-XIZ"/>
</mask>
</variation>
<connections>
<outlet property="downloadButton" destination="iyV-Rb-bea" id="6fy-CY-zos"/>
<outlet property="explanationHeight" destination="drU-cO-jmt" id="AAd-rw-kWs"/>
<outlet property="explanationText" destination="ls5-nm-zjo" id="XFr-4s-UEW"/>
<outlet property="explanationTitle" destination="3ki-f1-eeZ" id="Y4O-1s-mCP"/>
<outlet property="node" destination="vMH-GY-Vm2" id="LJq-UU-a3U"/>
<outlet property="nodeSize" destination="vU4-G9-kcd" id="DMJ-NW-mxf"/>
<outlet property="nodeTop" destination="XpP-Zw-jAa" id="zCV-UY-ru9"/>
<outlet property="parentNode" destination="GQf-xO-gh1" id="gWT-r0-LTv"/>
<outlet property="progressWrapper" destination="usc-Cm-gmg" id="NTd-bU-rDE"/>
</connections>
<point key="canvasLocation" x="417.39130434782612" y="539.39732142857144"/>
</view>
</objects>
<resources>
<systemColor name="opaqueSeparatorColor">
<color red="0.77647058820000003" green="0.77647058820000003" blue="0.7843137255" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
</resources>
</document>

View file

@ -0,0 +1,14 @@
#include <CoreApi/Framework.h>
@interface MWMMapWidgets : NSObject
+ (MWMMapWidgets *)widgetsManager;
- (void)setupWidgets:(Framework::DrapeCreationParams &)p;
- (void)resize:(CGSize)size;
- (void)updateAvailableArea:(CGRect)frame;
- (void)updateLayout:(CGRect)frame;
- (void)updateLayoutForAvailableArea;
@end

View file

@ -0,0 +1,91 @@
#import "MWMMapWidgets.h"
#import "EAGLView.h"
#import "MapViewController.h"
#import "SwiftBridge.h"
@interface MWMMapWidgets ()
@property(nonatomic) float visualScale;
@property(nonatomic) CGRect availableArea;
@end
@implementation MWMMapWidgets
{
std::unique_ptr<gui::Skin> m_skin;
}
+ (MWMMapWidgets *)widgetsManager
{
return [MapViewController sharedController].mapView.widgetsManager;
}
- (void)setupWidgets:(Framework::DrapeCreationParams &)p
{
self.visualScale = p.m_visualScale;
m_skin.reset(new gui::Skin(gui::ResolveGuiSkinFile("default"), p.m_visualScale));
m_skin->Resize(p.m_surfaceWidth, p.m_surfaceHeight);
m_skin->ForEach(
[&p](gui::EWidget widget, gui::Position const & pos) { p.m_widgetsInitInfo[widget] = pos; });
p.m_widgetsInitInfo[gui::WIDGET_SCALE_FPS_LABEL] = gui::Position(m2::PointF(self.visualScale * 10, self.visualScale * 45), dp::LeftTop);
}
- (void)resize:(CGSize)size
{
if (m_skin != nullptr)
m_skin->Resize(size.width, size.height);
dispatch_async(dispatch_get_main_queue(), ^{
if ([MWMCarPlayService shared].isCarplayActivated) {
CGRect bounds = [MapViewController sharedController].mapView.bounds;
[self updateLayout: bounds];
return;
}
[self updateLayoutForAvailableArea];
});
}
- (void)updateAvailableArea:(CGRect)frame
{
if (CGRectEqualToRect(self.availableArea, frame))
return;
self.availableArea = frame;
if ([MWMCarPlayService shared].isCarplayActivated) {
return;
}
[self updateLayout:frame];
}
- (void)updateLayoutForAvailableArea
{
[self updateLayout:self.availableArea];
}
- (void)updateLayout:(CGRect)frame
{
if (m_skin == nullptr)
return;
gui::TWidgetsLayoutInfo layout;
auto const vs = self.visualScale;
auto const viewHeight = [MapViewController sharedController].mapView.height;
auto const viewWidth = [MapViewController sharedController].mapView.width;
auto const rulerOffset =
m2::PointF(frame.origin.x * vs, (frame.origin.y + frame.size.height - viewHeight) * vs);
auto const kCompassAdditionalYOffset = [TrackRecordingManager.shared isActive] ? 50 : 0;
auto const compassOffset =
m2::PointF((frame.origin.x + frame.size.width - viewWidth) * vs, (frame.origin.y + kCompassAdditionalYOffset) * vs);
m_skin->ForEach([&](gui::EWidget w, gui::Position const & pos) {
m2::PointF pivot = pos.m_pixelPivot;
switch (w)
{
case gui::WIDGET_RULER:
case gui::WIDGET_COPYRIGHT: pivot += rulerOffset; break;
case gui::WIDGET_COMPASS: pivot += compassOffset; break;
case gui::WIDGET_SCALE_FPS_LABEL:
case gui::WIDGET_CHOOSE_POSITION_MARK: break;
}
layout[w] = pivot;
});
GetFramework().SetWidgetLayout(std::move(layout));
}
@end

View file

@ -0,0 +1,7 @@
@interface MWMMapWidgetsHelper : NSObject
+ (void)updateAvailableArea:(CGRect)frame;
+ (void)updateLayout:(CGRect)frame;
+ (void)updateLayoutForAvailableArea;
@end

View file

@ -0,0 +1,21 @@
#import "MWMMapWidgetsHelper.h"
#import "MWMMapWidgets.h"
@implementation MWMMapWidgetsHelper
+ (void)updateAvailableArea:(CGRect)frame
{
[[MWMMapWidgets widgetsManager] updateAvailableArea:frame];
}
+ (void)updateLayout:(CGRect)frame
{
[[MWMMapWidgets widgetsManager] updateLayout:frame];
}
+ (void)updateLayoutForAvailableArea
{
[[MWMMapWidgets widgetsManager] updateLayoutForAvailableArea];
}
@end