Demo looks great! Just want to say I’m excited by this project!
In principle, can JIVE work with dynamic / runtime CSS styles, or do they have to be specified at compile time?
Everything’s done at runtime so yeah you can update any layout or style properties whenever you need and the JIVE wrappers will pick those up. It’s all based on juce::ValueTree
and juce::DynamicObject
so it’s just a matter of setting the right properties.
See https://github.com/ImJimmi/JIVE/tree/main/jive_style_sheets and https://github.com/ImJimmi/JIVE/tree/main/jive_layouts for a list of properties
The more I’m thinking about it, the more of a use case for this in my project, and I’m eager to try it out. Hope you don’t mind me asking lots more questions over the coming weeks!
I don’t see any provisions for Components with custom SVG paths. Is this something you have plans for, or is this somewhere where I could implement my own GuiItemDecorator
?
Edit: I’ve clone the repo, but I’m having trouble building the demo runner. Do I need to create a new project, or is there a project file already set up for it?
You can embed SVG elements directly, or you can use an <Image>
element and set the source
property:
auto someComponent {
return juce::ValueTree {
"Component",
{},
{
// Inline svg
juce::ValueTree::fromXml ("<svg>...</svg>");
// Image element with SVG source
juce::ValueTree {
"Image",
{
{ "source", "<svg>...</svg>" },
}
},
},
};
};
I prefer to use inline SVG myself.
If you already have some existing components that you don’t want to migrate, you could add them to the component factory:
jive::Interpreter interpreter;
interpreter
.getComponentFactory()
.set("MyComponent",
[] {
return std::make_unique<MyComponent>();
});
auto view = interpreter.interpret(juce::ValueTree{
"MyComponent",
});
Depending on your needs, you may also need to add a custom decorator to handle the synchronisation of your custom component and the juce::ValueTree
representing it.
class MyComponentGuiItem : public jive::GuiItemDecorator
{
//...
};
interpreter.addDecorator<MyComponentGuiItem>("MyComponent");
How are you building it? It should be as simple as:
> git clone git@github.com:ImJimmi/JIVE.git
> cd JIVE
> cmake -B build -G <generator (e.g. Xcode)> -DJIVE_BUILD_DEMO_RUNNER=ON
> cmake --build build
I haven’t made a Projucer project for the demo runner so if you’re using that then yeah you may need to set up your own. I should probably add a Projucer project for it anyway…
Yes, please add a Projucer project. As for my own needs, I have no plans to move to CMake in the foreseeable future.
Will do.
FWIW all the modules are formatted as JUCE modules so they’ll work fine with the Projucer, just the demo project doesn’t have a .jucer
project yet.
Thank you. Much appreciated!
Hello, I started playing around with Jive tonight, and I can’t get Style sheets to work. I’ve basically copied your Getting Started guide verbatim, like this:
juce::ValueTree testText()
{
return { "Text",
{
{"text", "test text"}
}
};
}
juce::ValueTree window()
{
static constexpr auto style = []() {
return new jive::Object{
{ "background", "#254E70" },
{ "foreground", "#AEF3E7" },
{ "font-family", "Helvetica" },
{ "font-size", 25 }
};
};
return {
"Window",
{
{ "width", 440 },
{ "height", 200 },
{ "style", style() }
},
{ testText() }
};
}
std::unique_ptr<jive::GuiItem> getJiveWindow() // goes to JUCEApplication
{
jive::Interpreter jive;
return jive.interpret(window());
}
The window and the text box are displayed, but the style doesn’t get applied–it’s just the default style. Any idea what the problem is? I’m guessing it’s something very simple, and possibly a step that you forgot to mention in your Getting Started guide. When I was stepping through the code, I had the impression that the new style was being applied but then got reset, in case that helps.
Sounds like you could have the same issue as here: Getting started and CMake integration example errors · Issue #126 · ImJimmi/JIVE · GitHub
You likely need to define JIVE_GUI_ITEMS_HAVE_STYLE_SHEETS=1
.
This is turned off by default so the two modules jive_layouts
and jive_style_sheets
don’t depend on one another. I’ve been meaning to think of a nicer way to do this.
Yes that did it, thank you!
Whoah! Super cool. We definitely have a use case since we use Figma a lot for our component design. I wonder how we’d integrate it into our WYSIWYG design system, though.
Figma integration is something I’d like to add actually. There’s various exporters that can generate code from your Figma designs, and they have a nice plugin API that lets you write your own.
This would (hopefully) replace the need for a WYSIWYG editor as Figma would effectively act as your editor in that case.
That’s more of a quality-of-life thing though, at the moment I’m trying to focus on fleshing out features, so haven’t any plans to work on that any time soon.
I’ve now added Projucer projects for all the included example projects, as well as adding a plugin version of the demo app.
Check it out on the latest main
, or tag v1.0.4
I really like what you are trying to do here, and all the other GUI projects, it just helps developers. I’ve just cloned and am trying it out, nice work.
TBH I’m fairly new to JUCE (which is the basis of my thoughts below), and I started building my own basic GUI builder as I only needed something simple, using XML, as projects I had started the same way. I was working on a data structure (ValueTree), then a GUI to referto the ValueTree, which updated as child items were retrieved.
What I was trying to achieve was something fairly basic, describe the structure in XML (as JUCE handles that better than Json generally), read that into a ValueTree, and then describe the styles similarly, relating to the structure like HTML/css properties/classes. Most importantly to keep it simple, use external XML files that are easy to read and construct, so others on a project can pick up the structure and layout quickly, with the code reading in the external files, and doing the heavy lifting.
Needless to say, easier said than done. What you have done is MUCH further along than I have done so far, and better and more complete than I was planning to do. It’s impressive, as are the other GUI builders.
For my 2 cents of feedback, I wonder if there is a way to make the onboarding easier? Like that the structure and layouts are more familiar, like using XML/css files rather than learning new layouts in code?
Like the example you provided:
// .html
<div id="xyz" class="container">
</div>
// .css
.container {
display: flex;
flex-direction: column;
background-color: red;
border: 2px solid blue;
}
Could this be how the developer specifies it, then JIVE reads it into the required JIVE structure? Maybe this is a step towards other imports too, like Figma?
Again, just my 2 cents, and again, what you have done is great!
I don’t have any intension to officially support HTML in JIVE, I think that would be the wrong direction for this project. Couple reasons for this:
- JUCE already has a fully featured HTML renderer, the
juce::WebBrowserComponent
. If you want to use HTML, you should use that along with whatever JS and CSS frameworks your heart desires. I understand there are planned improvements to this approach coming in JUCE 8 so this will likely become an even more viable solution in the near future. - The HTML/CSS specification is HUGE. As soon as you start to “officially” support some of it, it becomes totally reasonable for anyone and everyone to request any and all features of those specifications be implemented.
Adding things like<div>
,<h1>
,<img>
etc. would be trivial - any contributor could smash that out in an afternoon. Things likedisplay: inline-block
wouldn’t be too tricky either. But what about things like<ul>
? More specifically, all the weird and wacky CSS properties that apply to it, like@counter-style
.
Trying to add support for all these things would completely derail the project IMO as all the development into the project would be spent reinventing wheels.
Some of my previous comments may somewhat contradict this so to clarify - for things like colour, I do wish to support the CSS <color>
data type. By this, I mean it should be possible to take a string like rgb(255 0 153)
and paste it into a CSS file, and into a C++ source file using JIVE and both produce an identical colour. However, I do not plan to support the CSS color
property as there are bound to be endless edge-cases and whatnot that would be wasted effort to try to replicate - instead I have foreground
which can be used to specify the colour of text, icons, etc.
I want JIVE to feel “native” to JUCE - it should feel familiar and comfortable for anyone who’s used JUCE for even just a short while. For that reason it should support only what is strictly necessery for developers to achieve whatever it is they need to achieve.
For example, JUCE doesn’t have any equivalent to HTML’s <ul>
(unordered list). If you want a bullet-point list in JUCE you’d have to write you own custom Component
class and build it from scratch. I’ll leave it up to your own imagination what that might entail, my guess would be ~150 lines of code.
JIVE doesn’t have an unordered-list type either, however what it does have is the ability to build one from scratch using relatively few lines of code. For example you might do something like this:
[[nodiscard]] auto listItem(const juce::String& item)
{
return juce::ValueTree {
"Component",
{
{ "flex-direction", "row" },
},
{
juce::ValueTree::fromXml ("<svg> ... </svg>"), // Bullet icon
juce::ValueTree { "Text", { "text", item } },
},
};
}
[[nodiscard]] auto unorderedList(const juce::StringArray& items)
{
juce::ValueTree view{
"Component",
{
{ "padding", "0 0 0 20" },
},
};
for (const auto& item : items)
view.appendChild(listItem(item), nullptr);
return view;
}
Two functions of less than 15 lines of code to do the equivalent of a 100+ line-of-code class is exactly what JIVE is built for.
As for making onboarding easier - I would like to make some more in-depth tutorials and more straight-forward examples at some point to help shallow the learning curve, but I haven’t any immediate plans.
Thanks for the reply. I probably shouldn’t have referred the your html/css example, as that seemed to have confused things.
I was meaning using XML as a templating option, parsing those into value trees to describe the UI structure, as an alternative option to writing explicitly in the code. Either way, thats not your approach which I respect.
Looking forward to where this project goes.
Ahh I see. Sorry I thought you meant HTML specifically.
You absolutely can use XML files if you like, or pretty much any other method that JUCE provides for constructing value trees. I use the ValueTree
constructor in the examples as it’s (currently) the way I’d recommend as it offers the most flexibility, but JIVE also includes some utilities to make working with XML easier.
jive::parseXML()
takes either a juce::XmlElement
, an XML-formatted string, or some XML string data (if loading from JUCE’s binary data mechanism for example) and will produce a juce::ValueTree
that you can pass to a jive::Interpreter
as in the examples.
Most of the heavy lifting in jive::parseXML()
is done by juce::ValueTree::fromXml()
interally however jive::parseXML()
has the additional benefit of handling inline text elements. For example,
<Button>
<Text>Hello World!</Text>
</Button>
This is valid XML but can’t be loaded directly to a juce::ValueTree
as juce::ValueTree
does not support inline text elements. So jive::parseXML()
will first convert this to:
<Button>
<Text text="Hello World!"/>
<Button>
Which is of course equivalent to
juce::ValueTree {
"Button",
{
},
{
juce::ValueTree {
"Text",
{
{ "text", "Hello, World!" },
},
},
},
};
Is that more what you had in mind?
Of course you can also do similar things for style sheets by reading JSON documents using juce::JSON::parse()
and passing the result to an element’s "style"
property. You could do this all in one line:
static constexpr auto button = R"(
<Button>
<Text>Hello, World!</Text>
</Button>
)";
static constexpr auto style = R"(
{
"background": "red"
}
)";
jive::Interpreter interpreter;
const auto view = interpreter.interpret(jive::parseXML(button).setProperty("style", style, nullptr));