-
Notifications
You must be signed in to change notification settings - Fork 32
Making a Resizeable UI
The CliptIt and Distort plug-in examples are the reference examples to copy in order to make your UI resizeable.
However, it is strongly recommended to read this guide though, as some of the details are important.
Here is a step by step guide to make your plug-in UI resizeable.
The constructor of FlatBackgroundGUI
and PBRBackgroundGUI
, instead of just a fixed pixel size, can take a SizeConstraints
that describes valid user size (and the initial size).
Several options exist to get one SizeConstraints
.
-
makeSizeConstraintsDiscrete
(recommended)This allows several discrete size, preserving aspect-ratio. Width and height of the plugin are scaled by the proposed ratio.
static immutable float[7] ratios = [0.5f, 0.75f, 1.0f, 1.25f, 1.5f, 1.75f, 2.0f]; super( makeSizeConstraintsDiscrete(640, 480, ratios) );
-
makeSizeConstraintsDiscreteXY
(recommended)This allows separate discrete ratios for horizontal and vertical size. Width and height of the plugin are scaled by the two selected ratios. Not preserving aspect-ratio.
static immutable float[7] ratiosX = [0.5f, 0.75f, 1.0f, 1.25f, 1.5f, 1.75f, 2.0f]; static immutable float[1] ratiosY = [1.0f]; super( makeSizeConstraintsDiscreteXY(640, 480, ratiosX, ratiosY) ); // plugin can only be resized alongside X dimension, with 7 possible widths.
-
makeSizeConstraintsFixed
This doesn't allow resizing. It is the default that happens when no
SizeConstraints
is provided.super( makeSizeConstraintsFixed(640, 480) ); // equivalent to the former `super(640, 480);`
-
makeSizeConstraintsContinuous
This allows a continuous range of scale factors, preserving aspect-ratio.
super( makeSizeConstraintsContinuous(640, 480, 0.5f, 2.0f) );
-
makeSizeConstraintsBounds
This allows a range of possible width, and a range of possible height, not preserving aspect-ratio.
int defaultWidth = 640; int minWidth = defaultWidth / 2; int maxWidth = defaultWidth * 2; int defaultHeight = 640; int minHeight = defaultHeight / 2; int maxHeight = defaultHeight * 2; super( makeSizeConstraintsBounds(minWidth, maxWidth, minHeight, maxHeight, defaultWidth, defaultHeight) );
This is a better fit for plugins that can adapt their content to different aspect ratios.
The UIElement.reflow()
method is the perfect place to set the .position
of children widgets.
This will in turn call their reflow()
method).
Key fact:
reflow()
should be relatively quick to avoid visual artifacts.
Inside reflow()
or onDrawRaw
/onDrawPBR
, you can access the widget rectangle with the position
getter.
This is an example of a reflow()
method at top-level:
override void reflow()
{
// necessary for PBRBackgroundGUI and FlatBackgroundGUI
super.reflow();
// Get width and height of widget
int W = position.width;
int H = position.height;
// Get a scale factor for how the UI is resized. Note that this example is for fixed aspect-ratio.
float S = W / cast(float)(context.getDefaultUIWidth());
// this position a widget, scaled, with rounded integer coordinates
_myWidget.position = rectangle(517, 176, 46, 46).scaleByFactor(S);
// whenever there is a property expressed in pixels, it has to be set in `reflow()`
_myWidget.fontSizeInPixels = 16 * S;
// save S factor for later use (eg: line width in onDrawRaw)
_S = S;
}
Note: reflow()
is guaranteed to be called before drawing, at at each time position/size of a widget changes.
- Put in your UI member declaration:
private:
UIWindowResizer _resizerCorner;
- Put in your UI constructor:
addChild(_resizerCorner= mallocNew!UIWindowResizer(context()));
- Put in your UI
reflow()
:
int W = position.width;
int H = position.height;
_resizerCorner.position = rectangle(W-30, H-30, 30, 30);
Without this corner resizer, the plugin will only be resizeable in VST3. You can also make your own resizer widget.
To use UIWindowResizer
, you will need dplug:flat-widgets
as dependency.
Obviously, as FlatBackgroundGUI
and PBRBackgroundGUI
now scales images to the selected user size, you will have some work to do to upscale existing the graphics to a larger size, in order to avoid blurriness. Using the largest size when visualizing may help.
For a brand new UI:
- Design the UI at the largest size.
- JPEG should probably be saved in 4:2:0 to save binary size.
For legacy graphics:
-
waifu2x
can be used to upscale the more photographics parts of the UI: http://waifu2x.udp.jp/ - Burn-in text usually has to be redone at a larger scale, or turned into a programmatic label.
- Logos, filmstrips controls... must be upscaled too.
At runtime, these resource images are now downscaled by widgets.
This can be done in two different ways:
-
If the image is small (like a logo), you can use the global resizer in
reflow()
.
override void reflow()
{
_imageResized.size(position.width, position.height);
ImageResizer* resizer = context.globalImageResizer; // only use this in reflow()
resizer.resizeImageGeneric(_imageVanilla.toRef(), _imageResized.toRef());
}
-
If the image is large, then it's not recommended to resize it in
reflow()
. Asreflow()
should be fast to execute, it can be better to delay the resize to first-draw.- You can use a temporary
ImageResizer
inonDrawXXX
(seeFlatBackgroundGUI
for reference) - or eventually resize only the parts necessary (see
UIFilmstripKnob
for reference).
You cannot use the global UI resizer outside of
reflow()
, asonDrawXXX
methods are called concurrently when possible. - You can use a temporary
Warning: watch out for this subtle trap with dirtyRect
and scaled rectangles.
When you compute a sub-rectangle position with scaleByFactor
, with the widget positionned itself with scaleByFactor
, it's easy to pass to setDirty
a rect that is outside _position
. Clip the dirty rect to the width and height of the widget.