NotifyBox almost finished.

Atom initialization moved to one place.
Added few ede specific atoms with implementation.
Added sample test.
This commit is contained in:
Sanel Zukan 2007-06-19 09:59:15 +00:00
parent 6529e02a7d
commit c2adf77325
10 changed files with 348 additions and 101 deletions

View File

@ -64,7 +64,8 @@ DesktopIcon::DesktopIcon(GlobalIconSettings* gs, IconSettings* is, int bg) :
if(!settings->icon.empty()) {
const char* nn = settings->icon.c_str();
edelib::String ipath = edelib::IconTheme::get(nn, edelib::ICON_SIZE_MEDIUM);
//edelib::String ipath = edelib::IconTheme::get(nn, edelib::ICON_SIZE_MEDIUM);
edelib::String ipath = edelib::IconTheme::get(nn, edelib::ICON_SIZE_HUGE);
Fl_Image* img = Fl_Shared_Image::get(ipath.c_str());
if(img) {
int img_w = img->w();

View File

@ -5,3 +5,4 @@ LINKLIBS += -L/opt/ede/lib -ledelib -lao -lvorbis -lvorbisfile -lmad -L/usr/loca
C++FLAGS += -Wall -g3 -I/opt/ede/include ;
Main eiconman : eiconman.cpp Utils.cpp Wallpaper.cpp DesktopIcon.cpp NotifyBox.cpp ;
Main test/notify : test/notify.cpp ;

View File

@ -18,14 +18,24 @@
#include <FL/fl_draw.h>
#include <FL/Fl_Group.h>
#define MAX_LABEL_WIDTH 100
#define MAX_LABEL_WIDTH 200
NotifyBox::NotifyBox() : Fl_Box(0, 0, 0, 0) {
#define TIMEOUT (1.0f/60.0f)
#define TIME_SHOWN 3.0f
#define STATE_SHOWING 1
#define STATE_HIDING 2
#define STATE_DONE 3
NotifyBox::NotifyBox(int aw, int ah) : Fl_Box(0, 0, 0, 0) {
area_w = aw;
area_h = ah;
lwidth = lheight = 0;
is_shown = false;
in_animate = false;
box(FL_FLAT_BOX);
color(FL_RED);
state = 0;
box(FL_BORDER_BOX);
color(FL_WHITE);
align(FL_ALIGN_WRAP);
}
@ -36,8 +46,8 @@ NotifyBox::~NotifyBox() {
void NotifyBox::update_label_size(void) {
lwidth = MAX_LABEL_WIDTH;
lheight= 0;
fl_font(labelfont(), labelsize());
fl_font(labelfont(), labelsize());
fl_measure(label(), lwidth, lheight, align());
lwidth += 10;
@ -45,58 +55,81 @@ void NotifyBox::update_label_size(void) {
}
void NotifyBox::show(void) {
update_label_size();
resize(x(), y()-lheight, lwidth, lheight);
if(shown())
return;
EDEBUG("%i %i\n", w(), h());
if(state == STATE_HIDING)
return;
update_label_size();
// center box
int x_pos = (area_w/2) - (lwidth/2);
resize(x_pos, y() - lheight, lwidth, lheight);
Fl_Box::show();
if(!in_animate && !is_shown)
Fl::add_timeout(TIMEOUT, animate_show_cb, this);
state = STATE_SHOWING;
Fl::add_timeout(TIMEOUT, animate_cb, this);
}
void NotifyBox::hide(void) {
if(!in_animate && is_shown)
Fl::add_timeout(TIMEOUT, animate_hide_cb, this);
if(!shown())
return;
// explicitely remove timer when hide() is called
Fl::remove_timeout(animate_cb);
Fl_Box::hide();
is_shown = false;
state = 0;
}
void NotifyBox::animate_show(void) {
if(y() < 0) {
position(x(), y() + 1);
EDEBUG("y: %i\n", y());
redraw();
Fl::repeat_timeout(TIMEOUT, animate_show_cb, this);
in_animate = true;
} else {
Fl::remove_timeout(animate_show_cb);
in_animate = false;
is_shown = true;
// add visibility timeout
Fl::add_timeout(TIME_SHOWN, visible_timeout_cb, this);
}
void NotifyBox::label(const char* l) {
Fl_Box::label(l);
}
void NotifyBox::animate_hide(void) {
if(y() > -lheight) {
position(x(), y() - 1);
redraw();
Desktop::instance()->redraw();
Fl::repeat_timeout(TIMEOUT, animate_hide_cb, this);
in_animate = true;
} else {
Fl::remove_timeout(animate_hide_cb);
Fl::remove_timeout(visible_timeout_cb);
in_animate = false;
is_shown = false;
EDEBUG("!!!!!!!!!!!!!!!!!\n");
void NotifyBox::copy_label(const char* l) {
Fl_Box::copy_label(l);
}
const char* NotifyBox::label(void) {
return Fl_Box::label();
}
void NotifyBox::animate(void) {
if(state == STATE_SHOWING) {
if(y() < 0) {
position(x(), y() + 1);
redraw();
//EDEBUG("SHOWING...\n");
Fl::repeat_timeout(TIMEOUT, animate_cb, this);
state = STATE_SHOWING;
} else {
state = STATE_DONE;
is_shown = true;
// now set visible timeout, which will procede to hiding()
visible_timeout();
}
} else if(state == STATE_HIDING) {
if(y() > -lheight) {
position(x(), y() - 1);
//redraw();
//EDEBUG("%i %i\n", x(), y());
// this will prevent redrawing whole screen
Desktop::instance()->damage(FL_DAMAGE_ALL, x(), 0, w(), h());
//EDEBUG("HIDING...\n");
Fl::repeat_timeout(TIMEOUT, animate_cb, this);
} else {
state = STATE_DONE;
is_shown = false;
}
}
}
void NotifyBox::visible_timeout(void) {
EDEBUG("XXXXXXXXXXXXXXXXX\n");
animate_hide();
state = STATE_HIDING;
Fl::repeat_timeout(TIME_SHOWN, animate_cb, this);
}

View File

@ -15,29 +15,28 @@
#include <FL/Fl_Box.h>
#define TIMEOUT (1.0f/60.0f)
#define TIME_SHOWN 3.0f
class NotifyBox : public Fl_Box {
private:
bool is_shown;
bool in_animate;
int state;
int lwidth, lheight;
int area_w, area_h;
void update_label_size(void);
public:
NotifyBox();
NotifyBox(int aw, int ah);
~NotifyBox();
virtual void show(void);
virtual void hide(void);
bool shown(void) { return is_shown; }
static void animate_show_cb(void* b) { ((NotifyBox*)b)->animate_show(); }
void animate_show(void);
const char* label(void);
void label(const char* l);
void copy_label(const char* l);
static void animate_hide_cb(void* b) { ((NotifyBox*)b)->animate_hide(); }
void animate_hide(void);
static void animate_cb(void* b) { ((NotifyBox*)b)->animate(); }
void animate(void);
static void visible_timeout_cb(void* b) { ((NotifyBox*)b)->visible_timeout(); }
void visible_timeout(void);

View File

@ -15,14 +15,17 @@
#include <FL/x.h>
#include <edelib/Debug.h>
#include <string.h> // strrchr
#include <string.h> // strrchr, strncpy, strlen
Atom NET_WORKAREA = 0;
Atom NET_WM_WINDOW_TYPE = 0;
Atom NET_WM_WINDOW_TYPE_DESKTOP = 0;
Atom NET_NUMBER_OF_DESKTOPS = 0;
Atom NET_CURRENT_DESKTOP = 0;
Atom NET_DESKTOP_NAMES = 0;
Atom _XA_NET_WORKAREA = 0;
Atom _XA_NET_WM_WINDOW_TYPE = 0;
Atom _XA_NET_WM_WINDOW_TYPE_DESKTOP = 0;
Atom _XA_NET_NUMBER_OF_DESKTOPS = 0;
Atom _XA_NET_CURRENT_DESKTOP = 0;
Atom _XA_NET_DESKTOP_NAMES = 0;
Atom _XA_EDE_DESKTOP_NOTIFY = 0;
Atom _XA_EDE_DESKTOP_NOTIFY_COLOR = 0;
int overlay_x = 0;
int overlay_y = 0;
@ -33,19 +36,28 @@ Fl_Window* overlay_drawable = NULL;
char dash_list[] = {1};
void init_atoms(void) {
_XA_NET_WORKAREA = XInternAtom(fl_display, "_NET_WORKAREA", False);
_XA_NET_WM_WINDOW_TYPE = XInternAtom(fl_display, "_NET_WM_WINDOW_TYPE", False);
_XA_NET_WM_WINDOW_TYPE_DESKTOP = XInternAtom(fl_display, "_NET_WM_WINDOW_TYPE_DESKTOP", False);
_XA_NET_NUMBER_OF_DESKTOPS = XInternAtom(fl_display, "_NET_NUMBER_OF_DESKTOPS", False);
_XA_NET_CURRENT_DESKTOP = XInternAtom(fl_display, "_NET_CURRENT_DESKTOP", False);
_XA_NET_DESKTOP_NAMES = XInternAtom(fl_display, "_NET_DESKTOP_NAMES", False);
_XA_EDE_DESKTOP_NOTIFY = XInternAtom(fl_display, "_EDE_DESKTOP_NOTIFY", False);
_XA_EDE_DESKTOP_NOTIFY_COLOR = XInternAtom(fl_display, "_EDE_DESKTOP_NOTIFY_COLOR", False);
}
bool net_get_workarea(int& x, int& y, int& w, int &h) {
Atom real;
if(!NET_WORKAREA)
NET_WORKAREA= XInternAtom(fl_display, "_NET_WORKAREA", 1);
int format;
unsigned long n, extra;
unsigned char* prop;
x = y = w = h = 0;
int status = XGetWindowProperty(fl_display, RootWindow(fl_display, fl_screen),
NET_WORKAREA, 0L, 0x7fffffff, False, XA_CARDINAL, &real, &format, &n, &extra, (unsigned char**)&prop);
_XA_NET_WORKAREA, 0L, 0x7fffffff, False, XA_CARDINAL, &real, &format, &n, &extra, (unsigned char**)&prop);
if(status != Success)
return false;
@ -65,12 +77,6 @@ bool net_get_workarea(int& x, int& y, int& w, int &h) {
}
void net_make_me_desktop(Fl_Window* w) {
if(!NET_WM_WINDOW_TYPE)
NET_WM_WINDOW_TYPE = XInternAtom(fl_display, "_NET_WM_WINDOW_TYPE", False);
if(!NET_WM_WINDOW_TYPE_DESKTOP)
NET_WM_WINDOW_TYPE_DESKTOP= XInternAtom(fl_display, "_NET_WM_WINDOW_TYPE_DESKTOP", False);
/*
* xid() will return zero if window is not shown;
* make sure it is shown
@ -79,26 +85,23 @@ void net_make_me_desktop(Fl_Window* w) {
/*
* Reminder for me (others possible):
* note '&NET_WM_WINDOW_TYPE_DESKTOP' conversion, since gcc will not report warning/error
* if placed 'NET_WM_WINDOW_TYPE_DESKTOP' only.
* note '&_XA_NET_WM_WINDOW_TYPE_DESKTOP' conversion, since gcc will not report warning/error
* if placed '_XA_NET_WM_WINDOW_TYPE_DESKTOP' only.
*
* I lost two hours messing with this ! (gdb is unusefull in X world)
*/
XChangeProperty(fl_display, fl_xid(w), NET_WM_WINDOW_TYPE, XA_ATOM, 32, PropModeReplace,
(unsigned char*)&NET_WM_WINDOW_TYPE_DESKTOP, sizeof(Atom));
XChangeProperty(fl_display, fl_xid(w), _XA_NET_WM_WINDOW_TYPE, XA_ATOM, 32, PropModeReplace,
(unsigned char*)&_XA_NET_WM_WINDOW_TYPE_DESKTOP, sizeof(Atom));
}
int net_get_workspace_count(void) {
if(!NET_NUMBER_OF_DESKTOPS)
NET_NUMBER_OF_DESKTOPS = XInternAtom(fl_display, "_NET_NUMBER_OF_DESKTOPS", False);
Atom real;
int format;
unsigned long n, extra;
unsigned char* prop;
int status = XGetWindowProperty(fl_display, RootWindow(fl_display, fl_screen),
NET_NUMBER_OF_DESKTOPS, 0L, 0x7fffffff, False, XA_CARDINAL, &real, &format, &n, &extra,
_XA_NET_NUMBER_OF_DESKTOPS, 0L, 0x7fffffff, False, XA_CARDINAL, &real, &format, &n, &extra,
(unsigned char**)&prop);
if(status != Success && !prop)
@ -111,15 +114,14 @@ int net_get_workspace_count(void) {
// desktops are starting from 0
int net_get_current_desktop(void) {
if(!NET_CURRENT_DESKTOP)
NET_CURRENT_DESKTOP = XInternAtom(fl_display, "_NET_CURRENT_DESKTOP", False);
Atom real;
int format;
unsigned long n, extra;
unsigned char* prop;
int status = XGetWindowProperty(fl_display, RootWindow(fl_display, fl_screen),
NET_CURRENT_DESKTOP, 0L, 0x7fffffff, False, XA_CARDINAL, &real, &format, &n, &extra, (unsigned char**)&prop);
_XA_NET_CURRENT_DESKTOP, 0L, 0x7fffffff, False, XA_CARDINAL, &real, &format, &n, &extra,
(unsigned char**)&prop);
if(status != Success && !prop)
return -1;
@ -131,12 +133,9 @@ int net_get_current_desktop(void) {
// call on this XFreeStringList(names)
int net_get_workspace_names(char**& names) {
if(!NET_DESKTOP_NAMES)
NET_DESKTOP_NAMES = XInternAtom(fl_display, "_NET_DESKTOP_NAMES", False);
// FIXME: add _NET_SUPPORTING_WM_CHECK and _NET_SUPPORTED ???
XTextProperty wnames;
XGetTextProperty(fl_display, RootWindow(fl_display, fl_screen), &wnames, NET_DESKTOP_NAMES);
XGetTextProperty(fl_display, RootWindow(fl_display, fl_screen), &wnames, _XA_NET_DESKTOP_NAMES);
// if wm does not understainds _NET_DESKTOP_NAMES this is not set
if(!wnames.nitems || !wnames.value)
@ -144,16 +143,59 @@ int net_get_workspace_names(char**& names) {
int nsz;
/*
* FIXME: Here should as alternative Xutf8TextPropertyToTextList since
* many wm's set UTF8_STRING property. Below is XA_STRING and for UTF8_STRING
* will fail.
*/
if(!XTextPropertyToStringList(&wnames, &names, &nsz)) {
XFree(wnames.value);
return 0;
}
//XFreeStringList(names);
XFree(wnames.value);
return nsz;
}
bool ede_get_desktop_notify(char* txt, int txt_len) {
XTextProperty names;
XGetTextProperty(fl_display, RootWindow(fl_display, fl_screen), &names, _XA_EDE_DESKTOP_NOTIFY);
if(!names.nitems || !names.value)
return false;
char** vnames;
int nsz = 0;
if(!XTextPropertyToStringList(&names, &vnames, &nsz)) {
XFree(names.value);
return false;
}
strncpy(txt, vnames[0], txt_len);
XFreeStringList(vnames);
return true;
}
Fl_Color ede_get_desktop_notify_color(void) {
Atom real;
int format;
unsigned long n, extra;
unsigned char* prop;
int status = XGetWindowProperty(fl_display, RootWindow(fl_display, fl_screen),
_XA_EDE_DESKTOP_NOTIFY_COLOR, 0L, 0x7fffffff, False, XA_CARDINAL, &real, &format, &n, &extra,
(unsigned char**)&prop);
int color = FL_WHITE;
if(status != Success && !prop)
return (Fl_Color)color;
color = int(*(long*)prop);
XFree(prop);
return (Fl_Color)color;
}
#if 0
int net_get_workspace_names(char** names) {
Atom nd = XInternAtom(fl_display, "_NET_DESKTOP_NAMES", False);

View File

@ -18,12 +18,29 @@
#include <X11/Xlib.h> // Pixmap
extern Atom _XA_NET_WORKAREA;
extern Atom _XA_NET_WM_WINDOW_TYPE;
extern Atom _XA_NET_WM_WINDOW_TYPE_DESKTOP;
extern Atom _XA_NET_NUMBER_OF_DESKTOPS;
extern Atom _XA_NET_CURRENT_DESKTOP;
extern Atom _XA_NET_DESKTOP_NAMES;
// via XGetTextProperty/XSetTextProperty
extern Atom _XA_EDE_DESKTOP_NOTIFY;
// via XChangeProperty (prop = Fl_Color, sizeof(int))
extern Atom _XA_EDE_DESKTOP_NOTIFY_COLOR;
void init_atoms(void);
int net_get_workspace_count(void);
int net_get_current_desktop(void);
bool net_get_workarea(int& x, int& y, int& w, int &h);
void net_make_me_desktop(Fl_Window* w);
int net_get_workspace_names(char**& names);
bool ede_get_desktop_notify(char* txt, int txt_len);
Fl_Color ede_get_desktop_notify_color(void);
void draw_xoverlay(int x, int y, int w, int h);
void clear_xoverlay(void);
void set_xoverlay_drawable(Fl_Window* win);

View File

@ -40,8 +40,11 @@
#define MIN(x,y) ((x) < (y) ? (x) : (y))
#define MAX(x,y) ((x) > (y) ? (x) : (y))
// which widgets Fl::belowmouse() to skip
#define NOT_SELECTABLE(widget) ((widget == this) || (widget == wallpaper))
/*
* Which widgets Fl::belowmouse() should skip. This should be updated
* when new non-icon child is added to Desktop window.
*/
#define NOT_SELECTABLE(widget) ((widget == this) || (widget == wallpaper) || (widget == notify))
Desktop* Desktop::pinstance = NULL;
bool running = false;
@ -70,10 +73,30 @@ void restart_signal(int signum) {
EDEBUG(ESTRLOC ": Restarting (got signal %d)\n", signum);
}
int desktop_xmessage_handler(int event) { return 0; }
int desktop_xmessage_handler(int event) {
if(fl_xevent->type == PropertyNotify) {
if(fl_xevent->xproperty.atom == _XA_NET_CURRENT_DESKTOP) {
Desktop::instance()->notify_desktop_changed();
return 1;
}
if(fl_xevent->xproperty.atom == _XA_EDE_DESKTOP_NOTIFY) {
char buff[256];
if(ede_get_desktop_notify(buff, 256) && buff[0] != '\0')
Desktop::instance()->notify_box(buff, true);
return 1;
}
if(fl_xevent->xclient.message_type == _XA_EDE_DESKTOP_NOTIFY_COLOR) {
Desktop::instance()->notify_box_color(ede_get_desktop_notify_color());
return 1;
}
}
return 0;
}
Desktop::Desktop() : Fl_Window(0, 0, 100, 100, "") {
selection_x = selection_y = 0;
moving = false;
@ -81,6 +104,7 @@ Desktop::Desktop() : Fl_Window(0, 0, 100, 100, "") {
dsett->color = FL_GRAY;
dsett->wp_use = false;
init_atoms();
update_workarea();
/*
@ -90,8 +114,8 @@ Desktop::Desktop() : Fl_Window(0, 0, 100, 100, "") {
*/
begin();
wallpaper = new Wallpaper(0, 0, w(), h());
//wallpaper->set_tiled("/home/sanel/wallpapers/katesmall.jpg");
notify = new NotifyBox();
wallpaper->set("/home/sanel/wallpapers/katebig.jpg");
notify = new NotifyBox(w(), h());
notify->hide();
end();
@ -508,14 +532,38 @@ DesktopIcon* Desktop::below_mouse(int px, int py) {
}
#endif
void Desktop::notify_box(const char* msg) {
if(notify->shown())
notify->hide();
notify->label(msg);
void Desktop::notify_box(const char* msg, bool copy) {
if(!msg)
return;
if(copy)
notify->copy_label(msg);
else
notify->label(msg);
notify->show();
}
void Desktop::notify_box_color(Fl_Color col) {
notify->color(col);
}
void Desktop::notify_desktop_changed(void) {
int num = net_get_current_desktop();
if(num == -1)
return;
char** names;
int ret = net_get_workspace_names(names);
if(num >= ret) {
XFreeStringList(names);
return;
}
notify_box(names[num], true);
XFreeStringList(names);
}
int Desktop::handle(int event) {
switch(event) {
case FL_FOCUS:
@ -560,12 +608,18 @@ int Desktop::handle(int event) {
return 1;
} else if(SELECTION_SINGLE) {
if(!in_selection(tmp_icon)) {
notify_box(tmp_icon->label());
/*
* for testing
* notify_box(tmp_icon->label());
*/
select_only(tmp_icon);
}
} else if(Fl::event_button() == 3) {
select_only(tmp_icon);
notify_box(tmp_icon->label());
/*
* for testing
* notify_box(tmp_icon->label());
*/
}
/*
@ -641,7 +695,8 @@ int main() {
* XSelectInput will redirect PropertyNotify messages, which
* are listened for
*/
XSelectInput(fl_display, RootWindow(fl_display, fl_screen), PropertyChangeMask | StructureNotifyMask );
XSelectInput(fl_display, RootWindow(fl_display, fl_screen), PropertyChangeMask | StructureNotifyMask | ClientMessage);
Fl::add_handler(desktop_xmessage_handler);
while(running)

View File

@ -118,7 +118,9 @@ class Desktop : public Fl_Window {
void update_workarea(void);
void set_bg_color(int c, bool do_redraw = true);
void notify_box(const char* msg);
void notify_box(const char* msg, bool copy = false);
void notify_box_color(Fl_Color col);
void notify_desktop_changed(void);
Fl_Window* desktop_window(void) { return this; }
};

70
eiconman/test/notify.cpp Normal file
View File

@ -0,0 +1,70 @@
#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Input.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Box.H>
#include <FL/fl_show_colormap.h>
#include <FL/x.h>
#include <stdio.h>
#include <string.h>
Fl_Button* color_button = 0;
Fl_Input* txt = 0;
void color_cb(Fl_Widget*, void*) {
Fl_Color c = fl_show_colormap(color_button->color());
color_button->color(c);
}
void send_cb(Fl_Widget*, void*) {
printf("send: %s color: %i\n", txt->value(), color_button->color());
Atom _XA_EDE_DESKTOP_NOTIFY = XInternAtom(fl_display, "_EDE_DESKTOP_NOTIFY", False);
Atom _XA_EDE_DESKTOP_NOTIFY_COLOR = XInternAtom(fl_display, "_EDE_DESKTOP_NOTIFY_COLOR", False);
//Atom _XA_UTF8_STRING = XInternAtom(fl_display, "UTF8_STRING", false);
// max size
unsigned char txt_send[8192];
int i;
const char* txt_val = txt->value() ? txt->value() : "(none)";
int len = strlen(txt_val);
for(i = 0; i < 8192-2 && i < len; i++)
txt_send[i] = txt_val[i];
txt_send[i] = '\0';
// send text
XChangeProperty(fl_display, RootWindow(fl_display, fl_screen),
_XA_EDE_DESKTOP_NOTIFY, XA_STRING, 8, PropModeReplace,
txt_send, i + 1);
// send color
int col = color_button->color();
XChangeProperty(fl_display, RootWindow(fl_display, fl_screen),
_XA_EDE_DESKTOP_NOTIFY_COLOR, XA_CARDINAL, 32, PropModeReplace,
(unsigned char*)&col, sizeof(int));
}
int main() {
fl_open_display();
Fl_Double_Window* win = new Fl_Double_Window(295, 144, "Notify test");
win->begin();
txt = new Fl_Input(10, 15, 275, 25);
txt->align(FL_ALIGN_TOP_LEFT);
color_button = new Fl_Button(260, 50, 25, 25, "Color");
color_button->align(FL_ALIGN_LEFT);
color_button->callback(color_cb);
Fl_Box* bx = new Fl_Box(10, 50, 164, 85, "Type some text and choose color, then press Send. "
"Desktop should get notified about this.");
bx->align(FL_ALIGN_WRAP);
Fl_Button* send_button = new Fl_Button(195, 110, 90, 25, "&Send");
send_button->callback(send_cb);
win->end();
win->show();
return Fl::run();
}

27
eiconman/test/notify.fl Normal file
View File

@ -0,0 +1,27 @@
# data file for the Fltk User Interface Designer (fluid)
version 1.0108
header_name {.h}
code_name {.cxx}
Function {} {open
} {
Fl_Window {} {
label {Notify test} open
xywh {465 279 295 144} type Double visible
} {
Fl_Input {} {
xywh {10 15 275 25} labelsize 14 align 5 textsize 14
}
Fl_Button {} {
label {&Send}
xywh {195 110 90 25} labelsize 14
}
Fl_Button {} {
label Color
xywh {260 50 25 25} labelsize 14 align 4
}
Fl_Box {} {
label {Type some text and choose color, then press Send. Desktop should get notified about this.} selected
xywh {10 50 164 85} labelsize 14 align 128
}
}
}