I need to do some documentation, but looking through the source files should give you a pretty good idea of what’s going on. You’re smart people after all. If you make anything cool using them, be sure to share.
Just uploaded an update to the MinimalCompsJS project. The demo looks pretty much the same as the last one, but the components are completely refactored. Now, each component creates its own individual canvas element and adds it to a div (or any other element that can contain a canvas). So you don’t need to create any canvas at all to use them. This has massively simplified a lot of the code for redrawing and for mouse events.
One might ask, why use canvas at all? Why not just use text and styles? This would be possible, but using canvas as the base of each component has helped to unify how everything works and makes good use of the canvas drawing API, which I conceive will be useful as this goes forward.
I came up with a few nice little tricks in there too. One of the nicest, I think, is method chaining. Almost all publicly intended methods that don’t obviously return a value will return the component instance itself. This way you can create a component and chain together multiple methods on the end of it, and still wind up with a reference to the component you just created. Here’s a demo:
var distVSlider = new mc.VSlider(div, 260, 30, onChange)
.setSize(10, 300)
.setVSliderValues(0, 400, 170)
.setContinuous(false)
.bindLabel(new mc.Label(div, 265, 335, "170").setAlign("center"), 0);
This is just like you see often in jQuery. Fun stuff. Looks odd at first, but I’ve learned to love it.
Label now has a setAlign method, which takes “left”, “center” or “right”.
A few things on the VSlider. One you see in the example above, bindLabel. Here, you pass in an instance of a Label, and a precision value in terms of how many decimal points to display. Now, whenever the slider value changes, it will update the label with its value. You also see some more chaining in the last line there, where we create a label and set its alignment, which returns the label to the bindLabel method.
Speaking of precision, the getValue method takes an optional precision value. If you pass nothing, you might get 3.14159. If you pass 2, you’ll get 3.14. Passing 0 will give you 3. You can also use negative numbers. If you pass -1, it will round 54321 to 54320. passing -2 will give you 54300, etc.
Also, you see setContinuous. True means, your change handler will fire continuously as you slide the slider. False means it will only fire when you release the slider.
A while back, when I first started playing with HTML5 and Canvas, I considered the idea of making a port of MinimalComps. I immediately rejected the idea as silly. I mean, if you’re in HTML, you already have UI controls, right? But I mentioned it to a few people and tweeted about it, and I was amazed how much interest there was in such a thing. At FOTB last month, I was asked about it a couple of times. So, I got to work.
As you can see, there is a Slider (vertical only), a PushButton, Label, and CheckBox. Not shown, but also fully working is a RadioButton. While there are various bells and whistles that could be added to what’s there, they seem to be pretty functional and solid.
Here’s a code sample from that demo:
new mc.Label(stage, 10, 10, "Angles:");
var s1 = new mc.Slider(stage, 10, 30, onAngle);
var s2 = new mc.Slider(stage, 25, 30, onAngle);
var s3 = new mc.Slider(stage, 40, 30, onAngle);
s1.setSize(10, 360);
s2.setSize(10, 360);
s3.setSize(10, 360);
s1.setSliderValues(0, Math.PI * 2, 1);
s2.setSliderValues(0, Math.PI * 2, 2);
s3.setSliderValues(0, Math.PI * 2, 3);
As you can see, they are very similar in use to the AS3 versions.
There were so many design decisions to make on this project. And while I obviously made certain decisions, I am by no means committed to any of them and am open to suggestions.
First and foremost is the decision to make the comps fully Canvas based. All components get added to a “stage” object, which is a wrapper around a canvas. Generally you would make a small canvas and use that for your component panel, or section off a rectangular area of another canvas for use by the controls. For me, this was the easiest way to get started, but I’m going to experiment with making each component have its own canvas. Then they can each be added to a div, with absolute positioning. This would actually simplify various things such as mouse events and redrawing. Currently, I have to listen to all mouse events on the stage and determine which component they belong to and farm them out. As for redrawing, some component drawing can be done just within the component bounds, but if anything is resized or repositioned, the entire canvas needs to be redrawn. If each component was its own DOM element, a lot of that would go away. Lots of stuff to experiment and keep my little brain busy for the next few weeks or months.
Anyway, I haven’t released the code officially, but feel free to grab it from view source and play around if you want. Each component has its own file, plus an overall MinimalComps.js file. Eventually, I’ll combine these all into one js file and minify it.
Doing some Android dev these days. Needed to check if a editable text field was empty or not. I had to dig around to find out how to do this. It turns out that an EditText’s getText() method returns an instance of Editable. You need to convert this to a string and then call equals(“”) on it. So you get this:
if(myText.getText().toString().equals("")) {...}
The similar thing in Objective-C would be:
if([[myText text] isEqualToString:@""]) {...}
And in ActionScript:
if(myText.text == "") {...}
I’m going to hold my tongue here, and let you make your own judgments. But as a person who created MinimalComponents, I bet you can guess where my preference lies.
During JavaScript month, there was little time for much else. There were bugs and issues in MinimalComps that had piled up and I was ignoring them. So yesterday I sat down and went through the whole lot. I realized that several were not bugs, but simply known issues with known workaround, such as the need to add the flex.swc to your project if compiling in Flash CS4/5. The current site had no easy place to put this stuff. I could have worked more on the site to make it more dynamic, but wound up installing WordPress and a nice minimal theme. I’ve been working on it on and off over the weekend, adding a gallery, showcase, FAQ, etc. The site is at the same old place:
I’ve also been toying with the idea of moving the source over to github. I’ve been on a project with a client that uses git and finally got very familiar with it. I had many people begging me to use it for the comps. I pretty much decided on it a few weeks back, but wanted to straighten out this other stuff first.
So as of yesterday, the official MinimalComps repository is at:
As of this moment, what’s there is identical with what’s on Google Code, but you should consider the Google Code site dead. I’m not going to update it there any more, and will eventually pull it down.
One of the pages on the new site is the Showcase, with links to sites and projects using MinimalComps. You can see it here, and feel free to add your own links to any projects in the comments.
First is on adding content to Windows and Panels. By default, if you added a child to one of these, it would just add it like any other child, positioning it from the top left of the parent’s position. In particular on the Window, this forced you to reposition your content so it wasn’t on top of the title bar. Also, in either case, if your content was larger than the parent, it would march on outside of the bounds of the parent.
To handle this, I created a content property on these components, which was positioned correctly, and masked the children added to it, so it looked correct. The recommended way of adding children to Panels or Windows was thus:
window.content.addChild(btn);
panel.content.addChild(myRadioBtn);
// etc.
or…
new PushButton(mywindow.content, 10, 10, "click me");
new RadioButton(mypanel.content, 10, 10, "option 1");
// etc.
Unfortunately, this just wasn’t very obvious, and in retrospect, pretty stupid. The proper way would be to override addChild to add the child to the content automatically. So, that’s what I did. I also added a new addRawChild method to each of these components, in case you are doing some custom hackery that really required adding children to the component itself. The following should illustrate the change:
If you were already using content, you won’t see any changes, but you don’t need to use it any more. If you were adding children with addChild and repositioning manually, you’ll probably need to adjust your code to remove those manual adjustments, again, particularly on the Window.
This change also applies to Accordion and Scrollpane, which are based on Panels and Windows.
I also adjusted MinimalConfigurator to account for this, which mainly means I was able to remove the code that checked if the parent had a content property. Now it can always just addChild.
The next change was something else that’s been bugging me for a long time – the PushButton down state. It added an inner drop shadow, but otherwise was not very visible, particularly when using toggle = true. It wasn’t very obvious when a button was toggled in the down state.
So I added a new property on the Style class: Style.BUTTON_DOWN, which is used to redraw the button face when it is down, making it slightly different and much more noticeable. I added values for this to the light and dark themes. If you’ve created custom themes, you can add this to it.
SVN has been updated, and a new SWC and zipped source package have been uploaded to Google Code.
ps. If you used MinimalComps and want to join the conversation, check out the new Google group.
Just added a new feature, as suggested by Karim in the previous post. This saves a hash of all components that have id attributes, and then allows you to find any component by its id. Here’s an example:
package
{
import com.bit101.components.*;
import com.bit101.utils.MinimalConfigurator;
import flash.display.Sprite;
import flash.events.Event;
public class Playground extends Sprite
{
private var config:MinimalConfigurator;
public function Playground()
{
Component.initStage(stage);
var xml:XML = <comps>
<PushButton id="foo" label="hello" event="click:onClick" x="10" y="10"/>
</comps>;
config = new MinimalConfigurator(this);
config.parseXML(xml);
}
public function onClick(event:Event):void
{
var btn:PushButton = config.getCompById("foo") as PushButton;
btn.label = "goodbye";
trace(btn.name); // foo
}
}
}
Also, it maps the id to the component’s native name property, in case that might be useful.
All in all, this allows you to access multiple components without having to make a bunch of public variables.
First, go over to soulwire and check out the great GUI tool created there. One thing that struck me about it was the dark colors on the components. I liked it and set out to create a similar theme by setting dark values on the Style class. Easy enough. But I didn’t want to go through that every time I wanted to create the same look, so I added a new method on Style, called Style.setStyle. This can be “light” or “dark” (Style.LIGHT or Style.DARK). If you don’t set it, or set it to light, you’ll get something like this:
But with one line:
Style.setStyle(Style.DARK);
you can instead have this:
I like it a lot. If you haven’t used the Style class before, It’s just a list of static vars containing colors that are used whenever a component draws itself. Setting a value on Style does NOT update anything that’s already created, until that component re-draws itself, if it ever does. You should set your style values BEFORE you create a single component, and not change them, unless your’e going to take responsibility for redrawing anything. Same goes for this new method. If anyone comes up with any other useful preset styles that look decent, send them over and if I like them, I may add them to Style. And you’ll win a free set of Minimal Components!
The other change I made is to the HBox and VBox. These have really been bugging me because HBoxes always top-align their contents, and VBoxes always left-align their contents. You can set the x position of an element in a VBox and the y position of an element in a HBox, but it was tricky and non-dynamic. Now they each have an alignment property. This can be top, middle, bottom, or none for HBox and left, center, right, or none for VBox. These are static constants on the respective classes as well (HBox.TOP, HBox.MIDDLE, etc.).
The default alignment for both is NONE, meaning that they’ll work the way they always have. Left and top aligned, unless you’ve set the x or y property of an element as just described. If you set the alignment to anything else, it will ignore the current x or y property and align them exactly as you would expect. Here’s a demo:
All these changes are checked into SVN. I have one other thing that I’m working on before I go and release a new SWC though.
This is a concept I’ve had for a while, and last week started to implement it. It wound up being orders of magnitude simpler than I imagined. The core part of it was done days ago. I cleaned things up more recently and finally checked it in and made some examples. As easy as Minimal Comps are to instantiate and set up, once you start getting into more complex layouts with lots of components, you can still wind up with a messy bunch of code. Well now you can create your layouts in an external xml file and leave your classes to handle the logic, as it should be.
The concept is to load an XML file, similar to MXML (you can call it MCML if you need a name for it), that defines what components are to be instantiated, and all their properties. In keeping with the minimal theme, it’s not as hard core as something like MXML. You won’t find data binding or in line ActionScript, or styles, etc. And ALL properties are expressed as attributes, not nested tags. But I am happy to say I worked out some pretty neat things that I originally thought would be more difficult.
One is that a component that has an id attribute will be mapped to a public variable in the parent container if such a variable exists, so you can code things around these created components. For example public var btn:PushButton; will map to < PushButton id="btn"/>.
Second is that you can nest components for those components that can have children. Some components, such as Window, Panel, ScrollPane, expect children to be added to a content property, while others, such as VBox and HBox are added directly. These are both handled correctly. So you can write:
And finally, you can assign events with the syntax: event=”eventName:eventHandler”. For example:
<PushButton label="Click me" event="click:onClick"/>
This assumes that the main container class has a public method called onClick that has the signature compatible with the event that will be dispatched. If it does not, the event handler will not be assigned.
You instantiate the class like so:
var config:MinimalConfigurator = new MinimalConfigurator(this);
You pass in the DisplayObjectContainer that the components will be added to. This is also where it will look for public ids and event handlers.
Then you can parse the xml in one of 3 ways:
// 1. load the xml from a url:
config.loadXML(urlString);
// 2. parse a string containing xml:
config.parseXMLString(xmlString);
// 3. parse an xml object:
config.parseXML(xml);
You can listen for an Event.COMPLETE event which will be fired when the xml is parsed and all the components are instantiated. This is mostly useful when loading the xml from a file, as that is asynchronous, obviously, though the rest are synchronous.
Here, you can try it out for yourself. Write some xml in the text field below and press “Parse XML” and it should create the components above.
Here are a few examples to try, you’ll get the idea soon enough.
Finally, here’s a code example that shows how to use ids and events:
package
{
import com.bit101.components.Component;
import com.bit101.components.Label;
import com.bit101.utils.MinimalConfigurator;
import flash.display.Sprite;
import flash.events.MouseEvent;
public class MinConfigTest extends Sprite
{
public var myLabel:Label;
public function MinConfigTest()
{
Component.initStage(stage);
var xml:XML = <comps>
<Label id="myLabel" x="10" y="10" text="waiting..."/>
<PushButton x="10" y="40" label="click me" event="click:onClick"/>
</comps>;
var config:MinimalConfigurator = new MinimalConfigurator(this);
config.parseXML(xml);
}
public function onClick(event:MouseEvent):void
{
myLabel.text = "You did it";
}
}
}
There’s a public var “myLabel” of type Label. This will be assigned the Label created in the first component tag, with the same id.
Then there’s a public method “onClick”. This is mapped to the button’s click event, so it will be called as an event handler when the button is clicked.
Pretty straightforward I think.
Obviously there are some more complex actions that will still need to be done with code, such as assigning list and combobox items, probably accordions, etc. But this should handle a good bunch of use cases.
The code is currently checked in and there’s a new SWC and zip available for download at the Google Code site. The class is com.bit101.utils.MinimalConfigurator. Enjoy.
Oh, and by the way, even if you aren’t interested in the new class, there are a few fixes in this build which might make it worth updating to anyway: Change Log.