Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions OsmAnd.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,7 @@
32BFF098299137D60023D067 /* OAUploadGPXFilesTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 32BFF097299137D60023D067 /* OAUploadGPXFilesTask.m */; };
32C1C4ED2DD4C4F200A053D4 /* OAClickableWayHelper.mm in Sources */ = {isa = PBXBuildFile; fileRef = 32C1C4E42DD4C4F200A053D4 /* OAClickableWayHelper.mm */; };
32C1C4EF2DD4C4F200A053D4 /* OAMapSelectionHelper.mm in Sources */ = {isa = PBXBuildFile; fileRef = 32C1C4E72DD4C4F200A053D4 /* OAMapSelectionHelper.mm */; };
32C1EF132FB608B6008BBA40 /* OACollapsablePoiView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32F01E3C2FB6049D00736C97 /* OACollapsablePoiView.swift */; };
32C21CB127DFC18E00DE4266 /* OAAlertBottomSheetViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 32C21CB027DFC18E00DE4266 /* OAAlertBottomSheetViewController.mm */; };
32C21CB327E08EFD00DE4266 /* OAAlertBottomSheetViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 32C21CB227E08E4D00DE4266 /* OAAlertBottomSheetViewController.xib */; };
32C21CB727E0A66B00DE4266 /* OARecordSettingsBottomSheetViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 32C21CB627E0A66A00DE4266 /* OARecordSettingsBottomSheetViewController.m */; };
Expand All @@ -604,6 +605,7 @@
32D7D6D22CD9564D00EB752F /* ObfConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32D7D6D12CD9564D00EB752F /* ObfConstants.swift */; };
32DA38A82BA9C4D500A3AC3C /* CLLocation+Extension.m in Sources */ = {isa = PBXBuildFile; fileRef = 32DA38A72BA9C4D500A3AC3C /* CLLocation+Extension.m */; };
32DE2BE42B6D13730025F2B9 /* OATwoButtonsTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32DE2BE32B6D13730025F2B9 /* OATwoButtonsTableViewCell.swift */; };
32E080C22FAB2DCA0095A278 /* SearchByRouteIdTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32E080C12FAB2DC50095A278 /* SearchByRouteIdTask.swift */; };
32E2A6E027206ACF00F018B5 /* OAQuickSearchCoordinateFormatsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 32E2A6DF2720699200F018B5 /* OAQuickSearchCoordinateFormatsViewController.xib */; };
32E2D30229B9DB5700A17D0B /* ic_custom_coordinates_location@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 32E2D2FE29B9DB5500A17D0B /* ic_custom_coordinates_location@3x.png */; };
32E2D30329B9DB5700A17D0B /* ic_custom_coordinates_map_center@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 32E2D2FF29B9DB5600A17D0B /* ic_custom_coordinates_map_center@2x.png */; };
Expand Down Expand Up @@ -4314,6 +4316,7 @@
32DA38A62BA9C4C600A3AC3C /* CLLocation+Extension.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CLLocation+Extension.h"; sourceTree = "<group>"; };
32DA38A72BA9C4D500A3AC3C /* CLLocation+Extension.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "CLLocation+Extension.m"; sourceTree = "<group>"; };
32DE2BE32B6D13730025F2B9 /* OATwoButtonsTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OATwoButtonsTableViewCell.swift; sourceTree = "<group>"; };
32E080C12FAB2DC50095A278 /* SearchByRouteIdTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchByRouteIdTask.swift; sourceTree = "<group>"; };
32E2A6DF2720699200F018B5 /* OAQuickSearchCoordinateFormatsViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = OAQuickSearchCoordinateFormatsViewController.xib; sourceTree = "<group>"; };
32E2D2FE29B9DB5500A17D0B /* ic_custom_coordinates_location@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "ic_custom_coordinates_location@3x.png"; path = "Resources/Icons/ic_custom_coordinates_location@3x.png"; sourceTree = "<group>"; };
32E2D2FF29B9DB5600A17D0B /* ic_custom_coordinates_map_center@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "ic_custom_coordinates_map_center@2x.png"; path = "Resources/Icons/ic_custom_coordinates_map_center@2x.png"; sourceTree = "<group>"; };
Expand All @@ -4332,6 +4335,7 @@
32ECBEAD2657AD7F005D33BD /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = "<group>"; };
32EEB6302E28651B002216B0 /* OATravelSelectionLayer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OATravelSelectionLayer.h; sourceTree = "<group>"; };
32EEB6312E286521002216B0 /* OATravelSelectionLayer.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = OATravelSelectionLayer.mm; sourceTree = "<group>"; };
32F01E3C2FB6049D00736C97 /* OACollapsablePoiView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OACollapsablePoiView.swift; sourceTree = "<group>"; };
32F3A6A92D5E1958008AE4CA /* PoiIconCollectionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PoiIconCollectionHandler.swift; sourceTree = "<group>"; };
32F3A6AB2D5E1A41008AE4CA /* IconsAppearanceCategory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconsAppearanceCategory.swift; sourceTree = "<group>"; };
32F6B3902E47A842007F3902 /* OAAmenitySearcher+cpp.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "OAAmenitySearcher+cpp.h"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -12984,6 +12988,7 @@
DA5A7E1C26C563A300F274C7 /* OACollapsableTransportStopRoutesView.h */,
DA5A7DF926C563A300F274C7 /* OACollapsableTransportStopRoutesView.mm */,
320DF4112F43799C00F80CE3 /* OACollapsableTravelGuidesView.swift */,
32F01E3C2FB6049D00736C97 /* OACollapsablePoiView.swift */,
DA5A7EB726C563A400F274C7 /* OACollapsableWaypointsView.h */,
DA5A7E2426C563A400F274C7 /* OACollapsableWaypointsView.mm */,
DA5A7EB426C563A400F274C7 /* OAEditPointViewController.h */,
Expand Down Expand Up @@ -14092,6 +14097,7 @@
32EB34472E462D58004F7C75 /* OAAmenitySearcher.h */,
32EB34482E462D68004F7C75 /* OAAmenitySearcher.mm */,
32F6B3902E47A842007F3902 /* OAAmenitySearcher+cpp.h */,
32E080C12FAB2DC50095A278 /* SearchByRouteIdTask.swift */,
DA5A80E026C563A600F274C7 /* OAAppSettings.h */,
DA5A80B226C563A600F274C7 /* OAAppSettings.m */,
2C8E8A542D5104E600746A69 /* OAArabicNormalizer.h */,
Expand Down Expand Up @@ -18302,6 +18308,7 @@
DA8DC3982A0CF47D006C116B /* MapMarkerSideWidget.swift in Sources */,
FA6408752D3E8047005FD12D /* WikiImageCard.swift in Sources */,
DA8DC39D2A0E2247006C116B /* OAMapViewHelper.mm in Sources */,
32C1EF132FB608B6008BBA40 /* OACollapsablePoiView.swift in Sources */,
DA5A81EB26C563A700F274C7 /* OAHistoryDB.mm in Sources */,
32D253B227F336C200324717 /* OACloudBackupViewController.mm in Sources */,
DA5A846526C563A900F274C7 /* OADownloadedRegionsLayer.mm in Sources */,
Expand Down Expand Up @@ -18840,6 +18847,7 @@
DA5A83EA26C563A800F274C7 /* OACollapsableWaypointsView.mm in Sources */,
27D33B572E7459FA00AD4F70 /* MoveToMyLocationAction.swift in Sources */,
DA5A837D26C563A800F274C7 /* GpxUIHelper.swift in Sources */,
32E080C22FAB2DCA0095A278 /* SearchByRouteIdTask.swift in Sources */,
DA21F1BB29BA101E004985BA /* OANetworkRouteDrawable.mm in Sources */,
DA4ABF252876DA6800B996EF /* OAFileSettingsItem.mm in Sources */,
FA7341142BBC1C2000CBF7EC /* OARequiredMapsResourceViewController.mm in Sources */,
Expand Down
5 changes: 5 additions & 0 deletions Resources/Localizations/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -1526,6 +1526,7 @@
"ltr_or_rtl_combine_via_per" = "%@ per %@";
"ltr_or_rtl_combine_with_brackets" = "%@ (%@)";

