title:: What's new in Qt GUI
summary:: A summary of new features and differences in Qt GUI
categories:: GUI, News

This document is intended for those already familiar with graphical user interface programming in SuperCollider. If you are new to this topic, you are suggested to first read the link::Guides/GUI-Introduction::.

For the purpose of this guide, let's switch to the Qt GUI:

code::
GUI.qt
::

SECTION:: View hierarchy

SUBSECTION:: Every view can be a window

Every view can be displayed as a window on its own, without the use of the Window class. Hence, most methods that are present in Window, are also present in View.

For example, you can display any view without embedding it in a Window or another container view using the link::Classes/View#-front:: method. For this reason, it is valid to omit the 'parent' argument at view construction - any view without a parent can be shown as a window:

code::
(
x = SoundFileView().front;
x.load(ExampleFiles.child);
)
::

SUBSECTION:: Every view can be a container

Every view can contain other views (i.e. act as their parent). For this reason, if you want to group several views together, you can simply use a View as the container:

code::
(
v=View(bounds:300@300);
5.do { |i|
    Slider(v).moveTo(i * 25 + 10, 10);
};
v.front;
)
::


SECTION:: Layout management

The Qt layout system allows you to forget about pixels - it manages the size and position of child views in a parent automatically.

SUBSECTION:: Intrinsic view sizes

You may have noticed in the examples above that, besides omitting the code::parent:: argument, we sometimes omitted the code::bounds:: argument as well, at view construction. This is possible because views have intrinsically defined preferred and minimum sizes. See link::Guides/GUI-Layout-Management#Intrinsic view sizes:: for further explanation.

SUBSECTION:: Layout classes

A collection of layout classes allows you to associate one of them with a parent view and several child views, and it will manage positions and sizes of the children automatically according to their size preferences and constraints. It will also do that dynamically, as you resize the window:

code::
(
w = Window.new(bounds:Rect(100,100,300,80)).layout_(
    VLayout (
        HLayout(
            Button().states_([["Super"]]),
            TextField().string_("Collider")
        ),
        Button().states_([["SuperCollider"]])
    )
).front;
)
::

See the link::Guides/GUI-Layout-Management:: guide for detailed explanation.


SECTION:: Color management

SUBSECTION:: The palette

Qt has the notion of the color palette - a collection of colors from which the views pick when drawing themselves. It is represented by the link::Classes/QPalette:: class, and can be set on a view using link::Classes/View#-palette::.

