Cherrio LLC
12/18/2022, 1:22 PM+= view
. Any api change or i have to import something?ayodele
12/19/2022, 8:15 AMayodele
12/29/2022, 7:00 PMmargin
be applied to a view itself instead of on the constraint
?ayodele
12/30/2022, 12:23 PMScrollPanel
with a SimpleMutableListModel
ayodele
01/02/2023, 12:56 PMNick
01/04/2023, 4:35 PMslider
, rangeSlider
, circularSlider
, circularRangeSlider
, file
and files
)
It also brings some performance improvements and fixes.Nick
01/07/2023, 3:33 PMCherrio LLC
01/08/2023, 5:03 PMval view = object: View(){}
Nick
01/08/2023, 5:12 PMlayout
is protected
to prevent arbitrary code from changing it for a view that assumes it has a particular one. Same goes for the render. But it’s easy to make a new view with whatever you’d like for these. The first option is to use the view builder. This will let you specify the render behavior (but not layout, though this might make sense to expose there as well).
val view = view {
render = {
// this points to a Canvas
rect(…)
}
}
The second is to override render or set layout in a derived class:
object: View() {
init {
layout = …
}
override render(…) {…}
}
Anant Kumar Gupta
01/13/2023, 3:38 PMNick
01/13/2023, 3:56 PMAnant Kumar Gupta
01/14/2023, 3:37 AMAnant Kumar Gupta
01/14/2023, 3:42 AMCherrio LLC
02/08/2023, 6:11 PMsimpleLayout
code in threadCherrio LLC
02/14/2023, 1:39 PMShellWen 颉文
02/15/2023, 4:29 AMCherrio LLC
02/19/2023, 5:02 PMrenderProperty
, to change the value of textField? When I try, I get:Nazar Pohonchuk
02/22/2023, 10:06 PMplugins {
id ("org.jetbrains.kotlin.js") version "1.7.21"
}
group = "org.example"
version = "1.0-SNAPSHOT"
repositories {
mavenCentral()
}
kotlin {
js().browser()
val doodleVersion = "0.9.0" // <--- Latest Doodle version
dependencies {
implementation ("io.nacular.doodle:core:$doodleVersion" )
implementation ("io.nacular.doodle:browser:$doodleVersion")
// Optional
implementation ("io.nacular.doodle:controls:$doodleVersion" )
implementation ("io.nacular.doodle:animation:$doodleVersion")
implementation ("io.nacular.doodle:themes:$doodleVersion" )
}
}
Main.kt
import io.nacular.doodle.application.Application
import io.nacular.doodle.application.application
fun main() {
application {
UsefulApp()
}
}
class UsefulApp: Application {
init {
//println("Hi!")
}
override fun shutdown() {}
}
When I run gradle task 'browserRun', I get thisNick
02/22/2023, 10:54 PMNick
02/23/2023, 4:56 PMprintln
statements to verify that your code is being executed? last thing to consider is whether display.size
is empty by chance. that would mean your view has no size. as defined, your view's size will be a fixed value and won't change as you resize the window. you can fix that by doing:
class HelloDoodle(display: Display): Application {
init {
val view = view {
render = {
text("Hello, Doodle!", color = Black)
}
}
display += view
display.fill(White.paint)
display.layout = constrain(view, fill) // adds a constraint based layout that causes view to fit the display
}
override fun shutdown() {}
}
Cherrio LLC
02/27/2023, 1:02 AMview.bounds
but couldn't so farNazar Pohonchuk
03/01/2023, 9:13 PMclass TicTacToe(display: Display, textMetrics: TextMetrics) : Application {
private var playerMoves = Player.X
private fun getPlayerName(): String {
val playerName = playerMoves.toString()
playerMoves = playerMoves.next
return playerName
}
init {
val panel = GridPanel().apply {
rowSpacing = { 10.0 }
columnSpacing = { 10.0 }
rowSizingPolicy = FitPanel // FitContent, or custom policy
columnSizingPolicy = FitPanel // FitContent, or custom policy
for (i in 0..2)
for (j in 0..2) {
add(Cell(textMetrics, ::getPlayerName), row = i, column = j)
}
val panelSize = min(display.width, display.height) * 0.8
size = Size(panelSize, panelSize)
Resizer(this).apply { movable = false }
}
display += panel
display.layout = constrain(display.first(), center)
}
override fun shutdown() {}
}
class Cell(textMetrics: TextMetrics, getPlayerName: () -> String) : View() {
var text by renderProperty("")
init {
this.pointerChanged += object : PointerListener {
override fun clicked(event: PointerEvent) {
if (text.isEmpty()) text = getPlayerName()
}
}
}
override fun render(canvas: Canvas) {
canvas.rect(bounds.atOrigin, Blue.lighter(0.4f))
canvas.text(text, at = Point(width / 2, height / 2), color = White)
}
}
enum class Player {
X {
override fun toString(): String {
return "X";
}
},
O {
override fun toString(): String {
return "O";
}
};
val next
get() = if (this == X) O else X
}
fun main() {
application(modules = listOf(PointerModule)) {
TicTacToe(display = instance(), textMetrics = instance())
}
}
Nazar Pohonchuk
03/01/2023, 9:14 PMNazar Pohonchuk
03/01/2023, 11:15 PMStan
03/02/2023, 1:18 PMis the project really active?
2) Many of the UI components don't render until I click the "Usage" link, then go back to "Demo".
https://nacular.github.io/doodle/docs/ui_components/overview
The ony Animations demo that renders is the #key-frames demo.
https://nacular.github.io/doodle/docs/animations
https://nacular.github.io/doodle/docs/animations#key-frames
Are there known issues about many of the demos not rendering?
I know the project is young and you say there are / will be bugs, and they could be due to other
reasons, i.e., browser extensions? OS (Linux) related? PEBKAC?
Regardless, I'm still interested.
3) I'm particularly interested in the doodle rendering and animations -- no CSS!?!
Did I mention no CSS!?!
Do you think there is a chance that any of the jewlry maker's photoshop chops transfer to
any aspects or concepts behind doodle code supporting image design, rendering, and/or animations?
4) Finally, can I "Build a full-stack web app with Kotlin Multiplatform", using only the doodle
ui-components I mentioned in item (#3 rendering and animations)?
https://kotlinlang.org/docs/multiplatform-full-stack-app.html
In other words, I want to build a Kotlin KMM system (first for web, then mobile,) with a small number
doodle widgets, not a a full blown "doodle system".
Does that make sense? Or do I have to go "all in"? That is probably a dumb question, due to my
lack of Kotlin chops. My java experience tells me "of course" I can use just the doodle widgets
I need in the web views that need them, but ask anyway. (You can laugh.)Nick
03/10/2023, 4:52 PMPopupManager
. Before, you could make custom popups by directly adding items to the Display
. This worked in many cases, but had a lot of limitations. For example, Views added this way would be affected by any Layout
on the Display
, which meant you couldn't control these Popups as easily. Also, new Views added to the Display
after a custom popup was shown could overlay the popup. So there was no way to guarantee that a popup remained the top-most View. The new PopupManager fixes these limitations and provides a simple API for showing popups.
Any View Can Be A Popup
The PopupManager's API works with View
, so you can make anything a popup. Simply do the following:
popupManager.show(myView) {
it.center eq parent.center
}
// ...
popupManager.hide(myView)
Views that are shown as popups will automatically become top-level (being removed from their existing parent if already displayed) and sit above all other existing Views. Hiding a popup removes it from the Display
, but it won't return the View to a previous parent if it had one.
Popup Layouts
Popups are shown with layout information directly. There are two ways to show a popup: relative to the Display
, or relative to another View
and the Display
. There is an API call for each:
Here, myView
is positioned with only a "reference" to the Display (parent).
popupManager.show(myView) {
it.center eq parent.center
}
But sometimes a popup needs to be positioned relative to another View. This shows how to place myView
so it tracks the bounds of someView
. The PopupManager will handle keeping the popup aligned with someView
.
// myView is positioned with only a "reference" to the Display (parent)
popupManager.show(myView, relativeTo = someView) { popup, anchor ->
<http://popup.top|popup.top> greaterEq 0 // Popup top-left always visible
popup.left greaterEq 0 // Popup top-left always visible
popup.width.preserve // don't shrink popup
popup.height.preserve // don't shrink popup
(popup.right lessEq parent.right ) .. Strong // stay in parent as long as doesn't compress popup
(popup.bottom lessEq parent.bottom) .. Strong // stay in parent as long as doesn't compress popup
(<http://popup.top|popup.top> eq anchor.bottom + 10) .. Medium // follow anchor, as long as stronger constraints not in conflict
(popup.centerY eq anchor.centerY ) .. Medium // follow anchor, as long as stronger constraints not in conflict
}
More Powerful Text Rendering
Text Alignment
There is a new TextAlignment
enum that controls how wrapped text is displayed within its margins. This is a replacement for HorizontalAlignment
, which currently uses Left
, Right
(instead of Start
, End
) and does not support Justify
. You can justify text by doing the following:
canvas.wrapped(
text,
at = Origin,
leftMargin = 0.0,
rightMargin = width,
alignment = TextAlignment.Justify,
fill = Black.paint
)
canvas.wrapped(
styledText,
at = Origin,
leftMargin = 0.0,
rightMargin = width,
alignment = TextAlignment.Justify,
)
Letter, Word, And Line Spacing
You can now control the way letters, words and lines are spaced when rendering text. Letter and words spacing can be provided to the text rendering methods on Canvas
using the new TextSpacing
class. This information can also be passed to TextMetrics
when measuring text. Label
also has support for both letter and word spacing.
Line spacing can also be specified whenever you deal with wrapped text.
APIs
• Rectangle.toPath
that allows specifying each corner radius
• Exposing insets
in simpleTextButtonRenderer
function
• repeat
and loop
animations can now have delays
• Canvas
can now render wrapped text with custom line-spacing
• Label
now has lineSpacing
property which controls how it displays wrapped text
• Animatable Properties are no longer restricted to use within Views
• New method to create Ellipse
and Circle
by inscribing within Rectangle
• New Circle.diameter
property
• New constructor for StyledText
that takes a String
and Style
Accessibility
• Browser
◦ Button
now sets accessibilityLabel
to its text
if no label is already provided
◦ HyperLink
with native behavior will now apply aria-label to the anchor tag using the HyperLink's text when the AccessibilityManager
is present
Fixes | Improvements
• General
◦ Bug with empty AnimationBlock when created during active animation
◦ Wrapped text not correctly aligned
◦ Bug in FilteredList
iterator.remove
◦ CommonLabelBehavior
no longer provides an x offset for wrapped text since the alignment is handled correctly by Canvas
◦ Edge case where layout can hang b/c of double imprecision
◦ Issue where CommonLabelBehavior
overrides Label.foregroundColor
on uninstall
◦ Bug where incorrect item could be removed from RenderManagerImpl.pendingLayout
◦ Bug where some View properties lost during behavior install
◦ Bug where some views not cleaned up by RenderManager
◦ Bug in pointer handling when Display
transformed
◦ ToggleButton
no longer relies on being displayed to listen to its model
◦ StyledText.text
now returns correct value
• Browser
◦ Misidentifying elements as native scroll panels led to incorrect pointer behavior
◦ Reuse instances for linear/radial gradient paints
◦ Reusing clipped images
◦ Hyperlinks now open in new tab
◦ Shadow render bug when using SVG
◦ Element sometimes not reused when drawing shadow
◦ Remove overflow on element w/ shadow
◦ Fixed wrapped StyledText
rendering
◦ Wrapped StyledText
now supports text decoration on previously unsupported cases
• Desktop
◦ Fixed SVG image file loading issue
◦ Fixed rendering of images with radius
◦ Issue where font families weren't being properly tracked and therefore incorrectly applied
◦ Incorrect paragraph width measurementStan
03/10/2023, 4:52 PMStan
03/10/2023, 4:56 PMNick
03/10/2023, 4:57 PMNick
03/10/2023, 4:57 PMNick
03/10/2023, 4:57 PM