And feel free to ask for help if you are strugglin...
# doodle
l
And feel free to ask for help if you are struggling with some aspects. I have quite a bit of experience putting GUI toolkits together and would be happy to contribute to your effort…
n
thanks for offering your time. i'll let you know where i could use some help.
do you have a lot of experience with CSS? if so, i could use your help with the embedded React/etc. component change. happy to share more if you’d be interested.
l
I there! I am not an expert with CSS and therefore do not always come up with elegant (technically speaking) solutions, but I know CSS well and can usually always find a way to achieve what I want! Happy to do my best if this can be helpful. Could be good to put a small project together that exemplify how to embed a React component: maybe as part of the main Doodle repository so that things can be adjusted on both sides.
n
no worries; i’m no expert either 😅. i’ll share the initial work in a branch. it allows embedding an arbitrary element into a special View. the issue i started looking at is preventing Doodle’s global CSS from taking affect for any such nested content.
l
This is the type of issue I am usually able to resolve: i.e. understand what is going on but then maybe not come up with the best workaround!
n
ok. might be simpler to just share the direction here in chat for now instead of going down the branch route. the basic idea is to provide a new factory for creating a
View
that will have an embedded
HtmlElement
.
Copy code
public interface ForeignViewFactory {
    // creates a View that holds element (scaling it to 100%)
    public operator fun invoke(element: HTMLElement): View
}
the new View class itself could be really simple:
Copy code
internal class ForeignViewInternal(htmlFactory: HtmlFactory, private val element: HTMLElement): View() {
    private val root = htmlFactory.create<HTMLIFrameElement>().apply {
        this.className = "FOREIGN" // This would change to be some known class used to avoid styling children
        style.setWidthPercent (100.0)
        style.setHeightPercent(100.0)

        element.style.setWidthPercent (100.0)
        element.style.setHeightPercent(100.0)

        add(element)
    }


    override fun render(canvas: Canvas) {
        if (canvas is NativeCanvas) {
            canvas.addData(listOf(root))
        }
    }
}
then the next step would be to ensure the styles set in SystemStylerImpl don't affect anything within such a
View
, hence the
class
to tag things. let me know if this sounds like it would allow the right level of flexibility to embed React components.
👀 1
l
Sounds like your main source of struggle is to find a scheme where you can shield children elements from the CSS definitions applied higher up throughout the Doodle hierarchy. On the CSS front, this issue can easily be solved (since 2020 for all browsers) using the all: initial; property setting.
I would stay away from anything resorting to iframe!!!
n
how would
all: initial
be used? i know you can reset the CSS properties to their defaults with this short-cut. but wondering how to target this so it only undoes Doodle's styles for these sub components, while letting them use their own styles (inline or via CSS). i assumed i'd need to modify the styles applied in
SystemStylerImpl
so they ignore the sub-children (maybe via some wizardry with not()). but you think
all: initial
can work?
l
I need to do a bit of testing on my side: I am getting cold feet now!
👍 1
I had a deeper look using various React library (MUI in particular) and the
all: initial
should do the trick. Libraries are usually well-behaved these days and they will inject appropriate class names and styles from the element they are instructed to start with, without need to have definitions further up the hierarchy. Thus, my recommendation would be to do nothing on the Doodle side: just allow for creating an HTML element (typically a div) and populate it through a function call (typically
createRoot(div).render(NestedApp.create())
when using React). It would be recommended to set the
all: initial
property to the div element, but there may be people who would rather prefer to inherit from the Doodle CSS style. You could for exemple imagine to where people who want to use the logic of MUI Base but have a style that match the Doodle style. The only reason I could foresee where it would be beneficial that Doodle create that initial div element is if you want to have it sized based on Doodle layout system. I wrote this quickly because I am cooking at the same time, so let me know if something doesn't make any sense!
n
that did it! i had to put the style as a global one tied to a unique id for each one of these elements though. that's b/c i don't see a way of applying the necessary style (
* {all:initial}
, for child elements) inline.
hmm. it actually doesn't work as expected b/c of specificity. this rule gets overridden by the more specific ones in the styler (i.e.
${prefix("body")} div { display:inline }
, which win b/c they target an element type instead of
*
. back to the drawing board 😔.
l
I started to do further experiments this morning and I am also getting confused by some of the behaviors. I don’t know what the CSS folks had in mind when they designed all of this… as the use case and needs are pretty clear.
Here is the HTML document I am toying with at the moment. Maybe you have something to toy with…
Here is what ChatGPT has to say about the particular treatment of the display property: The
all
property in CSS affects all individual properties, but its impact on the
display
property may not always be straightforward. The
display
property controls how an element is rendered in the document, and its behavior can be affected by other properties as well as the context of the element. When you use the
all: unset;
rule, it resets all properties to their initial values or inherits them from the parent. However, the impact on the
display
property might not be as obvious, and it won’t necessarily change the
display
value back to its initial state. For example, if you have an element with a specific
display
value, such as
block
or
inline
, applying
all: unset;
won’t necessarily revert the
display
property to its default value. The
display
property is often determined by the default styling associated with the HTML element, and the
all
property might not directly affect it. To reset the
display
property to its initial state, you can explicitly set it in your CSS. For example:
Copy code
css

