summaryrefslogtreecommitdiffstats
path: root/ios
diff options
context:
space:
mode:
authorJon Nermut <jon.nermut@gmail.com>2018-01-20 17:08:43 +1100
committerjan iversen <jani@libreoffice.org>2018-01-20 18:58:28 +0100
commita468fef9ac977e812e83e3cce462b75d8d24c64d (patch)
tree5170a58b7bf05a655a418e63b750c289201d6d6b /ios
parentiOS: Fix debugging in xcode by making include path non-recursive (diff)
downloadcore-a468fef9ac977e812e83e3cce462b75d8d24c64d.tar.gz
core-a468fef9ac977e812e83e3cce462b75d8d24c64d.zip
iOS: keep track of doc part
- implement swipe left/right and tap gestures for presentations - move some classes to their own files Change-Id: I3ddd3e17ec809c87097d5515f08038bbc969764f Reviewed-on: https://gerrit.libreoffice.org/48231 Reviewed-by: jan iversen <jani@libreoffice.org> Tested-by: jan iversen <jani@libreoffice.org>
Diffstat (limited to 'ios')
-rw-r--r--ios/LibreOfficeLight/LibreOfficeLight.xcodeproj/project.pbxproj28
-rwxr-xr-xios/LibreOfficeLight/LibreOfficeLight/DocumentController.swift4
-rw-r--r--ios/LibreOfficeLight/LibreOfficeLight/DocumentTiledView.swift65
-rw-r--r--ios/LibreOfficeLight/LibreOfficeLight/LOKit/DocumentHolder.swift327
-rw-r--r--ios/LibreOfficeLight/LibreOfficeLight/LOKit/LOKitThread.swift350
-rw-r--r--ios/LibreOfficeLight/LibreOfficeLight/LOKit/RenderCache.swift78
6 files changed, 489 insertions, 363 deletions
diff --git a/ios/LibreOfficeLight/LibreOfficeLight.xcodeproj/project.pbxproj b/ios/LibreOfficeLight/LibreOfficeLight.xcodeproj/project.pbxproj
index 48174b80e271..315d4d18151b 100644
--- a/ios/LibreOfficeLight/LibreOfficeLight.xcodeproj/project.pbxproj
+++ b/ios/LibreOfficeLight/LibreOfficeLight.xcodeproj/project.pbxproj
@@ -33,6 +33,8 @@
39B091CE1E5F0BB800682A59 /* unorc in Resources */ = {isa = PBXBuildFile; fileRef = 39B08B9C1E5F0BB600682A59 /* unorc */; };
39E950531FC9842000D82C49 /* source in Resources */ = {isa = PBXBuildFile; fileRef = 39E950521FC9842000D82C49 /* source */; };
39EF4E2F1FA500C9001914AC /* PropertiesController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39EF4E2E1FA500C9001914AC /* PropertiesController.swift */; };
+ FC31D01E2012F65500E7F402 /* DocumentHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC31D01D2012F65500E7F402 /* DocumentHolder.swift */; };
+ FC31D0202012F6D300E7F402 /* RenderCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC31D01F2012F6D300E7F402 /* RenderCache.swift */; };
FCAB1CB82009DB6900F1CC34 /* DocumentOverlaysView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCAB1CB72009DB6900F1CC34 /* DocumentOverlaysView.swift */; };
FCC2E3FA2004A01500CEB504 /* Document.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCC2E3F62004A01400CEB504 /* Document.swift */; };
FCC2E3FC2004A01500CEB504 /* LibreOfficeKitWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCC2E3F82004A01400CEB504 /* LibreOfficeKitWrapper.swift */; };
@@ -77,6 +79,14 @@
39E950521FC9842000D82C49 /* source */ = {isa = PBXFileReference; lastKnownFileType = folder; name = source; path = ../source; sourceTree = "<group>"; };
39EE81531FA644E800B73AB8 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
39EF4E2E1FA500C9001914AC /* PropertiesController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PropertiesController.swift; sourceTree = "<group>"; };
+ FC31D00E2012EE4A00E7F402 /* LibreOfficeKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LibreOfficeKit.h; sourceTree = "<group>"; };
+ FC31D00F2012EE4A00E7F402 /* LibreOfficeKit.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LibreOfficeKit.hxx; sourceTree = "<group>"; };
+ FC31D0102012EE4A00E7F402 /* LibreOfficeKitEnums.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LibreOfficeKitEnums.h; sourceTree = "<group>"; };
+ FC31D0112012EE4A00E7F402 /* LibreOfficeKitGtk.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LibreOfficeKitGtk.h; sourceTree = "<group>"; };
+ FC31D0122012EE4A00E7F402 /* LibreOfficeKitInit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LibreOfficeKitInit.h; sourceTree = "<group>"; };
+ FC31D0132012EE4A00E7F402 /* LibreOfficeKitTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LibreOfficeKitTypes.h; sourceTree = "<group>"; };
+ FC31D01D2012F65500E7F402 /* DocumentHolder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentHolder.swift; sourceTree = "<group>"; };
+ FC31D01F2012F6D300E7F402 /* RenderCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RenderCache.swift; sourceTree = "<group>"; };
FCAB1CB72009DB6900F1CC34 /* DocumentOverlaysView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentOverlaysView.swift; sourceTree = "<group>"; };
FCC2E3F62004A01400CEB504 /* Document.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Document.swift; sourceTree = "<group>"; };
FCC2E3F82004A01400CEB504 /* LibreOfficeKitWrapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LibreOfficeKitWrapper.swift; sourceTree = "<group>"; };
@@ -194,13 +204,29 @@
name = Resources;
sourceTree = SOURCE_ROOT;
};
+ FC31D00D2012EE4A00E7F402 /* LibreOfficeKit */ = {
+ isa = PBXGroup;
+ children = (
+ FC31D00E2012EE4A00E7F402 /* LibreOfficeKit.h */,
+ FC31D00F2012EE4A00E7F402 /* LibreOfficeKit.hxx */,
+ FC31D0102012EE4A00E7F402 /* LibreOfficeKitEnums.h */,
+ FC31D0112012EE4A00E7F402 /* LibreOfficeKitGtk.h */,
+ FC31D0122012EE4A00E7F402 /* LibreOfficeKitInit.h */,
+ FC31D0132012EE4A00E7F402 /* LibreOfficeKitTypes.h */,
+ );
+ name = LibreOfficeKit;
+ path = ../../include/LibreOfficeKit;
+ sourceTree = "<group>";
+ };
FCC2E3F52004A01400CEB504 /* LOKit */ = {
isa = PBXGroup;
children = (
FCC2E4042004B74000CEB504 /* AsyncUtil.swift */,
FCC2E3F62004A01400CEB504 /* Document.swift */,
+ FC31D01D2012F65500E7F402 /* DocumentHolder.swift */,
FCC2E3F82004A01400CEB504 /* LibreOfficeKitWrapper.swift */,
FCC2E3F92004A01400CEB504 /* LOKitThread.swift */,
+ FC31D01F2012F6D300E7F402 /* RenderCache.swift */,
FCC2E4022004B72700CEB504 /* Util.swift */,
);
path = LOKit;
@@ -306,7 +332,9 @@
files = (
FCC2E4032004B72700CEB504 /* Util.swift in Sources */,
392ED9B31E5E4B03005C8435 /* ViewPrintManager.swift in Sources */,
+ FC31D01E2012F65500E7F402 /* DocumentHolder.swift in Sources */,
FCAB1CB82009DB6900F1CC34 /* DocumentOverlaysView.swift in Sources */,
+ FC31D0202012F6D300E7F402 /* RenderCache.swift in Sources */,
399648471E5B87DC00E73E83 /* ViewProperties.swift in Sources */,
FCC2E3FC2004A01500CEB504 /* LibreOfficeKitWrapper.swift in Sources */,
39284DB31FA5F207006F43E4 /* DocumentActions.swift in Sources */,
diff --git a/ios/LibreOfficeLight/LibreOfficeLight/DocumentController.swift b/ios/LibreOfficeLight/LibreOfficeLight/DocumentController.swift
index a15889985f31..3decec85410a 100755
--- a/ios/LibreOfficeLight/LibreOfficeLight/DocumentController.swift
+++ b/ios/LibreOfficeLight/LibreOfficeLight/DocumentController.swift
@@ -61,9 +61,11 @@ class DocumentController: UIViewController, MenuDelegate, UIDocumentBrowserViewC
override func viewDidAppear(_ animated: Bool)
{
super.viewDidAppear(animated)
- let res = Bundle.main.url(forResource: "example", withExtension: "odt")
+ //let res = Bundle.main.url(forResource: "example", withExtension: "odt")
//let res = Bundle.main.url(forResource: "example2", withExtension: "docx")
+ let res = Bundle.main.url(forResource: "testdata/1", withExtension: "pptx")
+
if let exampleDoc = res
{
self.doOpen(exampleDoc)
diff --git a/ios/LibreOfficeLight/LibreOfficeLight/DocumentTiledView.swift b/ios/LibreOfficeLight/LibreOfficeLight/DocumentTiledView.swift
index 20ca23178f5c..f0a36878c4b3 100644
--- a/ios/LibreOfficeLight/LibreOfficeLight/DocumentTiledView.swift
+++ b/ios/LibreOfficeLight/LibreOfficeLight/DocumentTiledView.swift
@@ -18,9 +18,7 @@ class DocumentTiledLayer : CATiledLayer
}
}
-
-
-
+/// The main tiled view, which sits inside the scroll view
public class DocumentTiledView: UIView
{
var myScale: CGFloat
@@ -33,16 +31,11 @@ public class DocumentTiledView: UIView
var drawCount = 0
-
-
// Create a new view with the desired frame and scale.
public init(frame: CGRect, document: DocumentHolder, scale: CGFloat)
{
-
-
self.document = document
-
myScale = scale
initialSize = frame.size
var size = document.sync { $0.getDocumentSizeAsCGSize() }
@@ -67,12 +60,28 @@ public class DocumentTiledView: UIView
if let tiledLayer = self.layer as? CATiledLayer
{
+ // these are all tweakable parameters, that give different behaviour to the tiled view
tiledLayer.levelsOfDetail = 4
tiledLayer.levelsOfDetailBias = 7
tiledLayer.tileSize = CGSize(width: 1024.0, height: 1024.0)
//tiledLayer.tileSize = CGSize(width: 512.0, height: 512.0)
}
+ let tap = UITapGestureRecognizer(target: self, action: #selector(onTap) )
+ tap.numberOfTapsRequired = 1
+ self.addGestureRecognizer(tap)
+
+ if (document.isPresentation) // only for preso atm
+ {
+ // add swipe left/right gestures on a preso
+ let swipeRight = UISwipeGestureRecognizer(target: self, action: #selector(onSwipeRight))
+ swipeRight.direction = .right
+ self.addGestureRecognizer(swipeRight)
+
+ let swipeLeft = UISwipeGestureRecognizer(target: self, action: #selector(onSwipeLeft))
+ swipeLeft.direction = .left
+ self.addGestureRecognizer(swipeLeft)
+ }
}
required public init?(coder aDecoder: NSCoder)
@@ -80,6 +89,14 @@ public class DocumentTiledView: UIView
fatalError("init(coder:) has not been implemented")
}
+ func incrementPart(amount: Int)
+ {
+ document?.incrementPart(amount: Int32(amount))
+ document?.async { _ in
+ runOnMain { self.setNeedsDisplay() }
+ }
+ }
+
public func twipsToPixels(rect: CGRect) -> CGRect
{
return rect.applying(CGAffineTransform(scaleX: 1.0/initialScaleFactor, y: 1.0/initialScaleFactor ))
@@ -196,8 +213,32 @@ public class DocumentTiledView: UIView
}
-// override func pressesBegan
-// {
-//
-// }
+}
+
+/// Gesture handlers
+public extension DocumentTiledView
+{
+ @objc func onTap(_ sender: UITapGestureRecognizer)
+ {
+ if (document?.isPresentation ?? false)
+ {
+ incrementPart(amount: 1)
+ }
+ }
+
+ @objc func onSwipeRight(_ sender: UISwipeGestureRecognizer)
+ {
+ if (document?.isPresentation ?? false)
+ {
+ incrementPart(amount: -1)
+ }
+ }
+
+ @objc func onSwipeLeft(_ sender: UISwipeGestureRecognizer)
+ {
+ if (document?.isPresentation ?? false)
+ {
+ incrementPart(amount: 1)
+ }
+ }
}
diff --git a/ios/LibreOfficeLight/LibreOfficeLight/LOKit/DocumentHolder.swift b/ios/LibreOfficeLight/LibreOfficeLight/LOKit/DocumentHolder.swift
new file mode 100644
index 000000000000..a380cc45edd0
--- /dev/null
+++ b/ios/LibreOfficeLight/LibreOfficeLight/LOKit/DocumentHolder.swift
@@ -0,0 +1,327 @@
+//
+// This file is part of the LibreOffice project.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+//
+
+import Foundation
+import UIKit
+import QuartzCore
+
+/**
+ * Holds the document object so to enforce access in a thread safe way.
+ */
+public class DocumentHolder
+{
+ private let doc: Document
+
+ public weak var delegate: DocumentUIDelegate? = nil
+ public weak var searchDelegate: SearchDelegate? = nil
+
+ private let cache = RenderCache()
+
+ public let documentType: LibreOfficeKitDocumentType
+ public let documentSize: CGSize
+ public let views: Int32
+ public let parts: Int32
+
+ public private(set) var currentPart: Int32 = 0
+
+ init(doc: Document)
+ {
+ self.doc = doc
+ self.documentType = doc.getDocumentType()
+ documentSize = doc.getDocumentSizeAsCGSize()
+ views = doc.getViewsCount()
+ parts = doc.getParts()
+
+ doc.registerCallback() {
+ [weak self] typ, payload in
+ self?.onDocumentEvent(type: typ, payload: payload)
+ }
+ }
+
+ public var isPresentation: Bool
+ {
+ return documentType == LOK_DOCTYPE_PRESENTATION
+ }
+ public var isText: Bool
+ {
+ return documentType == LOK_DOCTYPE_TEXT
+ }
+ public var isDrawing: Bool
+ {
+ return documentType == LOK_DOCTYPE_DRAWING
+ }
+ public var isSpeadsheet: Bool
+ {
+ return documentType == LOK_DOCTYPE_SPREADSHEET
+ }
+
+ /// Gives async access to the document
+ public func async(_ closure: @escaping (Document) -> ())
+ {
+ LOKitThread.instance.async
+ {
+ closure(self.doc)
+ }
+ self.invokeHandlers()
+ }
+
+ public func invokeHandlers()
+ {
+ LOKitThread.instance.async
+ {
+ self.doc.invokeHandlers()
+ }
+ }
+
+ /// Gives sync access to the document - blocks until the closure runs.
+ /// Careful of deadlocks.
+ public func sync<R>( _ closure: @escaping (Document) -> R ) -> R
+ {
+ return LOKitThread.instance.sync
+ {
+ self.invokeHandlers()
+ return closure(self.doc)
+ }
+ }
+
+ /// Paints a tile and return synchronously, using a cached version if it can
+ public func paintTileToImage(canvasSize: CGSize,
+ tileRect: CGRect) -> UIImage?
+ {
+ if let cached = cache.get(part: currentPart, canvasSize: canvasSize, tileRect: tileRect)
+ {
+ return cached
+ }
+
+ let img = sync {
+ $0.paintTileToImage(canvasSize: canvasSize, tileRect: tileRect)
+ }
+ if let image = img
+ {
+ cache.add(cachedRender: CachedRender(part: currentPart, canvasSize: canvasSize, tileRect: tileRect, image: image))
+ }
+
+ return img
+ }
+
+ private func onDocumentEvent(type: LibreOfficeKitCallbackType, payload: String?)
+ {
+ print("onDocumentEvent type:\(type) payload:\(payload ?? "")")
+
+ switch type
+ {
+ case LOK_CALLBACK_INVALIDATE_TILES:
+ runOnMain {
+ self.delegate?.invalidateTiles( rects: decodeRects(payload) )
+ }
+ case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
+ runOnMain {
+ self.delegate?.invalidateVisibleCursor( rects: decodeRects(payload) )
+ }
+ case LOK_CALLBACK_TEXT_SELECTION:
+ runOnMain {
+ self.delegate?.textSelection( rects: decodeRects(payload) )
+ }
+ case LOK_CALLBACK_TEXT_SELECTION_START:
+ runOnMain {
+ self.delegate?.textSelectionStart( rects: decodeRects(payload) )
+ }
+ case LOK_CALLBACK_TEXT_SELECTION_END:
+ runOnMain {
+ self.delegate?.textSelectionEnd( rects: decodeRects(payload) )
+ }
+
+ case LOK_CALLBACK_SEARCH_NOT_FOUND:
+ runOnMain {
+ self.searchDelegate?.searchNotFound()
+ }
+ case LOK_CALLBACK_SEARCH_RESULT_SELECTION:
+ runOnMain {
+ self.searchResults(payload: payload)
+ }
+
+ case LOK_CALLBACK_SET_PART:
+ if let p = payload, let newPart = Int32(p)
+ {
+ self.currentPart = newPart
+ // TODO: callback?
+ }
+
+ default:
+ print("onDocumentEvent type:\(type) not handled!")
+ }
+ }
+
+ private func searchResults(payload: String?)
+ {
+ if let d = payload, let data = d.data(using: .utf8)
+ {
+ let decoder = JSONDecoder()
+ do
+ {
+ let searchResults = try decoder.decode(SearchResults.self, from: data )
+ self.searchDelegate?.searchResultSelection(searchResults: searchResults)
+ }
+ catch
+ {
+ print("Error decoding payload: \(error)")
+ }
+ }
+ }
+
+ public func search(searchString: String, forwardDirection: Bool = true, from: CGPoint)
+ {
+ var rootJson = JSONObject()
+ addProperty(&rootJson, "SearchItem.SearchString", "string", searchString);
+ addProperty(&rootJson, "SearchItem.Backward", "boolean", String(!forwardDirection) );
+ addProperty(&rootJson, "SearchItem.SearchStartPointX", "long", String(describing: from.x) );
+ addProperty(&rootJson, "SearchItem.SearchStartPointY", "long", String(describing: from.y) );
+ addProperty(&rootJson, "SearchItem.Command", "long", "0") // String.valueOf(0)); // search all == 1
+
+ if let jsonStr = encode(json: rootJson)
+ {
+ async {
+ $0.postUnoCommand(command: ".uno:ExecuteSearch", arguments: jsonStr, notifyWhenFinished: true)
+ }
+ }
+ }
+
+ public func incrementPart(amount: Int32)
+ {
+ async {
+ document in
+ let currentPart = document.getPart()
+ let numParts = document.getParts()
+ let newPart = currentPart + amount
+ if (newPart < numParts && newPart > 0)
+ {
+ document.setPart(nPart: newPart)
+ }
+ }
+ }
+}
+
+public typealias JSONObject = Dictionary<String, AnyObject>
+public func addProperty( _ json: inout JSONObject, _ parentValue: String, _ type: String, _ value: String)
+{
+ var child = JSONObject();
+ child["type"] = type as AnyObject
+ child["value"] = value as AnyObject
+ json[parentValue] = child as AnyObject
+}
+
+func encode(json: JSONObject) -> String?
+{
+ if let data = try? JSONSerialization.data(withJSONObject: json, options: .prettyPrinted)
+ {
+ return String(data: data, encoding: String.Encoding.utf8)
+ }
+ return nil
+}
+
+/// Decodes a series of rectangles in the form: "x, y, width, height; x, y, width, height"
+public func decodeRects(_ payload: String?) -> [CGRect]?
+{
+ guard var pl = payload else { return nil }
+ pl = pl.trimmingCharacters(in: .whitespacesAndNewlines )
+ if pl == "EMPTY" || pl.count == 0
+ {
+ return nil
+ }
+ var ret = [CGRect]()
+ for rectStr in pl.split(separator: ";")
+ {
+ let coords = rectStr.split(separator: ",").flatMap { Double($0.trimmingCharacters(in: .whitespacesAndNewlines)) }
+ if coords.count == 4
+ {
+ let rect = CGRect(x: coords[0],
+ y: coords[1],
+ width: coords[2],
+ height: coords[3])
+ ret.append( rect )
+ }
+ }
+ return ret
+}
+
+// MARK :- Delegates
+
+public protocol DocumentUIDelegate: class
+{
+ func invalidateTiles(rects: [CGRect]? )
+ func invalidateVisibleCursor(rects: [CGRect]? )
+
+ func textSelection(rects: [CGRect]? )
+ func textSelectionStart(rects: [CGRect]? )
+ func textSelectionEnd(rects: [CGRect]? )
+}
+
+public protocol SearchDelegate: class
+{
+ func searchNotFound()
+ func searchResultSelection(searchResults: SearchResults)
+}
+
+/**
+ Encodes this example json:
+ {
+ "searchString": "Office",
+ "highlightAll": "true",
+ "searchResultSelection": [
+ {
+ "part": "0",
+ "rectangles": "1951, 10743, 627, 239"
+ },
+ {
+ "part": "0",
+ "rectangles": "5343, 9496, 627, 287"
+ },
+ {
+ "part": "0",
+ "rectangles": "1951, 9256, 627, 239"
+ },
+ {
+ "part": "0",
+ "rectangles": "6502, 5946, 626, 287"
+ },
+ {
+ "part": "0",
+ "rectangles": "6686, 5658, 627, 287"
+ },
+ {
+ "part": "0",
+ "rectangles": "4103, 5418, 573, 239"
+ },
+ {
+ "part": "0",
+ "rectangles": "1951, 5418, 627, 239"
+ },
+ {
+ "part": "0",
+ "rectangles": "4934, 1658, 1586, 559"
+ }
+ ]
+ }
+ */
+public struct SearchResults: Codable
+{
+ public var searchString: String?
+ public var highlightAll: String?
+ public var searchResultSelection: Array<PartAndRectangles>?
+}
+
+public struct PartAndRectangles: Codable
+{
+ public var part: String?
+ public var rectangles: String?
+
+ public var rectsAsCGRects: [CGRect]? {
+ return decodeRects(self.rectangles)
+ }
+}
+
diff --git a/ios/LibreOfficeLight/LibreOfficeLight/LOKit/LOKitThread.swift b/ios/LibreOfficeLight/LibreOfficeLight/LOKit/LOKitThread.swift
index e8f60e0f2119..8e3607612a6b 100644
--- a/ios/LibreOfficeLight/LibreOfficeLight/LOKit/LOKitThread.swift
+++ b/ios/LibreOfficeLight/LibreOfficeLight/LOKit/LOKitThread.swift
@@ -119,280 +119,6 @@ public class LOKitThread
}
}
-
-open class CachedRender
-{
- open let canvasSize: CGSize
- open let tileRect: CGRect
- open let image: UIImage
-
- public init(canvasSize: CGSize, tileRect: CGRect, image: UIImage)
- {
- self.canvasSize = canvasSize
- self.tileRect = tileRect
- self.image = image
- }
-}
-
-class RenderCache
-{
- let CACHE_LOWMEM = 4
- let CACHE_NORMAL = 20
-
- var cachedRenders: [CachedRender] = []
- var hasReceivedMemoryWarning = false
-
- let lock = NSRecursiveLock()
-
- func emptyCache()
- {
- lock.lock(); defer { lock.unlock() }
-
- cachedRenders.removeAll()
-
- }
-
- func pruneCache()
- {
- lock.lock(); defer { lock.unlock() }
-
- let max = hasReceivedMemoryWarning ? CACHE_LOWMEM : CACHE_NORMAL
- while cachedRenders.count > max
- {
- cachedRenders.remove(at: 0)
- }
- }
-
- func add(cachedRender: CachedRender)
- {
- lock.lock(); defer { lock.unlock() }
-
- cachedRenders.append(cachedRender)
- pruneCache()
- }
-
- func get(canvasSize: CGSize, tileRect: CGRect) -> UIImage?
- {
- lock.lock(); defer { lock.unlock() }
-
- if let cr = cachedRenders.first(where: { $0.canvasSize == canvasSize && $0.tileRect == tileRect })
- {
- return cr.image
- }
- return nil
- }
-
-}
-
-/**
- * Holds the document object so to enforce access in a thread safe way.
- */
-public class DocumentHolder
-{
- private let doc: Document
-
- public weak var delegate: DocumentUIDelegate? = nil
- public weak var searchDelegate: SearchDelegate? = nil
-
- private let cache = RenderCache()
-
- init(doc: Document)
- {
- self.doc = doc
- doc.registerCallback() {
- [weak self] typ, payload in
- self?.onDocumentEvent(type: typ, payload: payload)
- }
- }
-
- /// Gives async access to the document
- public func async(_ closure: @escaping (Document) -> ())
- {
- LOKitThread.instance.async
- {
- closure(self.doc)
- }
- self.invokeHandlers()
- }
-
- public func invokeHandlers()
- {
- LOKitThread.instance.async
- {
- self.doc.invokeHandlers()
- }
- }
-
- /// Gives sync access to the document - blocks until the closure runs.
- /// Careful of deadlocks.
- public func sync<R>( _ closure: @escaping (Document) -> R ) -> R
- {
- return LOKitThread.instance.sync
- {
- self.invokeHandlers()
- return closure(self.doc)
- }
- }
-
- /// Paints a tile and return synchronously, using a cached version if it can
- public func paintTileToImage(canvasSize: CGSize,
- tileRect: CGRect) -> UIImage?
- {
- if let cached = cache.get(canvasSize: canvasSize, tileRect: tileRect)
- {
- return cached
- }
-
- let img = sync {
- $0.paintTileToImage(canvasSize: canvasSize, tileRect: tileRect)
- }
- if let image = img
- {
- cache.add(cachedRender: CachedRender(canvasSize: canvasSize, tileRect: tileRect, image: image))
- }
-
- return img
- }
-
-
- private func onDocumentEvent(type: LibreOfficeKitCallbackType, payload: String?)
- {
- print("onDocumentEvent type:\(type) payload:\(payload ?? "")")
-
- switch type
- {
- case LOK_CALLBACK_INVALIDATE_TILES:
- runOnMain {
- self.delegate?.invalidateTiles( rects: decodeRects(payload) )
- }
- case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
- runOnMain {
- self.delegate?.invalidateVisibleCursor( rects: decodeRects(payload) )
- }
- case LOK_CALLBACK_TEXT_SELECTION:
- runOnMain {
- self.delegate?.textSelection( rects: decodeRects(payload) )
- }
- case LOK_CALLBACK_TEXT_SELECTION_START:
- runOnMain {
- self.delegate?.textSelectionStart( rects: decodeRects(payload) )
- }
- case LOK_CALLBACK_TEXT_SELECTION_END:
- runOnMain {
- self.delegate?.textSelectionEnd( rects: decodeRects(payload) )
- }
-
- case LOK_CALLBACK_SEARCH_NOT_FOUND:
- runOnMain {
- self.searchDelegate?.searchNotFound()
- }
- case LOK_CALLBACK_SEARCH_RESULT_SELECTION:
- runOnMain {
- self.searchResults(payload: payload)
- }
-
-
- default:
- print("onDocumentEvent type:\(type) not handled!")
- }
- }
-
- private func searchResults(payload: String?)
- {
- if let d = payload, let data = d.data(using: .utf8)
- {
- let decoder = JSONDecoder()
-
- do
- {
- let searchResults = try decoder.decode(SearchResults.self, from: data )
-
- /*
- if let srs = searchResults.searchResultSelection
- {
- for par in srs
- {
- print("\(par.rectsAsCGRects)")
- }
- }
- */
-
- self.searchDelegate?.searchResultSelection(searchResults: searchResults)
- }
- catch
- {
- print("Error decoding payload: \(error)")
- }
-
- }
- }
-
- public func search(searchString: String, forwardDirection: Bool = true, from: CGPoint)
- {
- var rootJson = JSONObject()
-
- addProperty(&rootJson, "SearchItem.SearchString", "string", searchString);
- addProperty(&rootJson, "SearchItem.Backward", "boolean", String(!forwardDirection) );
- addProperty(&rootJson, "SearchItem.SearchStartPointX", "long", String(describing: from.x) );
- addProperty(&rootJson, "SearchItem.SearchStartPointY", "long", String(describing: from.y) );
- addProperty(&rootJson, "SearchItem.Command", "long", "0") // String.valueOf(0)); // search all == 1
-
- if let jsonStr = encode(json: rootJson)
- {
- async {
- $0.postUnoCommand(command: ".uno:ExecuteSearch", arguments: jsonStr, notifyWhenFinished: true)
- }
- }
- }
-
-
-}
-
-public typealias JSONObject = Dictionary<String, AnyObject>
-public func addProperty( _ json: inout JSONObject, _ parentValue: String, _ type: String, _ value: String)
-{
- var child = JSONObject();
- child["type"] = type as AnyObject
- child["value"] = value as AnyObject
- json[parentValue] = child as AnyObject
-}
-
-func encode(json: JSONObject) -> String?
-{
- //let encoder = JSONEncoder()
-
- if let data = try? JSONSerialization.data(withJSONObject: json, options: .prettyPrinted)
- {
- return String(data: data, encoding: String.Encoding.utf8)
- }
- return nil
-}
-
-/// Decodes a series of rectangles in the form: "x, y, width, height; x, y, width, height"
-public func decodeRects(_ payload: String?) -> [CGRect]?
-{
- guard var pl = payload else { return nil }
- pl = pl.trimmingCharacters(in: .whitespacesAndNewlines )
- if pl == "EMPTY" || pl.count == 0
- {
- return nil
- }
- var ret = [CGRect]()
- for rectStr in pl.split(separator: ";")
- {
- let coords = rectStr.split(separator: ",").flatMap { Double($0.trimmingCharacters(in: .whitespacesAndNewlines)) }
- if coords.count == 4
- {
- let rect = CGRect(x: coords[0],
- y: coords[1],
- width: coords[2],
- height: coords[3])
- ret.append( rect )
- }
- }
- return ret
-}
-
/**
* Delegate methods for global events emitted from LOKit.
* Mostly dispatched on the main thread unless noted.
@@ -405,85 +131,9 @@ public protocol LOKitUIDelegate: class
public protocol ProgressDelegate: class
{
func statusIndicatorStart()
-
func statusIndicatorFinish()
-
func statusIndicatorSetValue(value: Double)
}
-public protocol DocumentUIDelegate: class
-{
- func invalidateTiles(rects: [CGRect]? )
-
- func invalidateVisibleCursor(rects: [CGRect]? )
-
- func textSelection(rects: [CGRect]? )
- func textSelectionStart(rects: [CGRect]? )
- func textSelectionEnd(rects: [CGRect]? )
-}
-public protocol SearchDelegate: class
-{
- func searchNotFound()
-
- func searchResultSelection(searchResults: SearchResults)
-}
-
-/**
- Encodes this example json:
- {
- "searchString": "Office",
- "highlightAll": "true",
- "searchResultSelection": [
- {
- "part": "0",
- "rectangles": "1951, 10743, 627, 239"
- },
- {
- "part": "0",
- "rectangles": "5343, 9496, 627, 287"
- },
- {
- "part": "0",
- "rectangles": "1951, 9256, 627, 239"
- },
- {
- "part": "0",
- "rectangles": "6502, 5946, 626, 287"
- },
- {
- "part": "0",
- "rectangles": "6686, 5658, 627, 287"
- },
- {
- "part": "0",
- "rectangles": "4103, 5418, 573, 239"
- },
- {
- "part": "0",
- "rectangles": "1951, 5418, 627, 239"
- },
- {
- "part": "0",
- "rectangles": "4934, 1658, 1586, 559"
- }
- ]
- }
-*/
-public struct SearchResults: Codable
-{
- public var searchString: String?
- public var highlightAll: String?
- public var searchResultSelection: Array<PartAndRectangles>?
-}
-
-public struct PartAndRectangles: Codable
-{
- public var part: String?
- public var rectangles: String?
-
- public var rectsAsCGRects: [CGRect]? {
- return decodeRects(self.rectangles)
- }
-}
diff --git a/ios/LibreOfficeLight/LibreOfficeLight/LOKit/RenderCache.swift b/ios/LibreOfficeLight/LibreOfficeLight/LOKit/RenderCache.swift
new file mode 100644
index 000000000000..f217db0414a6
--- /dev/null
+++ b/ios/LibreOfficeLight/LibreOfficeLight/LOKit/RenderCache.swift
@@ -0,0 +1,78 @@
+//
+// This file is part of the LibreOffice project.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+//
+
+import Foundation
+import UIKit
+
+
+open class CachedRender
+{
+ open let part: Int32
+ open let canvasSize: CGSize
+ open let tileRect: CGRect
+ open let image: UIImage
+
+ public init(part: Int32, canvasSize: CGSize, tileRect: CGRect, image: UIImage)
+ {
+ self.canvasSize = canvasSize
+ self.tileRect = tileRect
+ self.image = image
+ self.part = part
+ }
+}
+
+class RenderCache
+{
+ let CACHE_LOWMEM = 4
+ let CACHE_NORMAL = 20
+
+ var cachedRenders: [CachedRender] = []
+ var hasReceivedMemoryWarning = false
+
+ let lock = NSRecursiveLock()
+
+ func emptyCache()
+ {
+ lock.lock(); defer { lock.unlock() }
+ cachedRenders.removeAll()
+ }
+
+ func pruneCache()
+ {
+ lock.lock(); defer { lock.unlock() }
+
+ let max = hasReceivedMemoryWarning ? CACHE_LOWMEM : CACHE_NORMAL
+ while cachedRenders.count > max
+ {
+ cachedRenders.remove(at: 0)
+ }
+ }
+
+ func add(cachedRender: CachedRender)
+ {
+ lock.lock(); defer { lock.unlock() }
+
+ cachedRenders.append(cachedRender)
+ pruneCache()
+ }
+
+ func get(part: Int32, canvasSize: CGSize, tileRect: CGRect) -> UIImage?
+ {
+ lock.lock(); defer { lock.unlock() }
+
+ if let cr = cachedRenders.first(where: {
+ $0.canvasSize == canvasSize
+ && $0.tileRect == tileRect
+ && $0.part == part
+ })
+ {
+ return cr.image
+ }
+ return nil
+ }
+}