"ltr_or_rtl_triple_combine_via_space" = "%@ %@ %@";
"shared_string_beta" = "Beta";
"of" = "%d of %d";
"downloaded_bytes" = "%@ of %@";
Expand Down Expand Up @@ -4370,6 +4371,10 @@
"rendering_attr_showClimbingRoutes_description" = "Show climbing routes";
"rendering_attr_showClimbingRoutes_name" = "Climbing routes";

"route_members" = "Members";
"route_part_of" = "Part of";
"multipoligon_related" = "Related";

"shared_string_view_all" = "View all";
"no_internet_descr" = "Please check your connection and try again.";
"no_photos_available" = "No photos available";
Expand Down
1 change: 1 addition & 0 deletions Sources/Common/OACollatorStringMatcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ typedef enum
CHECK_CONTAINS,
// simple collator equals
CHECK_EQUALS,
MULTISEARCH,

} StringMatcherMode;

Expand Down
2 changes: 2 additions & 0 deletions Sources/Controllers/Map/Layers/OAPOILayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@

@interface OAPOILayer : OASymbolMapLayer<OAContextMenuProvider>

+ (OATargetPoint *)getTargetPoint:(id)obj;

@end
7 changes: 6 additions & 1 deletion Sources/Controllers/Map/Layers/OAPOILayer.mm
Original file line number Diff line number Diff line change
Expand Up @@ -1519,6 +1519,11 @@ - (NSString *) getAmenityName:(OAPOI *)amenity
#pragma mark - OAContextMenuProvider

