forked from qt-creator/qt-creator
QmlDesigner: Remove programmatical derivation of theme colors
The Qt Quick Designer synthesized a few theme colors from existing theme colors. That was in order to prevent the need for 6 theme colors being defined in every theme. It turned out, however, that being able to define these if favorable. Todo in follow-up commit: remove QmlDesigner::Theme::qmlDesigner...() functions, and fix up the qml, accordingly. Change-Id: Ica85df5472cd7b9da8bf215eb6dbdcf608a4fb2d Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
@@ -217,6 +217,12 @@ QmlDesigner_BackgroundColor=ff3c3e40
|
||||
QmlDesigner_HighlightColor=ff46a2da
|
||||
QmlDesigner_FormEditorSelectionColor=ff4ba2ff
|
||||
QmlDesigner_FormEditorForegroundColor=ffffffff
|
||||
QmlDesigner_BackgroundColorDarkAlternate=ff3c3e40
|
||||
QmlDesigner_BackgroundColorDarker=ff151515
|
||||
QmlDesigner_BorderColor=SplitterColor
|
||||
QmlDesigner_ButtonColor=ff505050
|
||||
QmlDesigner_TabDark=shadowBackground
|
||||
QmlDesigner_TabLight=text
|
||||
|
||||
[Flags]
|
||||
ComboBoxDrawTextShadow=false
|
||||
|
@@ -8,10 +8,12 @@ darkText=ff000000
|
||||
textDisabled=88a0a0a0
|
||||
error=ffdf4f4f
|
||||
warning=ffecbc1c
|
||||
shadowBackground=ff232323
|
||||
splitterColor=ff151515
|
||||
|
||||
[Colors]
|
||||
BackgroundColorAlternate=ff3d3d3d
|
||||
BackgroundColorDark=ff232323
|
||||
BackgroundColorDark=shadowBackground
|
||||
BackgroundColorHover=ff515151
|
||||
BackgroundColorNormal=ffffffff
|
||||
BackgroundColorSelected=ff151515
|
||||
@@ -104,7 +106,7 @@ ProgressBarColorFinished=ff5aaa3c
|
||||
ProgressBarColorNormal=b4ffffff
|
||||
ProgressBarTitleColor=ffffffff
|
||||
ProgressBarBackgroundColor=18ffffff
|
||||
SplitterColor=ff151515
|
||||
SplitterColor=splitterColor
|
||||
TextColorDisabled=ff000000
|
||||
TextColorError=ffff0000
|
||||
TextColorHighlight=ffa0a0a4
|
||||
@@ -187,7 +189,13 @@ ClangCodeModel_Warning_TextMarkColor=warning
|
||||
QmlDesigner_BackgroundColor=ff4c4e50
|
||||
QmlDesigner_HighlightColor=ff46a2da
|
||||
QmlDesigner_FormEditorSelectionColor=ff4ba2ff
|
||||
QmlDesigner_FormEditorForegroundColor=ffffffff
|
||||
QmlDesigner_FormEditorForegroundColor=brightText
|
||||
QmlDesigner_BackgroundColorDarkAlternate=ff4c4e50
|
||||
QmlDesigner_BackgroundColorDarker=ff4e4e4e
|
||||
QmlDesigner_BorderColor=splitterColor
|
||||
QmlDesigner_ButtonColor=ff7a7a7a
|
||||
QmlDesigner_TabDark=shadowBackground
|
||||
QmlDesigner_TabLight=brightText
|
||||
|
||||
[Flags]
|
||||
ComboBoxDrawTextShadow=true
|
||||
|
@@ -201,6 +201,12 @@ QmlDesigner_BackgroundColor=ff4c4e50
|
||||
QmlDesigner_HighlightColor=ff3f91c4
|
||||
QmlDesigner_FormEditorSelectionColor=ff4ba2ff
|
||||
QmlDesigner_FormEditorForegroundColor=ffffffff
|
||||
QmlDesigner_BackgroundColorDarkAlternate=ff4c4e50
|
||||
QmlDesigner_BackgroundColorDarker=ff262728
|
||||
QmlDesigner_BorderColor=splitter
|
||||
QmlDesigner_ButtonColor=ff595b5c
|
||||
QmlDesigner_TabDark=shadowBackground
|
||||
QmlDesigner_TabLight=text
|
||||
|
||||
PaletteWindow=normalBackground
|
||||
PaletteWindowText=text
|
||||
|
@@ -199,6 +199,12 @@ QmlDesigner_BackgroundColor=fff8f8f8
|
||||
QmlDesigner_HighlightColor=ff46a2da
|
||||
QmlDesigner_FormEditorSelectionColor=ff4ba2ff
|
||||
QmlDesigner_FormEditorForegroundColor=ffffffff
|
||||
QmlDesigner_BackgroundColorDarkAlternate=fff8f8f8
|
||||
QmlDesigner_BackgroundColorDarker=fff5f5f5
|
||||
QmlDesigner_BorderColor=splitter
|
||||
QmlDesigner_ButtonColor=ffcccccc
|
||||
QmlDesigner_TabDark=ff585858
|
||||
QmlDesigner_TabLight=ffd0d0d0
|
||||
|
||||
[Flags]
|
||||
ComboBoxDrawTextShadow=false
|
||||
|
@@ -156,7 +156,7 @@ Debugger_WatchItem_ValueChanged=ffbf0303
|
||||
Debugger_Breakpoint_TextMarkColor=ffff4040
|
||||
|
||||
Welcome_TextColor=ff000000
|
||||
Welcome_ForegroundPrimaryColor=ff404244
|
||||
Welcome_ForegroundPrimaryColor=shadowBackground
|
||||
Welcome_ForegroundSecondaryColor=ff727476
|
||||
Welcome_BackgroundColor=normalBackground
|
||||
Welcome_ButtonBackgroundColor=normalBackground
|
||||
@@ -197,6 +197,12 @@ QmlDesigner_BackgroundColor=ff4c4e50
|
||||
QmlDesigner_HighlightColor=ff46a2da
|
||||
QmlDesigner_FormEditorSelectionColor=ff4ba2ff
|
||||
QmlDesigner_FormEditorForegroundColor=ffffffff
|
||||
QmlDesigner_BackgroundColorDarkAlternate=ff4c4e50
|
||||
QmlDesigner_BackgroundColorDarker=ff262728
|
||||
QmlDesigner_BorderColor=splitter
|
||||
QmlDesigner_ButtonColor=ff595b5c
|
||||
QmlDesigner_TabDark=shadowBackground
|
||||
QmlDesigner_TabLight=ffffffff
|
||||
|
||||
[Flags]
|
||||
ComboBoxDrawTextShadow=false
|
||||
|
@@ -291,11 +291,17 @@ public:
|
||||
ClangCodeModel_Error_TextMarkColor,
|
||||
ClangCodeModel_Warning_TextMarkColor,
|
||||
|
||||
/* QmlDesigner */
|
||||
/* QmlDesigner Plugin */
|
||||
QmlDesigner_BackgroundColor,
|
||||
QmlDesigner_HighlightColor,
|
||||
QmlDesigner_FormEditorSelectionColor,
|
||||
QmlDesigner_FormEditorForegroundColor
|
||||
QmlDesigner_FormEditorForegroundColor,
|
||||
QmlDesigner_BackgroundColorDarker,
|
||||
QmlDesigner_BackgroundColorDarkAlternate,
|
||||
QmlDesigner_TabLight,
|
||||
QmlDesigner_TabDark,
|
||||
QmlDesigner_ButtonColor,
|
||||
QmlDesigner_BorderColor
|
||||
};
|
||||
|
||||
enum Gradient {
|
||||
|
@@ -34,42 +34,10 @@
|
||||
#include <QPointer>
|
||||
#include <qqml.h>
|
||||
|
||||
namespace {
|
||||
QMap<QString, QColor> createDerivedDesignerColors()
|
||||
{
|
||||
/* Define QmlDesigner colors and remove alpha channels */
|
||||
const QColor backgroundColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_BackgroundColor);
|
||||
const QColor panelStatusBarBackgroundColor = Utils::creatorTheme()->color(Utils::Theme::PanelStatusBarBackgroundColor);
|
||||
const QColor fancyToolButtonSelectedColor = Utils::creatorTheme()->color(Utils::Theme::FancyToolButtonSelectedColor);
|
||||
const QColor darkerBackground = Utils::StyleHelper::alphaBlendedColors(panelStatusBarBackgroundColor, fancyToolButtonSelectedColor);
|
||||
const QColor fancyToolButtonHoverColor = Utils::creatorTheme()->color(Utils::Theme::FancyToolButtonHoverColor);
|
||||
const QColor buttonColor = Utils::StyleHelper::alphaBlendedColors(panelStatusBarBackgroundColor, fancyToolButtonHoverColor);
|
||||
|
||||
QColor tabLight = Utils::creatorTheme()->color(Utils::Theme::PanelTextColorLight);
|
||||
QColor tabDark = Utils::creatorTheme()->color(Utils::Theme::BackgroundColorDark);
|
||||
|
||||
/* hack for light themes */
|
||||
/* The selected tab is always supposed to be lighter */
|
||||
if (tabDark.value() > tabLight.value()) {
|
||||
tabLight = tabDark.darker(110);
|
||||
tabDark = tabDark.darker(260);
|
||||
}
|
||||
return {{"QmlDesignerBackgroundColorDarker", darkerBackground},
|
||||
{"QmlDesignerBackgroundColorDarkAlternate", backgroundColor},
|
||||
{"QmlDesignerTabLight", tabLight},
|
||||
{"QmlDesignerTabDark", tabDark},
|
||||
{"QmlDesignerButtonColor", buttonColor},
|
||||
{"QmlDesignerBorderColor", Utils::creatorTheme()->color(Utils::Theme::SplitterColor)}
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
Theme::Theme(Utils::Theme *originTheme, QObject *parent)
|
||||
: Utils::Theme(originTheme, parent)
|
||||
, m_derivedColors(createDerivedDesignerColors())
|
||||
{
|
||||
}
|
||||
|
||||
@@ -82,7 +50,7 @@ QColor Theme::evaluateColorAtThemeInstance(const QString &themeColorName)
|
||||
return color(static_cast<Utils::Theme::Color>(i)).name();
|
||||
}
|
||||
|
||||
qWarning() << "error while evaluate " << themeColorName;
|
||||
qWarning() << Q_FUNC_INFO << "error while evaluating" << themeColorName;
|
||||
return QColor();
|
||||
}
|
||||
|
||||
@@ -95,7 +63,6 @@ Theme *Theme::instance()
|
||||
|
||||
QString Theme::replaceCssColors(const QString &input)
|
||||
{
|
||||
const QMap<QString, QColor> &map = instance()->m_derivedColors;
|
||||
QRegExp rx("creatorTheme\\.(\\w+)");
|
||||
|
||||
int pos = 0;
|
||||
@@ -103,11 +70,7 @@ QString Theme::replaceCssColors(const QString &input)
|
||||
|
||||
while ((pos = rx.indexIn(input, pos)) != -1) {
|
||||
const QString themeColorName = rx.cap(1);
|
||||
QColor color;
|
||||
if (map.contains(themeColorName))
|
||||
color = map.value(themeColorName);
|
||||
else
|
||||
color = instance()->evaluateColorAtThemeInstance(themeColorName);
|
||||
const QColor color = instance()->evaluateColorAtThemeInstance(themeColorName);
|
||||
output.replace("creatorTheme." + rx.cap(1), color.name());
|
||||
pos += rx.matchedLength();
|
||||
}
|
||||
@@ -138,32 +101,32 @@ QPixmap Theme::getPixmap(const QString &id)
|
||||
|
||||
QColor Theme::qmlDesignerBackgroundColorDarker() const
|
||||
{
|
||||
return m_derivedColors.value("QmlDesignerBackgroundColorDarker");
|
||||
return getColor(QmlDesigner_BackgroundColorDarker);
|
||||
}
|
||||
|
||||
QColor Theme::qmlDesignerBackgroundColorDarkAlternate() const
|
||||
{
|
||||
return m_derivedColors.value("QmlDesignerBackgroundColorDarkAlternate");
|
||||
return getColor(QmlDesigner_BackgroundColorDarkAlternate);
|
||||
}
|
||||
|
||||
QColor Theme::qmlDesignerTabLight() const
|
||||
{
|
||||
return m_derivedColors.value("QmlDesignerTabLight");
|
||||
return getColor(QmlDesigner_TabLight);
|
||||
}
|
||||
|
||||
QColor Theme::qmlDesignerTabDark() const
|
||||
{
|
||||
return m_derivedColors.value("QmlDesignerTabDark");
|
||||
return getColor(QmlDesigner_TabDark);
|
||||
}
|
||||
|
||||
QColor Theme::qmlDesignerButtonColor() const
|
||||
{
|
||||
return m_derivedColors.value("QmlDesignerButtonColor");
|
||||
return getColor(QmlDesigner_ButtonColor);
|
||||
}
|
||||
|
||||
QColor Theme::qmlDesignerBorderColor() const
|
||||
{
|
||||
return m_derivedColors.value("QmlDesignerBorderColor");
|
||||
return getColor(QmlDesigner_BorderColor);
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -57,8 +57,6 @@ public:
|
||||
private:
|
||||
Theme(Utils::Theme *originTheme, QObject *parent);
|
||||
QColor evaluateColorAtThemeInstance(const QString &themeColorName);
|
||||
|
||||
QMap<QString, QColor> m_derivedColors;
|
||||
};
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -1,19 +1,19 @@
|
||||
QPushButton, QComboBox[editable="false"],
|
||||
QComboBox[editable="true"] {
|
||||
background-color: creatorTheme.QmlDesignerBackgroundColorDarkAlternate;
|
||||
background-color: creatorTheme.QmlDesigner_BackgroundColorDarkAlternate;
|
||||
border-width: 3;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
QPushButton:hover, QComboBox[editable="false"]:hover,
|
||||
QComboBox[editable="true"]:hover, QMenuBar::item:hover {
|
||||
background-color: creatorTheme.QmlDesignerBackgroundColorDarkAlternate;
|
||||
background-color: creatorTheme.QmlDesigner_BackgroundColorDarkAlternate;
|
||||
border-width: 3;
|
||||
}
|
||||
|
||||
QPushButton:pressed, QComboBox[editable="false"]:on,
|
||||
QComboBox[editable="true"]:on, QMenuBar::item:on {
|
||||
background-color: creatorTheme.QmlDesignerBackgroundColorDarkAlternate;
|
||||
background-color: creatorTheme.QmlDesigner_BackgroundColorDarkAlternate;
|
||||
border-width: 3;
|
||||
}
|
||||
|
||||
|
@@ -1,8 +1,8 @@
|
||||
QWidget#centralTabBar {
|
||||
background-color: creatorTheme.QmlDesignerBackgroundColorDarkAlternate;
|
||||
background-color: creatorTheme.QmlDesigner_BackgroundColorDarkAlternate;
|
||||
}
|
||||
QWidget#tabBarBackground {
|
||||
background-color: creatorTheme.QmlDesignerBackgroundColorDarkAlternate;
|
||||
background-color: creatorTheme.QmlDesigner_BackgroundColorDarkAlternate;
|
||||
}
|
||||
|
||||
QTabBar#centralTabBar::tab:first {
|
||||
@@ -10,8 +10,8 @@ QTabBar#centralTabBar::tab:first {
|
||||
height: 0px;
|
||||
}
|
||||
QTabBar#centralTabBar::tab {
|
||||
background-color: creatorTheme.QmlDesignerTabDark;
|
||||
color: creatorTheme.QmlDesignerTabLight;
|
||||
background-color: creatorTheme.QmlDesigner_TabDark;
|
||||
color: creatorTheme.QmlDesigner_TabLight;
|
||||
|
||||
width: 11px;
|
||||
height: 100px;
|
||||
@@ -24,12 +24,12 @@ QTabBar#centralTabBar::tab {
|
||||
}
|
||||
|
||||
QTabBar#centralTabBar::tab:selected {
|
||||
background-color: creatorTheme.QmlDesignerTabLight;
|
||||
color: creatorTheme.QmlDesignerTabDark;
|
||||
background-color: creatorTheme.QmlDesigner_TabLight;
|
||||
color: creatorTheme.QmlDesigner_TabDark;
|
||||
}
|
||||
|
||||
QToolButton#centralTabBar {
|
||||
background-color: creatorTheme.QmlDesignerBackgroundColorDarkAlternate;
|
||||
background-color: creatorTheme.QmlDesigner_BackgroundColorDarkAlternate;
|
||||
width: 08px;
|
||||
height: 16px;
|
||||
border: none;
|
||||
|
@@ -1,23 +1,23 @@
|
||||
|
||||
QStackedWidget {
|
||||
border: 0px;
|
||||
background-color: creatorTheme.QmlDesignerBackgroundColorDarkAlternate;
|
||||
background-color: creatorTheme.QmlDesigner_BackgroundColorDarkAlternate;
|
||||
}
|
||||
|
||||
QGraphicsView {
|
||||
padding: 2px;
|
||||
border: 0px;
|
||||
background-color: creatorTheme.QmlDesignerBackgroundColorDarkAlternate;
|
||||
background-color: creatorTheme.QmlDesigner_BackgroundColorDarkAlternate;
|
||||
}
|
||||
|
||||
QGraphicsView:focus {
|
||||
background-color: creatorTheme.QmlDesignerBackgroundColorDarkAlternate;
|
||||
background-color: creatorTheme.QmlDesigner_BackgroundColorDarkAlternate;
|
||||
}
|
||||
|
||||
QLineEdit
|
||||
{
|
||||
color: creatorTheme.PanelTextColorLight;
|
||||
border: 1px solid creatorTheme.QmlDesignerBackgroundColorDarker;
|
||||
border: 1px solid creatorTheme.QmlDesigner_BackgroundColorDarker;
|
||||
padding: 2px 8px;
|
||||
margin: 2px;
|
||||
background-color: creatorTheme.FancyToolButtonSelectedColor;
|
||||
|
@@ -11,7 +11,7 @@ QScrollBar:horizontal {
|
||||
}
|
||||
|
||||
QScrollBar::handle {
|
||||
background-color: creatorTheme.QmlDesignerButtonColor;
|
||||
background-color: creatorTheme.QmlDesigner_ButtonColor;
|
||||
}
|
||||
|
||||
QScrollBar::handle:vertical {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
QFrame {
|
||||
border: 2px;
|
||||
background-color: creatorTheme.QmlDesignerBackgroundColorDarkAlternate;
|
||||
background-color: creatorTheme.QmlDesigner_BackgroundColorDarkAlternate;
|
||||
}
|
||||
|
||||
QLabel {
|
||||
@@ -13,24 +13,24 @@ QLabel {
|
||||
|
||||
QScrollArea {
|
||||
border: 0px;
|
||||
background-color: creatorTheme.QmlDesignerBackgroundColorDarkAlternate;
|
||||
background-color: creatorTheme.QmlDesigner_BackgroundColorDarkAlternate;
|
||||
}
|
||||
|
||||
QStackedWidget {
|
||||
border: 0px;
|
||||
background-color: creatorTheme.QmlDesignerBackgroundColorDarkAlternate;
|
||||
background-color: creatorTheme.QmlDesigner_BackgroundColorDarkAlternate;
|
||||
}
|
||||
|
||||
QGraphicsView {
|
||||
border: 0px;
|
||||
background-color: creatorTheme.QmlDesignerBackgroundColorDarkAlternate;
|
||||
background-color: creatorTheme.QmlDesigner_BackgroundColorDarkAlternate;
|
||||
}
|
||||
|
||||
QLineEdit#itemLibrarySearchInput
|
||||
{
|
||||
color: creatorTheme.PanelTextColorLight;
|
||||
font-size: 11px;
|
||||
border: 1px solid creatorTheme.QmlDesignerBackgroundColorDarker;
|
||||
border: 1px solid creatorTheme.QmlDesigner_BackgroundColorDarker;
|
||||
border-width: 1;
|
||||
max-height: 20px;
|
||||
min-height: 20px;
|
||||
@@ -61,12 +61,12 @@ QLineEdit#itemLibrarySearchInput
|
||||
|
||||
QTabWidget {
|
||||
border: 0px;
|
||||
background-color: creatorTheme.QmlDesignerBackgroundColorDarkAlternate;
|
||||
background-color: creatorTheme.QmlDesigner_BackgroundColorDarkAlternate;
|
||||
}
|
||||
|
||||
QTabWidget::pane { /* The tab widget frame */
|
||||
border: 0px;
|
||||
background-color: creatorTheme.QmlDesignerBackgroundColorDarkAlternate;
|
||||
background-color: creatorTheme.QmlDesigner_BackgroundColorDarkAlternate;
|
||||
}
|
||||
|
||||
QTabBar::tab {
|
||||
@@ -74,8 +74,8 @@ QTabBar::tab {
|
||||
height: 22px;
|
||||
|
||||
border-image: none;
|
||||
background-color: creatorTheme.QmlDesignerTabDark;
|
||||
color: creatorTheme.QmlDesignerTabLight;
|
||||
background-color: creatorTheme.QmlDesigner_TabDark;
|
||||
color: creatorTheme.QmlDesigner_TabLight;
|
||||
|
||||
margin-top: 0x;
|
||||
margin-bottom: 0px;
|
||||
@@ -87,10 +87,10 @@ QTabBar::tab {
|
||||
|
||||
QTabBar::tab:selected {
|
||||
border: none; /* no border for a flat push button */
|
||||
background-color: creatorTheme.QmlDesignerTabLight;
|
||||
color: creatorTheme.QmlDesignerTabDark;
|
||||
background-color: creatorTheme.QmlDesigner_TabLight;
|
||||
color: creatorTheme.QmlDesigner_TabDark;
|
||||
}
|
||||
|
||||
QWidget#itemLibrarySearchInputSpacer {
|
||||
background-color: creatorTheme.QmlDesignerTabLight;
|
||||
background-color: creatorTheme.QmlDesigner_TabLight;
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
QFrame
|
||||
{
|
||||
background-color: creatorTheme.QmlDesignerBackgroundColorDarkAlternate;
|
||||
background-color: creatorTheme.QmlDesigner_BackgroundColorDarkAlternate;
|
||||
color: creatorTheme.PanelTextColorLight;
|
||||
font-size: 11px;
|
||||
border-radius: 0px;
|
||||
@@ -14,8 +14,8 @@ QTableView {
|
||||
}
|
||||
|
||||
QTabBar QToolButton {
|
||||
background-color: creatorTheme.QmlDesignerBackgroundColorDarkAlternate;
|
||||
border: 1px solid creatorTheme.QmlDesignerBackgroundColorDarker;
|
||||
background-color: creatorTheme.QmlDesigner_BackgroundColorDarkAlternate;
|
||||
border: 1px solid creatorTheme.QmlDesigner_BackgroundColorDarker;
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ QTableView {
|
||||
}
|
||||
|
||||
QWidget#widgetSpacer {
|
||||
background-color: creatorTheme.QmlDesignerTabLight;
|
||||
background-color: creatorTheme.QmlDesigner_TabLight;
|
||||
}
|
||||
|
||||
QStackedWidget {
|
||||
@@ -62,8 +62,8 @@ QTabBar::tab:selected {
|
||||
border-image: none;
|
||||
image: none;
|
||||
|
||||
background-color: creatorTheme.QmlDesignerTabLight;
|
||||
color: creatorTheme.QmlDesignerTabDark;
|
||||
background-color: creatorTheme.QmlDesigner_TabLight;
|
||||
color: creatorTheme.QmlDesigner_TabDark;
|
||||
}
|
||||
|
||||
|
||||
@@ -76,8 +76,8 @@ QTabBar::tab {
|
||||
margin-right: 0px;
|
||||
font: bold;
|
||||
font-size: 11px;
|
||||
background-color: creatorTheme.QmlDesignerTabDark;
|
||||
color: creatorTheme.QmlDesignerTabLight;
|
||||
background-color: creatorTheme.QmlDesigner_TabDark;
|
||||
color: creatorTheme.QmlDesigner_TabLight;
|
||||
}
|
||||
|
||||
QSpinBox
|
||||
|
Reference in New Issue
Block a user