Bug 406330 - "Winstripe and Gnomestripe look bad in RTL mode" (Fix gnomestripe rtl) [p=ventnor.bugzilla@yahoo.com.au (Michael Ventnor) r=Ryan a=blocking-firefox3+]

This commit is contained in:
reed@reedloden.com 2007-12-21 03:17:01 -08:00
parent 18f73c2030
commit 622482ea5f
13 changed files with 590 additions and 22 deletions

View File

@ -53,13 +53,11 @@
/* Root View */
#placesView {
border-top: 1px solid ThreeDDarkShadow;
background-color: Window;
}
#splitter {
border: 0px;
width: 3px;
min-width: 3px;
}
/* Place List, Place Content */
@ -68,14 +66,12 @@
}
#placesList {
-moz-appearance: none;
margin: 0px;
border: none;
padding: 0;
}
#placeContent {
-moz-appearance: none;
border: 0px;
}
@ -251,28 +247,11 @@
}
/**** expanders ****/
.expander-up,
.expander-down {
min-width: 0;
}
.expander-up {
list-style-image: url("chrome://global/skin/arrow/arrow-up.gif");
}
.expander-down {
list-style-image: url("chrome://global/skin/arrow/arrow-dn.gif");
}
.expander-down:hover:active {
list-style-image: url("chrome://global/skin/arrow/arrow-dn-hov.gif");
}
.expander-up:hover:active {
list-style-image: url("chrome://global/skin/arrow/arrow-up-hov.gif");
}
/**** menuitem stock icons ****/
menuitem:not([type]) {
-moz-binding: url("chrome://global/content/bindings/menu.xml#menuitem-iconic");

View File

@ -479,6 +479,7 @@ CSS_KEY(toolbargripper, toolbargripper)
CSS_KEY(dualbutton, dualbutton)
CSS_KEY(dualbutton-dropdown, dualbutton_dropdown)
CSS_KEY(separator, separator)
CSS_KEY(splitter, splitter)
CSS_KEY(statusbar, statusbar)
CSS_KEY(statusbarpanel, statusbarpanel)
CSS_KEY(resizerpanel, resizerpanel)

View File

@ -190,6 +190,7 @@ const PRInt32 nsCSSProps::kAppearanceKTable[] = {
eCSSKeyword_dualbutton, NS_THEME_TOOLBAR_DUAL_BUTTON,
eCSSKeyword_dualbutton_dropdown, NS_THEME_TOOLBAR_DUAL_BUTTON_DROPDOWN,
eCSSKeyword_separator, NS_THEME_TOOLBAR_SEPARATOR,
eCSSKeyword_splitter, NS_THEME_SPLITTER,
eCSSKeyword_statusbar, NS_THEME_STATUSBAR,
eCSSKeyword_statusbarpanel, NS_THEME_STATUSBAR_PANEL,
eCSSKeyword_resizerpanel, NS_THEME_STATUSBAR_RESIZER_PANEL,

View File

@ -7,6 +7,7 @@ classic.jar:
+ skin/classic/global/toolbarbutton.css
+ skin/classic/global/button.css
+ skin/classic/global/checkbox.css
+ skin/classic/global/splitter.css
+ skin/classic/global/radio.css
+ skin/classic/global/global.css
+ skin/classic/global/tree.css

View File

@ -0,0 +1,63 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998-2001
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Joe Hewitt (hewitt@netscape.com)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/* ===== splitter.css ===================================================
== Styles used by the XUL splitter element.
======================================================================= */
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
/* ::::: splitter (vertical) ::::: */
splitter {
-moz-appearance: splitter;
cursor: ew-resize;
}
/* ::::: splitter (horizontal) ::::: */
splitter[orient="vertical"] {
cursor: ns-resize;
}
/* ::::: splitter grippy ::::: */
grippy {
display: none;
}

View File

@ -0,0 +1,398 @@
#extensionsBox {
margin: 10px 10px 0px 10px;
min-width:1px;
}
#extensionsView {
border: 2px solid;
-moz-border-top-colors: ThreeDShadow ThreeDDarkShadow;
-moz-border-right-colors: ThreeDHighlight ThreeDLightShadow;
-moz-border-bottom-colors: ThreeDHighlight ThreeDLightShadow;
-moz-border-left-colors: ThreeDShadow ThreeDDarkShadow;
margin: 0 !important;
}
#resizerBox {
margin-top: -12px;
visibility: hidden;
}
#resizerBox > resizer {
height: 10px;
max-height: 10px;
visibility: visible;
}
/* Command Bar */
#commandBarBottom {
margin: 10px 10px 5px 10px;
min-width: 1px;
}
#commandBarBottom button {
margin: 0;
list-style-image: url("chrome://mozapps/skin/extensions/actionbuttons.png");
-moz-margin-end: 5px;
}
#commandBarBottom button .button-icon {
margin-top: 0px;
margin-bottom: 0px;
-moz-margin-start: 0px;
-moz-margin-end: 5px;
}
#installFileButton, #installUpdatesAllButton {
-moz-image-region: rect(0px, 84px, 21px, 63px);
}
#installFileButton[disabled="true"],
#installUpdatesAllButton[disabled="true"] {
-moz-image-region: rect(21px, 84px, 42px, 63px);
}
#checkUpdatesAllButton, #showUpdateInfoButton, #hideUpdateInfoButton {
-moz-image-region: rect(0px, 63px, 21px, 42px);
}
#checkUpdatesAllButton[disabled="true"] {
-moz-image-region: rect(21px, 63px, 42px, 42px);
}
#restartAppButton {
-moz-image-region: rect(0px, 42px, 21px, 21px);
}
#restartAppButton[disabled="true"] {
-moz-image-region: rect(21px, 42px, 42px, 21px);
}
/* these skip/continue icons don't make sense. it's just a placeholder. */
#skipDialogButton {
-moz-image-region: rect(0px, 42px, 21px, 21px);
}
#skipDialogButton[disabled="true"] {
-moz-image-region: rect(21px, 42px, 42px, 21px);
}
#continueDialogButton {
-moz-image-region: rect(0px, 42px, 21px, 21px);
}
#continueDialogButton[disabled="true"] {
-moz-image-region: rect(21px, 42px, 42px, 21px);
}
/* List Items */
richlistitem {
padding-top: 6px;
padding-bottom: 6px;
-moz-padding-start: 7px;
-moz-padding-end: 7px;
min-height: 25px;
border-bottom: 1px dotted #C0C0C0;
}
richlistitem[isDisabled="true"] .addonIcon {
opacity: 0.3;
}
richlistitem[isDisabled="true"] {
color: GrayText;
}
richlistitem[selected="true"] {
background-color: -moz-Dialog;
color: -moz-DialogText;
}
#extensionsView:focus > richlistitem[selected="true"] {
background-image: url("chrome://mozapps/skin/extensions/itemEnabledFader.png");
background-color: Highlight;
color: HighlightText;
}
#extensionsView:focus > richlistitem[selected="true"][isDisabled="true"] {
background-image: url("chrome://mozapps/skin/extensions/itemDisabledFader.png");
}
.descriptionWrap {
margin-bottom: 2px;
}
richlistitem[selected="true"]:not([opType]) .descriptionCrop {
display: none;
}
.addonName {
font-weight: bold;
}
.previewText {
font-size: larger;
font-weight: bold;
color: ThreeDShadow;
text-align: center;
}
#themePreviewArea {
-moz-appearance: listbox;
border: 2px solid;
-moz-border-top-colors: ThreeDShadow ThreeDDarkShadow;
-moz-border-right-colors: ThreeDHighlight ThreeDLightShadow;
-moz-border-bottom-colors: ThreeDHighlight ThreeDLightShadow;
-moz-border-left-colors: ThreeDShadow ThreeDDarkShadow;
background-color: -moz-Field;
color: -moz-FieldText;
overflow: auto;
width: 0px;
}
#themeSplitter {
border-width: 0;
}
.addonIcon {
-moz-margin-end: 2px;
width: 32px;
max-width: 32px;
height: 32px;
max-height: 32px;
}
.updateBadge,
.notifyBadge {
width: 16px;
height: 16px;
margin-bottom: -3px;
list-style-image: url("chrome://mozapps/skin/extensions/notifyBadges.png");
}
.updateBadge {
-moz-margin-end: -2px;
-moz-image-region: rect(0px 16px 16px 0px);
}
.notifyBadge {
-moz-margin-start: -2px;
-moz-image-region: rect(0px 48px 16px 32px);
}
.updateBadge,
.updateAvailableBox,
.notifyBadge {
display: none;
}
richlistitem[availableUpdateURL][updateable="true"] .updateBadge,
richlistitem[availableUpdateURL][updateable="true"] .updateAvailableBox,
richlistitem[compatible="false"] .notifyBadge,
richlistitem[providesUpdatesSecurely="false"] .notifyBadge,
richlistitem[blocklisted="true"] .notifyBadge,
richlistitem[satisfiesDependencies="false"] .notifyBadge {
display: -moz-box;
}
/* Selected Add-on buttons
See content/extensions.css to hide / display buttons */
.selectedButtons {
margin-top: 4px;
min-height: 2.1em;
}
.selectedButtons > button {
margin-top: 0;
margin-bottom: 0;
}
.enableButton,
.disableButton,
.optionsButton,
.useThemeButton {
-moz-margin-end: 0;
}
.enableButton, .disableButton,
.uninstallButton, .cancelUninstallButton {
-moz-margin-start: 5px;
}
/* Selected Add-on status messages and images */
richlistitem[compatible="true"] .incompatibleBox,
richlistitem[providesUpdatesSecurely="true"] .insecureUpdateBox,
richlistitem[satisfiesDependencies="true"] .needsDependenciesBox,
richlistitem[blocklisted="false"] .blocklistedBox,
richlistitem[opType="needs-uninstall"] .blocklistedBox,
richlistitem[opType="needs-uninstall"] .incompatibleBox,
richlistitem[opType="needs-uninstall"] .needsDependenciesBox,
richlistitem[opType="needs-uninstall"] .blocklistedBox {
display: none;
}
richlistitem[loading="true"] .updateBadge {
display: -moz-box;
width: 16px;
height: 16px;
margin-bottom: -3px;
-moz-margin-end: -2px;
list-style-image: url("chrome://global/skin/throbber/Throbber-small.gif");
-moz-image-region: auto;
}
.addonThrobber {
-moz-margin-start: 5px;
width: 16px;
height: 16px;
list-style-image: url("chrome://global/skin/throbber/Throbber-small.gif");
}
.selectedStatusMsgs > hbox {
margin-top: 2px;
margin-bottom: 2px;
}
.selectedStatusMsgs label {
font-weight: bold;
}
.selectedStatusMsgs label.text-link {
font-weight: normal;
border: none;
}
richlistitem[opType="needs-uninstall"] .notifyBadge {
display: none;
}
#progressBox {
padding: 5px 5px 5px 5px;
}
#progressBox > hbox {
-moz-box-align: center;
}
/* View buttons */
.viewSelector {
border-bottom: 2px groove ThreeDFace;
margin: 0px;
-moz-padding-start: 10px;
background-color: -moz-Field;
color: -moz-FieldText;
}
#viewGroup radio {
-moz-appearance: none;
margin: 0px 1px 0px 1px;
padding: 1px 3px 1px 3px;
min-width: 4.5em;
list-style-image: url("chrome://mozapps/skin/extensions/viewButtons.png");
}
#viewGroup radio:hover {
background-color: #E0E8F6;
color: black;
}
#viewGroup radio[selected="true"] {
background-color: #C1D2EE;
color: black;
}
.viewButtonIcon {
width: 32px;
height: 32px;
}
radio#extensions-view {
-moz-image-region: rect(0px, 32px, 32px, 0px)
}
radio#extensions-view:hover, radio#extensions-view[selected="true"] {
-moz-image-region: rect(32px, 32px, 64px, 0px)
}
radio#themes-view {
-moz-image-region: rect(0px, 64px, 32px, 32px)
}
radio#themes-view:hover, radio#themes-view[selected="true"] {
-moz-image-region: rect(32px, 64px, 64px, 32px)
}
radio#locales-view {
-moz-image-region: rect(0px, 96px, 32px, 64px)
}
radio#locales-view:hover, radio#locales-view[selected="true"] {
-moz-image-region: rect(32px, 96px, 64px, 64px)
}
radio#plugins-view {
-moz-image-region: rect(0px, 128px, 32px, 96px)
}
radio#plugins-view:hover, radio#plugins-view[selected="true"] {
-moz-image-region: rect(32px, 128px, 64px, 96px)
}
radio#updates-view {
-moz-image-region: rect(0px, 160px, 32px, 128px)
}
radio#updates-view:hover, radio#updates-view[selected="true"] {
-moz-image-region: rect(32px, 160px, 64px, 128px)
}
radio#installs-view {
-moz-image-region: rect(0px, 192px, 32px, 160px)
}
radio#installs-view:hover, radio#installs-view[selected="true"] {
-moz-image-region: rect(32px, 192px, 64px, 160px)
}
/* Update view checkbox */
.includeUpdate {
-moz-user-focus: none;
}
richlistitem[selected="true"] .includeUpdate {
-moz-user-focus: normal;
}
#infoDisplay {
overflow-y: auto;
}
#infoDisplay body {
padding: 5px;
}
#infoDisplay h1,
#infoDisplay h2,
#infoDisplay h3 {
text-align: left;
font-weight: bold;
margin: 0 0 0.7em 0;
}
#infoDisplay h1 {
font-size: 150%;
}
#infoDisplay h2 {
font-size: 125%;
}
#infoDisplay h3 {
font-size: 100%;
}
#infoDisplay ol,
#infoDisplay ul {
margin: 0 0 0.7em 0;
}
#infoDisplay li {
text-align: left;
}
#infoDisplay p {
text-align: justify;
margin: 0 0 0.7em 0;
}