div { all: unset; display: initial; /* or display: block; or display: inline; depending on your needs */ }
This will explicitly set the
display
property to its initial value for the selected element(s). In summary, while the
all
property can unset many individual properties, its impact on the
display
property may require additional, explicit styling to achieve the desired result.
n
ok. i think i may have it working. screen shot showing an app w/ a Doodle View alongside an embedded React component that uses css for styling.
l
Very cool! Congratulation for taming the CSS beast. What was the solution to the madness?
n
had to avoid using an ID in the SystemStyler styles, since that has higher specificity than most CSS will (targeting classes). also had to use a
when()
selector to lower specificity for another rule. then i had to use a class (i.e. what
FOREIGN
was the placeholder for) and do
.className * { all:revert }
. this seemed to do the trick. but i can't guarantee there aren't some other edge cases. is this something you would be willing to also test out if i published another snapshot build? i don't do React or anything else in the web ecosystem. so my basic testing might miss some edge case(s).
l
I can certainly do some testing. I am a bit struggling with one limitation of Kotlin Multiplatform because I use a lot of expect/actual constructs and whereas for the JVM platform one can have multiple targets (e.g. android, swing, javafx, jvmCompose, jvmDoodle is what I usually have), for JS only one can be defined (where I wish to have react, jsCompose, jsDoodle, …).
n
new snapshot published (still
0.10.0-SNAPSHOT
). you can try out hosting html elements by injecting the
HtmlElementViewFactory
(available in the
Modules._HtmlElementViewModule
), and constructing a View as follows:_
Copy code
val view = htmlElementViewFactory(element = htmlElement)
l
Fantastic! Will dive into it mostly next week.
n
have you had time to play around with it yet?
l
Yes, but I conducted so far my experiments on the JVM and did not start testing the React integration! I don’t have yet a fully working software but part of it are functional. From the top of my head, here are few comments (you do not need to reply to each of them, they are more meant to give you a feeling of how things are perceived on the other end):
Canvas doesn’t seem to have the ability to stroke and fill paths.
TreeTable doesn’t display anything on the JVM. Maybe I am doing something wrong.
The example for the TreeTable at https://nacular.github.io/doodle/docs/ui_components/overview#treetable doesn’t implement the children property, which makes it impractical to just copy-paste the example code to try things out.
I would somehow prefer if TreeModel would not rely on an object and would instead have all the methods of path retrofitted into the TreeModel interface. This would eliminate some superfluous object creations when integrating with another hierarchical model.
You have strict dependencies to particular versions of the libraries (e.g. kotlinx-coroutines-core) that are difficult to override. Would you have a way to make it less strict?
n
super helpful! thanks for sharing. • stroking/filling paths is supported. can you elaborate on what's missing?
l
Great! Somehow I didn’t spot these methods even though I thought I did search for the fill/stroke/path!
n
TreeTable also works on desktop. might be a configuration issue on your side?
l
About graphics I wanted to express that it is “tiring” to rewrite code for each graphic subsystem (i.e. Swing’s Graphics2D, JavaFX GraphicsContext, Jetpack Compose DrawScope, HTML CanvasRenderingContext2D, etc. In my code base, I did find that the HTML Canvas API to be a good, although low-level, common denominator that can be emulated for each graphic subsystem (through adapter classes, with a bit of performance overhead). Could be an idea to implement something similar for Doodle, where it would make the programmer’s life easy to port/copy-paste graphic code. I believe there is also a need for something similar for vector graphics (aka SVG) that works cross-platform.
The reason why TreeTable doesn’t work on my end seems to be due to the fact that the model is not yet populated upon instantiation of the component. While I have `override val changed: Pool<ModelObserver<N>> = SetPool()`in my custom model and I do proper notification, no one is listening the for the changes (changed has 0 observer).
I take it back concerning vector graphics: I just discovered SvgImage. Will need to play with it!
In some of the examples for the Component Library, you do not provide the full code, e.g. io.nacular.doodle.docs.utils.panel for the SplitPanel example.
The React integration seems to work just greet, although I didn’t try it yet with components relying on CSS and such. It ain’t pretty, but you can have a look for yourself at: https://www.macrofocus.com/treemap-kotlin-doodle/
❤️ 1