From a57a29059a83c7c2763446e06c1572038f97ecca Mon Sep 17 00:00:00 2001 From: Milen Pivchev Date: Wed, 10 Sep 2025 14:26:20 +0200 Subject: [PATCH 1/7] WIP Signed-off-by: Milen Pivchev --- Nextcloud.xcodeproj/project.pbxproj | 2 + .../Collection Common/Cell/NCGridCell.xib | 13 ++- .../Collection Common/Cell/NCListCell.swift | 100 ++++++++++++++++++ .../Collection Common/Cell/NCListCell.xib | 7 +- ...nViewCommon+CollectionViewDataSource.swift | 16 ++- 5 files changed, 122 insertions(+), 16 deletions(-) diff --git a/Nextcloud.xcodeproj/project.pbxproj b/Nextcloud.xcodeproj/project.pbxproj index 3af64748e2..0a940ee220 100644 --- a/Nextcloud.xcodeproj/project.pbxproj +++ b/Nextcloud.xcodeproj/project.pbxproj @@ -1392,6 +1392,7 @@ F3E173AF2C9AF637006D177A /* ScreenAwakeManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenAwakeManager.swift; sourceTree = ""; }; F3E173BF2C9B1067006D177A /* AwakeMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AwakeMode.swift; sourceTree = ""; }; F3F442ED2DDE292600FD701F /* NCMetadataPermissions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCMetadataPermissions.swift; sourceTree = ""; }; + F3F93F0B2E7077EA0025B43A /* NextcloudKit */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = NextcloudKit; path = ../NextcloudKit; sourceTree = SOURCE_ROOT; }; F700222B1EC479840080073F /* Custom.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Custom.xcassets; sourceTree = ""; }; F700510022DF63AC003A3356 /* NCShare.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = NCShare.storyboard; sourceTree = ""; }; F700510422DF6A89003A3356 /* NCShare.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShare.swift; sourceTree = ""; }; @@ -3328,6 +3329,7 @@ C04E2F202A17BB4D001BAD85 /* NextcloudIntegrationTests.xctest */, C0046CDA2A17B98400D87C9D /* NextcloudUITests.xctest */, F7F1FBA62E27D13700C79E20 /* Frameworks */, + F3F93F0B2E7077EA0025B43A /* NextcloudKit */, ); sourceTree = ""; }; diff --git a/iOSClient/Main/Collection Common/Cell/NCGridCell.xib b/iOSClient/Main/Collection Common/Cell/NCGridCell.xib index 2466c28b44..74b4d877fb 100644 --- a/iOSClient/Main/Collection Common/Cell/NCGridCell.xib +++ b/iOSClient/Main/Collection Common/Cell/NCGridCell.xib @@ -1,9 +1,8 @@ - + - - + @@ -61,7 +60,7 @@ - diff --git a/iOSClient/Main/Collection Common/Cell/NCListCell.swift b/iOSClient/Main/Collection Common/Cell/NCListCell.swift index 7f519d9258..c97ebdae3c 100755 --- a/iOSClient/Main/Collection Common/Cell/NCListCell.swift +++ b/iOSClient/Main/Collection Common/Cell/NCListCell.swift @@ -342,3 +342,103 @@ class NCListLayout: UICollectionViewFlowLayout { return proposedContentOffset } } + +class BidiFilenameLabel: UILabel { + var fullFilename: String = "" { + didSet { needsUpdate = true } + } + + override var numberOfLines: Int { + didSet { needsUpdate = true } + } + + var isFolder: Bool = false { + didSet { needsUpdate = true } + } + + var lastKnownWidth: CGFloat = 0 + + /// Whether the filename should be treated as RTL + @IBInspectable var isRTL: Bool = false + + private var needsUpdate = false + + override init(frame: CGRect) { + super.init(frame: frame) + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + } + + override func layoutSubviews() { + super.layoutSubviews() + preferredMaxLayoutWidth = bounds.width + +// if needsUpdate || text == nil { + updateText() +// } + +// Only update if width changed +// if bounds.width != lastKnownWidth { +// lastKnownWidth = bounds.width +// updateText() +// } + +// updateText() re-truncate using new width + } + + private func updateText() { + guard !fullFilename.isEmpty else { + self.text = "" + return + } + + let availableWidth = bounds.width + guard availableWidth > 0 else { return } + + let isRTL = UIView.userInterfaceLayoutDirection(for: semanticContentAttribute) == .rightToLeft + let sanitizedFilename = fullFilename.sanitizeForBidiCharacters(isFolder: isFolder, isRTL: isRTL) + + let nsFilename = sanitizedFilename as NSString + let ext = nsFilename.pathExtension + var base = nsFilename.deletingPathExtension + + let dotExt = ext.isEmpty ? "" : "." + ext + let truncatedBase = truncateBase(base: base, dotExt: dotExt, maxWidth: intrinsicContentSize.width, font: font ?? UIFont.systemFont(ofSize: 17)) + + self.text = sanitizedFilename.replacingOccurrences(of: base, with: truncatedBase) + + needsUpdate = false + } + + private func truncateBase(base: String, dotExt: String, maxWidth: CGFloat, font: UIFont) -> String { + let extWidth = (dotExt as NSString).size(withAttributes: [.font: font]).width + + if (base as NSString).size(withAttributes: [.font: font]).width + extWidth <= maxWidth { + return base + } + + let characters = Array(base) + var low = 0 + var high = characters.count + var result = "" + + while low <= high { + let mid = (low + high) / 2 + let prefixCount = mid / 2 + let suffixCount = mid - prefixCount + let finalString = String(characters.prefix(prefixCount)) + "…" + String(characters.suffix(suffixCount)) + let finalStringWidth = (finalString as NSString).size(withAttributes: [.font: font]).width + extWidth + + if finalStringWidth <= maxWidth { + result = finalString + low = mid + 1 + } else { + high = mid - 1 + } + } + + return result + } +} diff --git a/iOSClient/Main/Collection Common/Cell/NCListCell.xib b/iOSClient/Main/Collection Common/Cell/NCListCell.xib index 0d02c3cfba..27786b0e3a 100755 --- a/iOSClient/Main/Collection Common/Cell/NCListCell.xib +++ b/iOSClient/Main/Collection Common/Cell/NCListCell.xib @@ -1,9 +1,8 @@ - + - - + @@ -60,7 +59,7 @@ -