View File

@ -2,6 +2,7 @@ classic.jar:
#ifndef MINIMO
+ skin/classic/mozapps/update/update.png (update/update.png)
+ skin/classic/mozapps/downloads/downloads.css (downloads/downloads.css)
+ skin/classic/mozapps/extensions/extensions.css (extensions/extensions.css)
#ifdef MOZ_PLACES
+ skin/classic/mozapps/places/tagContainerIcon.png (places/tagContainerIcon.png)
#endif

View File

@ -86,6 +86,8 @@ static GtkWidget* gTreeHeaderSortArrowWidget;
static GtkWidget* gExpanderWidget;
static GtkWidget* gToolbarSeparatorWidget;
static GtkWidget* gMenuSeparatorWidget;
static GtkWidget* gHPanedWidget;
static GtkWidget* gVPanedWidget;
static GtkShadowType gMenuBarShadowType;
static GtkShadowType gToolbarShadowType;
@ -136,6 +138,26 @@ ensure_button_widget()
return MOZ_GTK_SUCCESS;
}
static gint
ensure_hpaned_widget()
{
if (!gHPanedWidget) {
gHPanedWidget = gtk_hpaned_new();
setup_widget_prototype(gHPanedWidget);
}
return MOZ_GTK_SUCCESS;
}
static gint
ensure_vpaned_widget()
{
if (!gVPanedWidget) {
gVPanedWidget = gtk_vpaned_new();
setup_widget_prototype(gVPanedWidget);
}
return MOZ_GTK_SUCCESS;
}
static gint
ensure_toggle_button_widget()
{
@ -644,6 +666,19 @@ moz_gtk_option_menu_get_metrics(gboolean* interior_focus,
return MOZ_GTK_SUCCESS;
}
gint
moz_gtk_splitter_get_metrics(gint orientation, gint* size)
{
if (orientation == GTK_ORIENTATION_HORIZONTAL) {
ensure_hpaned_widget();
gtk_widget_style_get(gHPanedWidget, "handle_size", size, NULL);
} else {
ensure_vpaned_widget();
gtk_widget_style_get(gVPanedWidget, "handle_size", size, NULL);
}
return MOZ_GTK_SUCCESS;
}
static gint
moz_gtk_toggle_paint(GdkDrawable* drawable, GdkRectangle* rect,
GdkRectangle* cliprect, GtkWidgetState* state,
@ -1027,6 +1062,36 @@ moz_gtk_gripper_paint(GdkDrawable* drawable, GdkRectangle* rect,
return MOZ_GTK_SUCCESS;
}
static gint
moz_gtk_hpaned_paint(GdkDrawable* drawable, GdkRectangle* rect,
GdkRectangle* cliprect, GtkWidgetState* state)
{
GtkStateType hpaned_state = ConvertGtkState(state);
ensure_hpaned_widget();
gtk_paint_handle(gHPanedWidget->style, drawable, hpaned_state,
GTK_SHADOW_NONE, cliprect, gHPanedWidget, "paned",
rect->x, rect->y, rect->width, rect->height,
GTK_ORIENTATION_VERTICAL);
return MOZ_GTK_SUCCESS;
}
static gint
moz_gtk_vpaned_paint(GdkDrawable* drawable, GdkRectangle* rect,
GdkRectangle* cliprect, GtkWidgetState* state)
{
GtkStateType vpaned_state = ConvertGtkState(state);
ensure_vpaned_widget();
gtk_paint_handle(gVPanedWidget->style, drawable, vpaned_state,
GTK_SHADOW_NONE, cliprect, gVPanedWidget, "paned",
rect->x, rect->y, rect->width, rect->height,
GTK_ORIENTATION_HORIZONTAL);
return MOZ_GTK_SUCCESS;
}
static gint
moz_gtk_entry_paint(GdkDrawable* drawable, GdkRectangle* rect,
GdkRectangle* cliprect, GtkWidgetState* state,
@ -2115,6 +2180,8 @@ moz_gtk_get_widget_border(GtkThemeWidgetType widget, gint* left, gint* top,
w = gCheckMenuItemWidget;
break;
/* These widgets have no borders, since they are not containers. */
case MOZ_GTK_SPLITTER_HORIZONTAL:
case MOZ_GTK_SPLITTER_VERTICAL:
case MOZ_GTK_CHECKBUTTON:
case MOZ_GTK_RADIOBUTTON:
case MOZ_GTK_SCROLLBAR_BUTTON:
@ -2415,6 +2482,12 @@ moz_gtk_widget_paint(GtkThemeWidgetType widget, GdkDrawable* drawable,
(widget == MOZ_GTK_RADIOMENUITEM),
direction);
break;
case MOZ_GTK_SPLITTER_HORIZONTAL:
return moz_gtk_vpaned_paint(drawable, rect, cliprect, state);
break;
case MOZ_GTK_SPLITTER_VERTICAL:
return moz_gtk_hpaned_paint(drawable, rect, cliprect, state);
break;
case MOZ_GTK_WINDOW:
return moz_gtk_window_paint(drawable, rect, cliprect, direction);
break;
@ -2472,6 +2545,8 @@ moz_gtk_shutdown()
gExpanderWidget = NULL;
gToolbarSeparatorWidget = NULL;
gMenuSeparatorWidget = NULL;
gHPanedWidget = NULL;
gVPanedWidget = NULL;
is_initialized = FALSE;

View File

@ -183,6 +183,10 @@ typedef enum {
MOZ_GTK_CHECKMENUITEM,
MOZ_GTK_RADIOMENUITEM,
MOZ_GTK_MENUSEPARATOR,
/* Paints a GtkVPaned separator */
MOZ_GTK_SPLITTER_HORIZONTAL,
/* Paints a GtkHPaned separator */
MOZ_GTK_SPLITTER_VERTICAL,
/* Paints the background of a window, dialog or page. */
MOZ_GTK_WINDOW
} GtkThemeWidgetType;
@ -335,6 +339,15 @@ gint moz_gtk_get_expander_size(gint* size);
*/
gint moz_gtk_get_menu_separator_height(gint* size);
/**
* Get the desired size of a splitter
* orientation: [IN] GTK_ORIENTATION_HORIZONTAL or GTK_ORIENTATION_VERTICAL
* size: [OUT] width or height of the splitter handle
*
* returns: MOZ_GTK_SUCCESS if there was no error, an error code otherwise
*/
gint moz_gtk_splitter_get_metrics(gint orientation, gint* size);
/**
* Retrieve an actual GTK scrollbar widget for style analysis. It will not
* be modified.

View File

@ -510,6 +510,12 @@ nsNativeThemeGTK::GetGtkWidgetAndState(PRUint8 aWidgetType, nsIFrame* aFrame,
aGtkWidgetType = MOZ_GTK_TAB;
}
break;
case NS_THEME_SPLITTER:
if (IsHorizontal(aFrame))
aGtkWidgetType = MOZ_GTK_SPLITTER_VERTICAL;
else
aGtkWidgetType = MOZ_GTK_SPLITTER_HORIZONTAL;
break;
case NS_THEME_MENUBAR:
aGtkWidgetType = MOZ_GTK_MENUBAR;
break;
@ -884,6 +890,21 @@ nsNativeThemeGTK::GetMinimumWidgetSize(nsIRenderingContext* aContext,
*aIsOverridable = PR_FALSE;
}
break;
case NS_THEME_SPLITTER:
{
gint metrics;
if (IsHorizontal(aFrame)) {
moz_gtk_splitter_get_metrics(GTK_ORIENTATION_HORIZONTAL, &metrics);
aResult->width = metrics;
aResult->height = 0;
} else {
moz_gtk_splitter_get_metrics(GTK_ORIENTATION_VERTICAL, &metrics);
aResult->width = 0;
aResult->height = metrics;
}
*aIsOverridable = PR_FALSE;
}
break;
case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
{
@ -1153,6 +1174,7 @@ nsNativeThemeGTK::ThemeSupportsWidget(nsPresContext* aPresContext,
case NS_THEME_MENUSEPARATOR:
case NS_THEME_CHECKMENUITEM:
case NS_THEME_RADIOMENUITEM:
case NS_THEME_SPLITTER:
case NS_THEME_WINDOW:
case NS_THEME_DIALOG:
case NS_THEME_DROPDOWN:

View File

@ -218,6 +218,17 @@ nsNativeTheme::IsLastTab(nsIFrame* aFrame)
return aFrame->GetContent()->HasAttr(kNameSpaceID_None, nsWidgetAtoms::lasttab);
}
PRBool
nsNativeTheme::IsHorizontal(nsIFrame* aFrame)
{
if (!aFrame)
return PR_FALSE;
return !aFrame->GetContent()->AttrValueIs(kNameSpaceID_None, nsWidgetAtoms::orient,
nsWidgetAtoms::vertical,
eCaseMatters);
}
// progressbar:
PRBool
nsNativeTheme::IsIndeterminateProgress(nsIFrame* aFrame)

View File

@ -115,6 +115,8 @@ class nsNativeTheme
PRBool IsBottomTab(nsIFrame* aFrame);
PRBool IsFirstTab(nsIFrame* aFrame);
PRBool IsLastTab(nsIFrame* aFrame);
PRBool IsHorizontal(nsIFrame* aFrame);
// progressbar:
PRBool IsIndeterminateProgress(nsIFrame* aFrame);

View File

@ -78,6 +78,7 @@ WIDGET_ATOM(Forward, "Forward")
WIDGET_ATOM(Home, "Home")
WIDGET_ATOM(hidden, "hidden")
WIDGET_ATOM(horizontal, "horizontal")
WIDGET_ATOM(vertical, "vertical")
WIDGET_ATOM(id, "id")
WIDGET_ATOM(image, "image")
WIDGET_ATOM(input, "input")