Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make selection optional when loading initial HTML #71

Merged
merged 1 commit into from
Nov 3, 2022
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
3 changes: 2 additions & 1 deletion MarkupEditor/MarkupEditorUIView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,11 @@ public class MarkupEditorUIView: UIView, MarkupDelegate {
wkUIDelegate: WKUIDelegate? = nil,
userScripts: [String]? = nil,
html: String?,
select: Bool = true,
resourcesUrl: URL? = nil,
id: String? = nil) {
super.init(frame: CGRect.zero)
webView = MarkupWKWebView(html: html, resourcesUrl: resourcesUrl, id: "Document", markupDelegate: markupDelegate ?? self)
webView = MarkupWKWebView(html: html, select: select, resourcesUrl: resourcesUrl, id: "Document", markupDelegate: markupDelegate ?? self)
// The coordinator acts as the WKScriptMessageHandler and will receive callbacks
// from markup.js using window.webkit.messageHandlers.markup.postMessage(<message>)
coordinator = MarkupCoordinator(markupDelegate: markupDelegate, webView: webView)
Expand Down
5 changes: 4 additions & 1 deletion MarkupEditor/MarkupEditorView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,15 @@ public struct MarkupEditorView: View, MarkupDelegate {
private var resourcesUrl: URL?
private var id: String?
private var html: Binding<String>?
private var select: Bool = true

public var body: some View {
VStack(spacing: 0) {
if MarkupEditor.toolbarLocation == .top {
MarkupToolbar(markupDelegate: markupDelegate, subToolbarEdge: .bottom).makeManaged()
Divider()
}
MarkupWKWebViewRepresentable(markupDelegate: markupDelegate, wkNavigationDelegate: wkNavigationDelegate, wkUIDelegate: wkUIDelegate, userScripts: userScripts, html: html, resourcesUrl: resourcesUrl, id: id)
MarkupWKWebViewRepresentable(markupDelegate: markupDelegate, wkNavigationDelegate: wkNavigationDelegate, wkUIDelegate: wkUIDelegate, userScripts: userScripts, html: html, select: select, resourcesUrl: resourcesUrl, id: id)
if MarkupEditor.toolbarLocation == .bottom {
Divider()
MarkupToolbar(markupDelegate: markupDelegate, subToolbarEdge: .top).makeManaged()
Expand All @@ -48,13 +49,15 @@ public struct MarkupEditorView: View, MarkupDelegate {
wkUIDelegate: WKUIDelegate? = nil,
userScripts: [String]? = nil,
html: Binding<String>? = nil,
select: Bool = true,
resourcesUrl: URL? = nil,
id: String? = nil) {
self.markupDelegate = markupDelegate ?? self
self.wkNavigationDelegate = wkNavigationDelegate
self.wkUIDelegate = wkUIDelegate
self.userScripts = userScripts
self.html = html
self.select = select
self.resourcesUrl = resourcesUrl
self.id = id
}
Expand Down
38 changes: 16 additions & 22 deletions MarkupEditor/MarkupWKWebView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public class MarkupWKWebView: WKWebView, ObservableObject {
/// The HTML that is currently loaded, if it is loaded. If it has not been loaded yet, it is the
/// HTML that will be loaded once it finishes initializing.
private var html: String?
private var select: Bool = true // Whether to set the selection after loading html
private var resourcesUrl: URL?
public var id: String = UUID().uuidString
public var userScripts: [String]? {
Expand Down Expand Up @@ -86,9 +87,10 @@ public class MarkupWKWebView: WKWebView, ObservableObject {
initForEditing()
}

public init(html: String? = nil, resourcesUrl: URL? = nil, id: String? = nil, markupDelegate: MarkupDelegate? = nil) {
public init(html: String? = nil, select: Bool = true, resourcesUrl: URL? = nil, id: String? = nil, markupDelegate: MarkupDelegate? = nil) {
super.init(frame: CGRect.zero, configuration: WKWebViewConfiguration())
self.html = html
self.select = select
self.resourcesUrl = resourcesUrl
if id != nil {
self.id = id!
Expand Down Expand Up @@ -140,10 +142,13 @@ public class MarkupWKWebView: WKWebView, ObservableObject {
firstResponder = MarkupEditor.observedFirstResponder.$id.sink { [weak self] selectedId in
guard let self, self.id == selectedId, !self.hasFocus else { return }
if (self.becomeFirstResponder()) {
//print("\(self.id) became firstResponder")
print("\(self.id) became firstResponder")
MarkupEditor.selectedWebView = self
self.hasFocus = true
self.getSelectionState()
self.getSelectionState { selectionState in
print(" Resetting MarkupEditor selectionState")
MarkupEditor.selectionState.reset(from: selectionState)
}
}
}
}
Expand Down Expand Up @@ -605,7 +610,7 @@ public class MarkupWKWebView: WKWebView, ObservableObject {

public func setHtml(_ html: String, handler: (()->Void)? = nil) {
self.html = html // Our local record of what we set, used by setHtmlIfChanged
evaluateJavaScript("MU.setHTML('\(html.escaped)')") { result, error in
evaluateJavaScript("MU.setHTML('\(html.escaped)', \(select))") { result, error in
handler?()
}
}
Expand All @@ -627,6 +632,10 @@ public class MarkupWKWebView: WKWebView, ObservableObject {
}
}

private func contentHeight(from clientHeight: Int) -> Int {
return clientHeight + 2 * bodyMargin
}

public func cleanUpHtml(handler: ((Error?)->Void)?) {
evaluateJavaScript("MU.cleanUpHTML()") { result, error in
handler?(error)
Expand Down Expand Up @@ -852,22 +861,6 @@ public class MarkupWKWebView: WKWebView, ObservableObject {
}
}

//MARK: Autosizing

public func setHtmlAndHeight(_ html: String, handler: ((Int) -> Void)? = nil) {
evaluateJavaScript("MU.setHtml('\(html.escaped)')") { result, error in
self.getClientHeight() { clientHeight in
// Note that clientHeight does not reflect css-specified body margin
// which is present at top and bottom in addition to clientHeight
handler?(self.contentHeight(from: clientHeight))
}
}
}

private func contentHeight(from clientHeight: Int) -> Int {
return clientHeight + 2 * bodyMargin
}

//MARK: Paste

/// Return the Pasteable type based on the types found in the UIPasteboard.general
Expand Down Expand Up @@ -1028,7 +1021,8 @@ public class MarkupWKWebView: WKWebView, ObservableObject {
/// Get the selectionState async and execute a handler with it.
///
/// Note we keep a local copy up-to-date so we can use it for handling actions coming in from
/// the MarkupMenu and hot-keys
/// the MarkupMenu and hot-keys. Calls to getSelectionState here only affect the locally cached
/// selectionState, not the MarkupEditor.selectionState that is reflected in the MarkupToolbar.
public func getSelectionState(handler: ((SelectionState)->Void)? = nil) {
evaluateJavaScript("MU.getSelectionState()") { result, error in
guard
Expand All @@ -1049,7 +1043,7 @@ public class MarkupWKWebView: WKWebView, ObservableObject {
newSelectionState = SelectionState()
}
self.selectionState.reset(from: newSelectionState)
handler?(self.selectionState)
handler?(newSelectionState)
}
}

Expand Down
5 changes: 4 additions & 1 deletion MarkupEditor/MarkupWKWebViewRepresentable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public struct MarkupWKWebViewRepresentable: UIViewRepresentable {
private var resourcesUrl: URL?
private var id: String?
@Binding private var html: String
private var select: Bool

/// Initialize with html content that is bound to an externally-held String (and therefore changable)
///
Expand All @@ -39,13 +40,15 @@ public struct MarkupWKWebViewRepresentable: UIViewRepresentable {
wkUIDelegate: WKUIDelegate? = nil,
userScripts: [String]? = nil,
html: Binding<String>? = nil,
select: Bool = true,
resourcesUrl: URL? = nil,
id: String? = nil) {
self.markupDelegate = markupDelegate
self.wkNavigationDelegate = wkNavigationDelegate
self.wkUIDelegate = wkUIDelegate
self.userScripts = userScripts
_html = html ?? .constant("")
self.select = select
self.resourcesUrl = resourcesUrl
self.id = id
}
Expand All @@ -55,7 +58,7 @@ public struct MarkupWKWebViewRepresentable: UIViewRepresentable {
}

public func makeUIView(context: Context) -> MarkupWKWebView {
let webView = MarkupWKWebView(html: html, resourcesUrl: resourcesUrl, id: id, markupDelegate: markupDelegate)
let webView = MarkupWKWebView(html: html, select: select, resourcesUrl: resourcesUrl, id: id, markupDelegate: markupDelegate)
// By default, the webView responds to no navigation events unless the navigationDelegate is set
// during initialization of MarkupEditorUIView.
webView.navigationDelegate = wkNavigationDelegate
Expand Down
22 changes: 17 additions & 5 deletions MarkupEditor/Resources/markup.js
Original file line number Diff line number Diff line change
Expand Up @@ -1615,7 +1615,7 @@ const unmuteFocusBlur = function() {
* Restore the range captured on blur and then let Swift know focus happened.
*/
MU.editor.addEventListener('focus', function(ev) {
_restoreSelection();
if (MU.currentSelection) { _restoreSelection() }; // No need if nothing to restore
if (!_muteFocusBlur) {
//_consoleLog("focused: " + ev.target.id)
_callback('focus');
Expand Down Expand Up @@ -2617,7 +2617,7 @@ MU.emptyDocument = function() {
*
* @param {String} contents The HTML for the editor element
*/
MU.setHTML = function(contents) {
MU.setHTML = function(contents, select=true) {
// Note for history:
// Originally this method used a div tempWrapper and just assigned contents to its innerHTML.
// In doing so, the image.onload method would fire, but I could never get an event listener to
Expand All @@ -2630,7 +2630,19 @@ MU.setHTML = function(contents) {
_prepImages(element);
MU.editor.innerHTML = ''; // Clean it out!
MU.editor.appendChild(element);
_initializeRange();
// By default, we initialize range to point to the first element. In cases where you are
// using multiple MarkupWKWebViews, you may want to explicitly prevent the range from
// being initialized and the first element being selected by passing select=false. Otherwise,
// each of your views will receive a multiple selectionChange events after they load,
// which in turn will propagate calls to the MarkupDelegate about that change, and potentially
// update the MarkupToolbar when all you wanted to do was to load the content and deal
// with selection later.
if (select) {
_initializeRange(); // Causes a selectionChange event
_focusOn(MU.editor, 30).then(_callback('updateHeight')); // Longer delay helps
} else {
_callback('updateHeight');
};
};

/**
Expand Down Expand Up @@ -2769,8 +2781,8 @@ const _initializeRange = function() {
_backupSelection();
} else {
MU.emptyDocument()
}
_focusOn(MU.editor).then(_callback('updateHeight'));
};
// Caller has to do _focusOn and/or callback to updateHeight if needed
};

/**
Expand Down