By default, a window will get the global palette ( link::Classes/QtGUI#*palette:: ), and the palette is inherited by child views from their parent. Thus, changing the parent's palette will also affect its children, unless the palette has been overridden on a particular child. You can easily change the appearance of the whole GUI by changing the global palette.

SUBSECTION:: Predefined palettes

There are two predefined palettes ( link::Classes/QPalette#*light:: and link::Classes/QPalette#*dark:: ), and you can also access the native palette of your operating system ( link::Classes/QPalette#*system:: ):

Try changing the global palette with the code below; if you have the Qt GUI active, this will affect this window as well:

code::
QtGUI.palette = QPalette.dark;

QtGUI.palette = QPalette.light;

QtGUI.palette = QPalette.system;
::

SECTION:: View actions and hooks

SUBSECTION:: Mouse and key event propagation

In addition to key events, mouse events can also propagate to parent views.

Also, the control over event propagation works differently in Qt. See link::Classes/View#Key and mouse event processing:: for detailed explanation.

Moreover, you can make a view transparent for mouse events using link::Classes/View#-acceptsMouse::, which will forward all mouse events to the view under, regardless of whether they are in a parent-child relationship.

code::
(
var win, parent, child, sibling1, sibling2;
win = Window(bounds:Rect(30,30,300,300));
parent = Slider2D(win, win.bounds.moveTo(0,0).insetBy(50,50));

// A StaticText will propagate mouse clicks to parent by default:
child = StaticText(parent, Rect(100,-50,150,150))
    .align_(\bottomLeft)
    .string_("\npropagate\nto\nSlider2D")
    .background_(Color.red.alpha_(0.4));

// This StaticText is not a child of Slider2D so will propagate mouse
// clicks to the window instead:
sibling1 = StaticText(win, Rect(0,0,150,150))
    .align_(\topLeft)
    .string_("propagate\nto\nWindow")
    .background_(Color.cyan.alpha_(0.4));

// This StaticText is not a child of Slider2D, but is made transparent for mouse events:
sibling2 = StaticText(win, Rect(150,150,150,150))
    .align_(\bottomRight)
    .string_("ignore")
    .background_(Color.green.alpha_(0.4))
    .acceptsMouse_(false);
win.view.mouseDownAction = { win.background = Color.red(0.6) };
win.view.mouseUpAction = { win.view.palette = QPalette() };
win.front;
)
::

SUBSECTION:: Extended mouse interaction

Many Qt views already implement some kind of strong::mouse wheel:: interaction. For example, you can scroll a ScrollView, ListView and TreeView using the mouse wheel. You can also change the value of a Slider or a Knob using the mouse wheel. In addition, you can assign an action of your own to the mouse wheel event using link::Classes/View#-mouseWheelAction:::

There's another two handy new mouse actions triggered when the strong::mouse enters or leaves:: the view: link::Classes/View#-mouseEnterAction:: and link::Classes/View#-mouseLeaveAction:::.

code::
(
var val = 1.0;
t=StaticText(bounds:Rect(30,30,100,100))
    .font_(Font(size:25))
    .align_(\center)
    .string_(val.asString)
    .stringColor_(Color.red)
    .background_(Color.black)
    .front;
t.mouseEnterAction = { t.background = Color.white };
t.mouseLeaveAction = { t.background = Color.black };
t.mouseWheelAction = { |v,x,y,mod,dx,dy|
    if(dy > 0) { val = val + 0.05 } { val = val - 0.05 };
    val = val.clip(0,2).round(0.01);
    t.string = val.asString;
    t.stringColor = Color.red(val);
};
)
::



SUBSECTION:: Hooks for position and size change

Since views can be automatically repositioned and resized by link::#Layout management#layouts::, or by the user (if they are windows), it may come handy to make your view respond to these changes using link::Classes/View#-onMove:: and link::Classes/View#-onResize::.

code::
(
var view, update;
update = { |v|
    var bounds = v.bounds;
    v.string = "%@%\n%x%".format(
        bounds.left,
        bounds.top,
        bounds.height,
        bounds.width
    );
};

x = StaticText(bounds:Rect(100,100,200,200)).align_(\center).font_(Font(size:25));
x.onMove = update;
x.onResize = update;
update.(x);

x.front;
)
::

SECTION:: Enhancements

SUBSECTION:: Stethoscope

Qt brings a new implementation of link::Classes/Stethoscope:: that uses emphasis::shared memory:: to allow highly efficient monitoring of buses on any strong::local server::.

All the 'scope' methods of various classes (like Server, Bus, Function, etc.) are wired to this new implementation, so you don't need to worry about instantiating a Stethoscope yourself.

code::
Server.local.scope;
::

SUBSECTION:: SoundFileView

link::Classes/SoundFileView:: has strong::infinite display resolution::. This means you can always zoom into the waveform until you see a single sound frame.

It also offers convenient strong::mouse interaction:: for zooming in and scrolling:
list::
## shift + right-click + mouse-up/down = strong::zoom::
## right-click + mouse-left/right = strong::scroll::
::

code::
(
var x = SoundFileView().front;
x.load(ExampleFiles.child);
)
::

Alternatively to displaying a soundfile, you can allocate an empty amount of display frames, and fill it strong::part by part:: with data to display (see documentation of link::Classes/SoundFileView#-alloc:: and link::Classes/SoundFileView#-set::). This allows, for example, to implement efficient monitoring of recording into a Buffer.

code::
(
var v, s;
v = SoundFileView().front;
s = Signal.sineFill(1000, 1.0/[1,2,3,4]);
v.alloc(5000, samplerate: 500);
3.do { |i| v.set( i * 2000, s * (1/(i+1)) ) };
)
::

SUBSECTION:: EnvelopeView

link::Classes/EnvelopeView:: offers two different strong::display styles::: in addition to traditional style where nodes are drawn as rectangles with labels inside, it can draw nodes as small dots, with labels next to them. See link::Classes/EnvelopeView#-style::.

code::
(
var w, e, m;
e = EnvelopeView()
    .value_([[0,0.4,0.6,1.0],[0,0.7,0.5,0.56]])
    .strings_(["alpha", "beta", "gamma", "delta"])
    .thumbWidth_(60)
    .style_(\dots);
m = PopUpMenu()
    .items_(["Dot Style", "Rect Style"])
    .action_({e.style = m.value});
w = Window()
    .layout_(VLayout(m,e))
    .front;
)
::

You can enforce a strong::strict order of nodes:: on the time axis. In this case, a node can not move to position before the previous node, or after the next node. See link::Classes/EnvelopeView#-keepHorizontalOrder:: and the example below.

You can also control how a strong::selection of nodes:: behaves when it is strong::moved::: it can either keep its form rigidly and block all movement when meeting view edges or other nodes, or it can adjust its form to the obstacles, allowing the movement of those nodes that are not blocked individually. See link::Classes/EnvelopeView#-elasticSelection:: and the following example.

Example: try selecting several nodes (by clicking on them with Shift key pressed) and moving them around, then use the menu to switch the way selection behaves, and repeat:

code::
(
var w, e, m;
e = EnvelopeView()
    .value_([
        [0, 0.1, 0.3, 0.4, 0.55, 0.7, 1.0],
        [0, 1.0, 0.7, 0.3, 0.5, 0.2, 0]
    ])
    .keepHorizontalOrder_(true)
    .elasticSelection_(true)
    .front;
m = PopUpMenu()
    .items_(["Elastic Selection", "Rigid Selection"])
    .action_({e.elasticSelection = m.value == 0});
w = Window()
    .layout_(VLayout(m,e))
    .front;
)
::

SUBSECTION:: ScrollView

link::Classes/ScrollView:: allows to strong::replace the canvas:: that holds its child views with an arbitrary view. This allows great flexibility, including using a link::#Layout management#layout:: to manage the child views. See link::Classes/ScrollView#-canvas:: for explanation, and this link::Classes/ScrollView#examples#example::.


SECTION:: New views

SUBSECTION:: TreeView

link::Classes/TreeView:: is a powerful addition to the group of views that display a strong::set of items:: (including ListView and PopUpMenu). It displays items organized in a strong::hierarchical:: manner, akin to filesystem browsers.

Unlike ListView, where items are only arranged in one column, TreeView may consist of strong::several columns::: each item occupies a row, and may contain one text value for each column. Columns also have strong::labeled headers::.

Moreover, each data field of an item may also strong::contain another view::, giving you a great potential for interactivity with items.

SUBSECTION:: WebView

link::Classes/WebView:: is the core component for strong::web page browsing::. It is also implemented in the Cocoa GUI, but we list it here nonetheless.

It is a view that displays web pages, with web technology support comparable to widespread desktop web browsers.

NOTE:: There may currently be some issues with displaying multimedia content. ::

code::
(
w = WebView(bounds: Window.screenBounds.insetBy(100,40))
    .url_("http://supercollider.sourceforge.net/")
    .front;
)
::