- (OATargetPoint *)getTargetPoint:(id)obj touchLocation:(CLLocation *)touchLocation
{
return [self.class getTargetPoint:obj];
}

+ (OATargetPoint *)getTargetPoint:(id)obj
{
if ([obj isKindOfClass:OAPOI.class])
return [self getTargetPoint:obj renderedObject:nil placeDetailsObject:nil];
Expand All @@ -1529,7 +1534,7 @@ - (OATargetPoint *)getTargetPoint:(id)obj touchLocation:(CLLocation *)touchLocat
return nil;
}

- (OATargetPoint *) getTargetPoint:(OAPOI *)poi renderedObject:(OARenderedObject *)renderedObject placeDetailsObject:(BaseDetailsObject *)placeDetailsObject
+ (OATargetPoint *) getTargetPoint:(OAPOI *)poi renderedObject:(OARenderedObject *)renderedObject placeDetailsObject:(BaseDetailsObject *)placeDetailsObject
{
if (placeDetailsObject)
poi = placeDetailsObject.syntheticAmenity;
Expand Down
18 changes: 14 additions & 4 deletions Sources/Controllers/Panels/OAMapPanelViewController.mm
Original file line number Diff line number Diff line change
Expand Up @@ -1430,7 +1430,7 @@ - (void) showContextMenuWithPoints:(NSArray<OATargetPoint *> *)targetPoints sele
if (_activeTargetType == OATargetRouteIntermediateSelection && targetPoints.count > 1)
{
[validPoints addObjectsFromArray:targetPoints];
if (selectedObjects)
if (!NSArrayIsEmpty(selectedObjects))
[validSelectedObjects addObjectsFromArray:selectedObjects];
}
else
Expand All @@ -1441,7 +1441,7 @@ - (void) showContextMenuWithPoints:(NSArray<OATargetPoint *> *)targetPoints sele
if ([self processTargetPoint:targetPoint])
{
[validPoints addObject:targetPoint];
if (selectedObjects)
if (!NSArrayIsEmpty(selectedObjects))
[validSelectedObjects addObject:selectedObjects[i]];
}
}
Expand Down Expand Up @@ -1559,16 +1559,26 @@ - (void)showContextMenu:(OATargetPoint *)targetPoint saveState:(BOOL)saveState p

