Namek Dev
a developer's log
NamekDev

JavaFX WebView: snippets

March 14, 2016

Today I present a few snippets around JavaFX WebView that I use in The Console:

  • load style sheet
  • set style or class of element
  • scroll page to the bottom
  • check whether the page is scrolled down to the bottom
  • handle adding DOM elements while web engine loads
  • remove some elements from document
  • style scrollbar in the document

Load style sheet

webView.engine.userStyleSheetLocation = getClass().getResource("ConsoleOutput_WebView.css").toString

Set element style or class

entryNode.setAttribute("style", "color: #" + hexColor)
entryNode.setAttribute("class", className)

Scroll to the bottom

Since HTML page is not a domain of JavaFX but our WebView, simply scrolling down to the bottom of page is a matter of internal WebView scrollbar rather than any JavaFX control. That’s why we have to handle this situation with JavaScript.

def void scrollToBottom() {
	val script = 'window.scrollTo(0, document.body.scrollHeight)'
	engine.executeScript(script)<br>}
}

Is it scrolled down?

In The Console I scroll down the page when new text line appears. But sometimes it’s a bit frustrating when you want to read certain lines and now ones keep being added. That’s why I want to check whether page is scrolled down to the bottom or not. If the page wasn’t scrolled down before adding a text entry then I don’t scroll it down automatically after adding the text entry. JavaScript comes again.

def boolean isScrolledToBottom() {
	val script = '(window.innerHeight + window.scrollY) >= document.body.offsetHeight'
	return engine.executeScript(script) as Boolean
}

Handle adding data to HTML during loading

WebView consists of WebKit engine which is loaded asynchronously. In The Console I write initialization logs to console too, so I need it as quick as possible. Instead of waiting for initialization I create tasks and push them to the queue. When succeeds loading then I launch all tasks.

// this could be in class
val BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>

// this in some initialization method
webView.engine.loadWorker.stateProperty.addListener([ observableValue, state, newState |
	if (newState.equals(Worker.State.SUCCEEDED)) {
		while (!queue.empty) {
			queue.poll.run()
		}
	}
])
engine.loadContent("<html><head></head><body></body></html>")

And here’s a method I used for appending text entries to the console, even if it’s still not loaded - by queuing them as tasks.

def private createTextEntry(String text, String styleClass, BiConsumer<Element, Text> nodeModifier) {
	val entry = createEntry()

	val Runnable task = [
		val wasAlreadyScrolled = isScrolledToBottom()

		val doc = engine.document
		val body = doc.getElementsByTagName("body").item(0)
		val entryNode = doc.createElement("pre")
		val textNode = doc.createTextNode(text)

		entryNode.appendChild(textNode)
		body.appendChild(entryNode)

		entry.entryNode = entryNode
		entry.textNode = textNode

		var className = 'entry-text'
		if (styleClass != null) {
			className += ' ' + styleClass
		}
		entryNode.setAttribute("class", className)

		if (nodeModifier != null) {
			nodeModifier.accept(entryNode, textNode)
		}

		if (wasAlreadyScrolled) {
			scrollToBottom()
		}
	]

	if (!isWebLoaded) {
		queue.put(task)
	}
	else {
		task.run()
	}

	return entry
}

Remove some elements

Here’s another job that needs dealing with a document. But this time, instead of injecting JavaScript code, we’re going to use the power of org.w3c.dom.

override clear() {
	val Runnable task = [
		val doc = engine.document
		val body = doc.getElementsByTagName("body").item(0)

		for (entry : entries) {
			body.removeChild(entry.entryNode)
			entry.dispose()
		}
	]

	if (!isWebLoaded) {
		queue.put(task)
	}
	else {
		task.run()
	}
}

entries  is a custom collection that contains entryNode s.

Style WebView scrollbar

Styling again but this time it’s a little harder. Styling JavaFX scrollbars is easy but it’s a little harder to find out how to style scrollbars inside WebView’s document. Here’s what I used for The Console.

::-webkit-scrollbar {
    width: 16px;
}
::-webkit-scrollbar-track  {
    background-color: black;
}

::-webkit-scrollbar-thumb {
    border: 1px solid rgba(255, 255, 255, 0.2);
    border-radius: 6px;
    box-shadow: inset 0 0 6px rgba(0,0,0,0.5);
    background: black;
}

::-webkit-scrollbar-thumb:vertical {
    min-height: 100px;
}

::-webkit-scrollbar-thumb:hover, ::-webkit-scrollbar-thumb:active {
	background: rgba(255, 255, 255, 0.1);
}

Daj Się Poznać, java, xtend, the-console
comments powered by Disqus