- (void)setSelectedObject:(OATargetPoint *)targetPoint
{

OAMapObject *obj = nil;
if ([targetPoint.targetObj isKindOfClass:OAMapObject.class])
{
obj = targetPoint.targetObj;

}
else if([targetPoint.targetObj isKindOfClass:BaseDetailsObject.class])
{
BaseDetailsObject *baseDetails = (BaseDetailsObject *) targetPoint.targetObj;
obj = (OAMapObject *) [baseDetails syntheticAmenity];
}
if (obj != nil)
{
QVector<OsmAnd::PointI> points;
OAMapObject *obj = targetPoint.targetObj;
if (obj.x && obj.x.count > 0)
{
for (int i = 0; i < obj.x.count; i++)
points.push_back(OsmAnd::PointI(obj.x[i].intValue, obj.y[i].intValue));
}

[_mapViewController.mapLayers.contextMenuLayer highlightPolygon:points];
}
}
Expand Down
117 changes: 117 additions & 0 deletions Sources/Controllers/TargetMenu/OACollapsablePoiView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
//
// OACollapsablePoiView.swift
// OsmAnd
//
// Created by Max Kojin on 14/05/26.
// Copyright © 2026 OsmAnd. All rights reserved.
//

@objcMembers
final class OACollapsablePoiView: OACollapsableView {

private let kButtonHeight: CGFloat = 36.0

private var titles = [String]()
private var amenities = [OAPOI]()
private var buttons = [OAButton]()
private var selectedButtonIndex = 0

func setData(titles: [String], amenities: [OAPOI]) {
self.amenities = amenities
self.titles = titles
buildViews()
}

func updateLayout(width: CGFloat) {
var y: CGFloat = 0.0
var viewHeight: CGFloat = 0.0
var i = 0
for button in buttons {
if i > 0 {
y += kButtonHeight + 10.0
viewHeight += 10.0
}

let height: CGFloat = kButtonHeight
button.frame = CGRect(x: kMarginLeft, y: y, width: width - kMarginLeft - kMarginRight, height: height)
viewHeight += button.frame.size.height
i += 1
}

viewHeight += 8.0
frame = CGRect(x: frame.origin.x, y: frame.origin.y, width: width, height: viewHeight)
}

private func buildViews() {
for i in 0..<amenities.count {
let btn = createButton(title: titles[i])
btn.tag = i
self.addSubview(btn)
buttons.append(btn)
}
}

private func createButton(title: String) -> OAButton {
let btn = OAButton(type: .system)
btn.setTitle(title, for: .normal)
btn.contentHorizontalAlignment = .left
btn.contentEdgeInsets = UIEdgeInsets(top: 0, left: 12.0, bottom: 0, right: 12.0)
btn.titleLabel?.lineBreakMode = .byTruncatingTail
btn.titleLabel?.font = .preferredFont(forTextStyle: .footnote)
btn.layer.cornerRadius = 4.0
btn.layer.masksToBounds = true
btn.layer.borderWidth = 0.8
btn.layer.borderColor = UIColor.customSeparator.cgColor
btn.setBackgroundImage(OAUtilities.image(with: .clear), for: .normal)
btn.tintColor = UIColor.iconColorActive
btn.delegate = self
return btn
}

override func copy(_ sender: Any?) {
guard buttons.count > selectedButtonIndex else { return }
let button = buttons[selectedButtonIndex]
let pasteboard = UIPasteboard.general
pasteboard.string = button.titleLabel?.text
}

private func updateButtonBorderColor() {
for button in buttons {
button.layer.borderColor = UIColor.customSeparator.cgColor
}
}

override var canBecomeFirstResponder: Bool {
true
}

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)

if traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) {
updateButtonBorderColor()
}
}

override func adjustHeight(forWidth width: CGFloat) {
updateLayout(width: width)
}
}

extension OACollapsablePoiView: OAButtonDelegate {

func onButtonTapped(_ tag: Int) {
guard amenities.count > tag else { return }
let amenity = amenities[tag]
if let targetPoint = OAPOILayer.getTargetPoint(amenity) {
targetPoint.centerMap = true
OARootViewController.instance().mapPanel.showContextMenu(with: [targetPoint], selectedObjects: [], touchPointLatLon: CLLocation(latitude: targetPoint.location.latitude, longitude: targetPoint.location.longitude))
}
}

func onButtonLongPressed(_ tag: Int) {
selectedButtonIndex = tag
guard buttons.count > selectedButtonIndex else { return }
OAUtilities.showMenu(in: self, from: buttons[selectedButtonIndex])
}
}
Loading