# HG changeset patch
# User John Donoghue <john.donoghue@ieee.org>
# Date 1663719074 14400
#      Tue Sep 20 20:11:14 2022 -0400
# Node ID 4c333659fb48893feb463844741488ebf002cf31
# Parent  95fa3182f00ce043808a96a2fcbca584c998f34b
Add uilabel functionality

* libgui/graphics/Label.cc, libgui/graphics/Label.h,
  scripts/gui/uilabel.m: new files

* doc/interpreter/genpropdoc.m (genpropdoc): add uilabel to objnames

* doc/interpreter/gui.txi: add uilabel docstring

* doc/interpreter/module.mk: add rule for plot-uilabelproperties.texi

* doc/interpreter/octave.texi, doc/interpreter/plot.txi: add doc refs
  for uilabel properties

* libgui/graphics/QtHandlesUtils.cc: add computeFont<uilabel>

* libgui/graphics/module.mk: add ref to Label.h and Label.cc

* libgui/graphics/qt-graphics-toolkit.cc: add processing for uilabel

* libinterp/corefcn/gl-render.cc: add skip for uilabel

* libinterp/corefcn/graphics.cc:
  (default_uilabel_position): new function
  (make_graphics_object_from_type): add processing for uilabel
  (property_list::set): add processing for uilabel
  (property_list::lookup): add processing for uilabel
  (uilabel::properties::*): new functions
  (root_figure::init_factory_properties): add map for uilabel
  (__go_uilabel__): new function

* libinterp/corefcn/graphics.in.h: add uilabel class

* scripts/gui/module.mk: add ref to uilabel.m

* NEWS: add note on uilabel

diff -r 95fa3182f00c -r 4c333659fb48 doc/interpreter/genpropdoc.m
--- a/doc/interpreter/genpropdoc.m	Mon Sep 19 13:14:16 2022 +0200
+++ b/doc/interpreter/genpropdoc.m	Tue Sep 20 20:11:14 2022 -0400
@@ -41,7 +41,7 @@
   objnames = {"root", "figure", "axes", "legend", ...
               "image", "light", "line", "patch", "scatter", "surface", ...
               "text", "uibuttongroup", "uicontextmenu", "uicontrol", ...
-              "uipanel", "uimenu", "uipushtool", "uitable", ...
+              "uilabel", "uipanel", "uimenu", "uipushtool", "uitable", ...
               "uitoggletool", "uitoolbar"
              };
 
diff -r 95fa3182f00c -r 4c333659fb48 doc/interpreter/gui.txi
--- a/doc/interpreter/gui.txi	Mon Sep 19 13:14:16 2022 +0200
+++ b/doc/interpreter/gui.txi	Tue Sep 20 20:11:14 2022 -0400
@@ -116,6 +116,8 @@
 
 @DOCSTRING(uicontrol)
 
+@DOCSTRING(uilabel)
+
 @DOCSTRING(uitable)
 
 @DOCSTRING(uimenu)
diff -r 95fa3182f00c -r 4c333659fb48 doc/interpreter/module.mk
--- a/doc/interpreter/module.mk	Mon Sep 19 13:14:16 2022 +0200
+++ b/doc/interpreter/module.mk	Tue Sep 20 20:11:14 2022 -0400
@@ -15,6 +15,7 @@
   %reldir%/plot-uimenuproperties.texi \
   %reldir%/plot-uibuttongroupproperties.texi \
   %reldir%/plot-uicontextmenuproperties.texi \
+  %reldir%/plot-uilabelproperties.texi \
   %reldir%/plot-uipanelproperties.texi \
   %reldir%/plot-uicontrolproperties.texi \
   %reldir%/plot-uitableproperties.texi \
@@ -74,6 +75,9 @@
 %reldir%/plot-uicontextmenuproperties.texi: %reldir%/genpropdoc.m $(GRAPHICS_PROPS_SRC)
 	$(AM_V_GEN)$(call gen-propdoc-texi,uicontextmenu)
 
+%reldir%/plot-uilabelproperties.texi: %reldir%/genpropdoc.m $(GRAPHICS_PROPS_SRC)
+	$(AM_V_GEN)$(call gen-propdoc-texi,uilabel)
+
 %reldir%/plot-uipanelproperties.texi: %reldir%/genpropdoc.m $(GRAPHICS_PROPS_SRC)
 	$(AM_V_GEN)$(call gen-propdoc-texi,uipanel)
 
diff -r 95fa3182f00c -r 4c333659fb48 doc/interpreter/octave.texi
--- a/doc/interpreter/octave.texi	Mon Sep 19 13:14:16 2022 +0200
+++ b/doc/interpreter/octave.texi	Tue Sep 20 20:11:14 2022 -0400
@@ -594,6 +594,7 @@
 * Uimenu Properties::
 * Uibuttongroup Properties::
 * Uicontextmenu Properties::
+* Uilabel Properties::
 * Uipanel Properties::
 * Uicontrol Properties::
 * Uitable Properties::
diff -r 95fa3182f00c -r 4c333659fb48 doc/interpreter/plot.txi
--- a/doc/interpreter/plot.txi	Mon Sep 19 13:14:16 2022 +0200
+++ b/doc/interpreter/plot.txi	Tue Sep 20 20:11:14 2022 -0400
@@ -1609,6 +1609,7 @@
 * Uimenu Properties::
 * Uibuttongroup Properties::
 * Uicontextmenu Properties::
+* Uilabel Properties::
 * Uipanel Properties::
 * Uicontrol Properties::
 * Uitable Properties::
@@ -1727,6 +1728,13 @@
 @include plot-uicontextmenuproperties.texi
 
 
+@node Uilabel Properties
+@subsubsection Uilabel Properties
+@prindex @sortas{@ Uilabel Properties} Uilabel Properties
+
+@include plot-uilabelproperties.texi
+
+
 @node Uipanel Properties
 @subsubsection Uipanel Properties
 @prindex @sortas{@ Uipanel Properties} Uipanel Properties
diff -r 95fa3182f00c -r 4c333659fb48 etc/NEWS.8.md
--- a/etc/NEWS.8.md	Mon Sep 19 13:14:16 2022 +0200
+++ b/etc/NEWS.8.md	Tue Sep 20 20:11:14 2022 -0400
@@ -74,6 +74,7 @@
 * `pagectranspose`
 * `pagetranspose`
 * `uifigure`
+* `uilabel`
 
 ### Deprecated functions, properties, and operators
 
diff -r 95fa3182f00c -r 4c333659fb48 libgui/graphics/Label.cc
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libgui/graphics/Label.cc	Tue Sep 20 20:11:14 2022 -0400
@@ -0,0 +1,200 @@
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2022 The Octave Project Developers
+//
+// See the file COPYRIGHT.md in the top-level directory of this
+// distribution or <https://octave.org/copyright/>.
+//
+// This file is part of Octave.
+//
+// Octave is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Octave is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Octave; see the file COPYING.  If not, see
+// <https://www.gnu.org/licenses/>.
+//
+////////////////////////////////////////////////////////////////////////
+
+#if defined (HAVE_CONFIG_H)
+#  include "config.h"
+#endif
+
+#include <QLabel>
+
+#include "Container.h"
+#include "Label.h"
+#include "QtHandlesUtils.h"
+
+#include "octave-qobject.h"
+
+namespace octave
+{
+  static void
+  updatePalette (const uilabel::properties& up, QLabel *w)
+  {
+    QPalette p = w->palette ();
+    QColor bg;
+    if (up.backgroundcolor_is("none"))
+	bg = QColor(Qt::transparent);
+    else
+        bg = Utils::fromRgb (up.get_backgroundcolor_rgb ());
+
+    p.setColor (QPalette::Active, QPalette::Window, bg);
+    p.setColor (QPalette::Inactive, QPalette::Window, bg);
+    p.setColor (QPalette::Active, QPalette::WindowText,
+                Utils::fromRgb (up.get_fontcolor_rgb ()));
+    p.setColor (QPalette::Inactive, QPalette::WindowText,
+                Utils::fromRgb (up.get_fontcolor_rgb ()));
+
+    w->setPalette (p);
+  }
+
+  static void
+  updateText (const uilabel::properties& up, QLabel *w)
+  {
+
+    if (up.interpreter_is("html"))
+        w->setTextFormat (Qt::RichText);
+    else
+        w->setTextFormat (Qt::PlainText);
+
+    w->setText (Utils::fromStringVector (up.get_text_vector ()).join ("\n"));
+  }
+
+
+  Label *
+  Label::create (octave::base_qobject& oct_qobj,
+                       octave::interpreter& interp, const graphics_object& go)
+  {
+    Object *parent = parentObject (interp, go);
+
+    if (parent)
+      {
+        Container *container = parent->innerContainer ();
+
+        if (container)
+          return new Label (oct_qobj, interp, go, new QLabel (container));
+      }
+
+    return nullptr;
+  }
+
+  Label::Label (octave::base_qobject& oct_qobj,
+                            octave::interpreter& interp,
+                            const graphics_object& go, QLabel *label)
+    : Object (oct_qobj, interp, go, label)
+  {
+    qObject ()->setObjectName ("UILabel");
+
+    uilabel::properties& up = properties<uilabel> ();
+
+    label->setAutoFillBackground (true);
+
+    label->setWordWrap (false);
+    label->setAlignment (Utils::fromHVAlign (up.get_horizontalalignment (),
+                         up.get_verticalalignment ()));
+
+    updateText (up, label);
+
+    Matrix bb = up.get_boundingbox (false);
+    label->setGeometry (octave::math::round (bb(0)), octave::math::round (bb(1)),
+                    octave::math::round (bb(2)), octave::math::round (bb(3)));
+    label->setFont (Utils::computeFont<uilabel> (up, bb(3)));
+
+    updatePalette (up, label);
+
+    if (up.enable_is ("inactive"))
+      label->blockSignals (true);
+    else
+      label->setEnabled (up.enable_is ("on"));
+
+    label->setToolTip (Utils::fromStdString (up.get_tooltip ()));
+    label->setVisible (up.is_visible ());
+  }
+
+  Label::~Label (void)
+  { }
+
+  void
+  Label::redraw (void)
+  {
+    update (uilabel::properties::ID_POSITION);
+  }
+
+  void
+  Label::update (int pId)
+  {
+    uilabel::properties& up = properties<uilabel> ();
+    QLabel *label = qWidget<QLabel> ();
+
+    switch (pId)
+      {
+      case uilabel::properties::ID_TEXT:
+        updateText (up, label);
+        break;
+
+      case uilabel::properties::ID_HORIZONTALALIGNMENT:
+      case uilabel::properties::ID_VERTICALALIGNMENT:
+        label->setAlignment (Utils::fromHVAlign (up.get_horizontalalignment (),
+                             up.get_verticalalignment ()));
+        break;
+
+      case uilabel::properties::ID_POSITION:
+        {
+          Matrix bb = up.get_boundingbox (false);
+          label->setGeometry (octave::math::round (bb(0)), octave::math::round (bb(1)),
+                          octave::math::round (bb(2)), octave::math::round (bb(3)));
+        }
+        break;
+
+      case uilabel::properties::ID_FONTNAME:
+      case uilabel::properties::ID_FONTSIZE:
+      case uilabel::properties::ID_FONTWEIGHT:
+      case uilabel::properties::ID_FONTANGLE:
+        label->setFont (Utils::computeFont<uilabel> (up));
+        break;
+
+      case uilabel::properties::ID_BACKGROUNDCOLOR:
+      case uilabel::properties::ID_FONTCOLOR:
+        updatePalette (up, label);
+        break;
+
+      case uilabel::properties::ID_ENABLE:
+        if (up.enable_is ("inactive"))
+          {
+            label->blockSignals (true);
+            label->setEnabled (true);
+          }
+        else
+          {
+            label->blockSignals (false);
+            label->setEnabled (up.enable_is ("on"));
+          }
+        break;
+
+      case uilabel::properties::ID_WORDWRAP:
+	label->setWordWrap(up.wordwrap_is ("on"));
+        break;
+
+      case uilabel::properties::ID_TOOLTIP:
+        label->setToolTip (Utils::fromStdString (up.get_tooltip ()));
+        break;
+
+      case base_properties::ID_VISIBLE:
+        label->setVisible (up.is_visible ());
+        break;
+
+      default:
+        break;
+      }
+  }
+
+}
diff -r 95fa3182f00c -r 4c333659fb48 libgui/graphics/Label.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libgui/graphics/Label.h	Tue Sep 20 20:11:14 2022 -0400
@@ -0,0 +1,60 @@
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2022 The Octave Project Developers
+//
+// See the file COPYRIGHT.md in the top-level directory of this
+// distribution or <https://octave.org/copyright/>.
+//
+// This file is part of Octave.
+//
+// Octave is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Octave is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Octave; see the file COPYING.  If not, see
+// <https://www.gnu.org/licenses/>.
+//
+////////////////////////////////////////////////////////////////////////
+
+#if ! defined (octave_Label_h)
+#define octave_Label_h 1
+
+#include "BaseControl.h"
+
+class QLabel;
+
+namespace octave
+{
+  class base_qobject;
+  class interpreter;
+}
+
+namespace octave
+{
+  class Label : public Object
+  {
+  public:
+    Label (octave::base_qobject& oct_qobj, octave::interpreter& interp,
+                 const graphics_object& go, QLabel *label);
+    ~Label (void);
+
+    static Label *
+    create (octave::base_qobject& oct_qobj, octave::interpreter& interp,
+            const graphics_object& go);
+
+    Container * innerContainer (void) { return nullptr; }
+  protected:
+    void update (int pId);
+    void redraw (void);
+  };
+
+}
+
+#endif
diff -r 95fa3182f00c -r 4c333659fb48 libgui/graphics/QtHandlesUtils.cc
--- a/libgui/graphics/QtHandlesUtils.cc	Mon Sep 19 13:14:16 2022 +0200
+++ b/libgui/graphics/QtHandlesUtils.cc	Tue Sep 20 20:11:14 2022 -0400
@@ -135,6 +135,9 @@
     template QFont computeFont<uicontrol> (const uicontrol::properties& props,
                                            int height);
 
+    template QFont computeFont<uilabel> (const uilabel::properties& props,
+                                           int height);
+
     template QFont computeFont<uipanel> (const uipanel::properties& props,
                                          int height);
 
diff -r 95fa3182f00c -r 4c333659fb48 libgui/graphics/module.mk
--- a/libgui/graphics/module.mk	Mon Sep 19 13:14:16 2022 +0200
+++ b/libgui/graphics/module.mk	Tue Sep 20 20:11:14 2022 -0400
@@ -54,6 +54,7 @@
   %reldir%/GLCanvas.h \
   %reldir%/GenericEventNotify.h \
   %reldir%/KeyMap.h \
+  %reldir%/Label.h \
   %reldir%/ListBoxControl.h \
   %reldir%/Logger.h \
   %reldir%/Menu.h \
@@ -93,6 +94,7 @@
   %reldir%/FigureWindow.cc \
   %reldir%/GLCanvas.cc \
   %reldir%/KeyMap.cc \
+  %reldir%/Label.cc \
   %reldir%/ListBoxControl.cc \
   %reldir%/Logger.cc \
   %reldir%/Menu.cc \
diff -r 95fa3182f00c -r 4c333659fb48 libgui/graphics/qt-graphics-toolkit.cc
--- a/libgui/graphics/qt-graphics-toolkit.cc	Mon Sep 19 13:14:16 2022 +0200
+++ b/libgui/graphics/qt-graphics-toolkit.cc	Tue Sep 20 20:11:14 2022 -0400
@@ -38,6 +38,7 @@
 #include "ContextMenu.h"
 #include "EditControl.h"
 #include "Figure.h"
+#include "Label.h"
 #include "ListBoxControl.h"
 #include "Logger.h"
 #include "Menu.h"
@@ -84,6 +85,7 @@
     else if (go.isa ("uicontrol")
              || go.isa ("uipanel")
              || go.isa ("uibuttongroup")
+             || go.isa ("uilabel")
              || go.isa ("uimenu")
              || go.isa ("uicontextmenu")
              || go.isa ("uitable")
@@ -124,6 +126,7 @@
         || (go.isa ("uicontrol") && go.get ("style").string_value () != "frame")
         || go.isa ("uipanel")
         || go.isa ("uibuttongroup")
+        || go.isa ("uilabel")
         || go.isa ("uimenu")
         || go.isa ("uicontextmenu")
         || go.isa ("uitable")
@@ -164,6 +167,7 @@
         || pId == uicontrol::properties::ID___OBJECT__
         || pId == uipanel::properties::ID___OBJECT__
         || pId == uibuttongroup::properties::ID___OBJECT__
+        || pId == uilabel::properties::ID___OBJECT__
         || pId == uimenu::properties::ID___OBJECT__
         || pId == uicontextmenu::properties::ID___OBJECT__
         || pId == uitable::properties::ID___OBJECT__
@@ -320,6 +324,47 @@
               }
           }
       }
+    else if (go.isa ("uilabel"))
+      {
+        octave_value str = go.get ("text");
+        if (! str.isempty ())
+          {
+            const uilabel::properties& up =
+              dynamic_cast<const uilabel::properties&> (go.get_properties ());
+            Matrix bb = up.get_boundingbox (false);
+            QFont font = Utils::computeFont<uilabel> (up, bb(3));
+            QFontMetrics fm (font);
+
+            QString s;
+            QSize sz;
+
+            if (str.is_string ())
+              {
+                s = QString::fromStdString (str.string_value ());
+                sz = fm.size (Qt::TextSingleLine, s);
+                ext(2) = sz.width ();
+                ext(3) = sz.height ();
+              }
+            else if (str.iscellstr ())
+              {
+                string_vector sv = str.string_vector_value ();
+                double wd = 0.0;
+                double hg = 0.0;
+                for (octave_idx_type ii = 0; ii < sv.numel (); ii++)
+                  {
+                    s = QString::fromStdString (sv(ii));
+                    sz = fm.size (Qt::TextSingleLine, s);
+                    wd = std::max (wd, static_cast<double> (sz.width ()));
+                    hg = std::max (hg, static_cast<double> (sz.height ()));
+                  }
+
+                ext(2) = wd;
+                // FIXME: Find a better way to determine the height of e.g.
+                // listbox uicontrol objects
+                ext(3) = hg * sv.numel ();
+              }
+          }
+      }
 
     return ext;
   }
@@ -434,6 +479,8 @@
       }
     else if (go.isa ("uibuttongroup"))
       obj = ButtonGroup::create (m_octave_qobj, m_interpreter, go);
+    else if (go.isa ("uilabel"))
+      obj = Label::create (m_octave_qobj, m_interpreter, go);
     else if (go.isa ("uipanel"))
       obj = Panel::create (m_octave_qobj, m_interpreter, go);
     else if (go.isa ("uimenu"))
diff -r 95fa3182f00c -r 4c333659fb48 libinterp/corefcn/gl-render.cc
--- a/libinterp/corefcn/gl-render.cc	Mon Sep 19 13:14:16 2022 +0200
+++ b/libinterp/corefcn/gl-render.cc	Tue Sep 20 20:11:14 2022 -0400
@@ -744,7 +744,7 @@
     else if (go.isa ("uimenu") || go.isa ("uicontrol")
              || go.isa ("uicontextmenu") || go.isa ("uitoolbar")
              || go.isa ("uipushtool") || go.isa ("uitoggletool")
-             || go.isa ("uitable"))
+             || go.isa ("uitable") ||  go.isa ("uilabel"))
       ; // SKIP
     else if (go.isa ("uipanel"))
       {
diff -r 95fa3182f00c -r 4c333659fb48 libinterp/corefcn/graphics.cc
--- a/libinterp/corefcn/graphics.cc	Mon Sep 19 13:14:16 2022 +0200
+++ b/libinterp/corefcn/graphics.cc	Tue Sep 20 20:11:14 2022 -0400
@@ -541,6 +541,20 @@
 }
 
 static Matrix
+default_uilabel_position (void)
+{
+  Matrix retval (1, 4);
+
+  retval(0) = 100;
+  retval(1) = 100;
+  retval(2) = 31;
+  retval(3) = 22;
+
+  return retval;
+}
+
+
+static Matrix
 default_control_position (void)
 {
   Matrix retval (1, 4);
@@ -1155,7 +1169,7 @@
 
                   if (pfx.compare ("surface") || pfx.compare ("scatter")
                       || pfx.compare ("hggroup") || pfx.compare ("uipanel")
-                      || pfx.compare ("uitable"))
+                      || pfx.compare ("uitable") || pfx.compare ("uilabel"))
                     offset = 7;
                   else if (len >= 9)
                     {
@@ -1233,6 +1247,8 @@
     go = new uimenu (h, p);
   else if (type.compare ("uicontrol"))
     go = new uicontrol (h, p);
+  else if (type.compare ("uilabel"))
+    go = new uilabel (h, p);
   else if (type.compare ("uipanel"))
     go = new uipanel (h, p);
   else if (type.compare ("uibuttongroup"))
@@ -2332,7 +2348,7 @@
 
                   if (pfx.compare ("surface") || pfx.compare ("scatter")
                       || pfx.compare ("hggroup")|| pfx.compare ("uipanel")
-                      || pfx.compare ("uitable"))
+                      || pfx.compare ("uitable") || pfx.compare ("uilabel"))
                     offset = 7;
                   else if (len > 9)
                     {
@@ -2403,6 +2419,8 @@
             has_property = uicontrol::properties::has_core_property (pname);
           else if (pfx == "uibuttongroup")
             has_property = uibuttongroup::properties::has_core_property (pname);
+          else if (pfx == "uilabel")
+            has_property = uilabel::properties::has_core_property (pname);
           else if (pfx == "uipanel")
             has_property = uipanel::properties::has_core_property (pname);
           else if (pfx == "uicontextmenu")
@@ -2477,7 +2495,7 @@
 
                   if (pfx.compare ("surface") || pfx.compare ("scatter")
                       || pfx.compare ("hggroup") || pfx.compare ("uipanel")
-                      || pfx.compare ("uitable"))
+                      || pfx.compare ("uitable") || pfx.compare ("uilabel"))
                     offset = 7;
                   else if (len > 9)
                     {
@@ -11173,6 +11191,52 @@
 
 // ---------------------------------------------------------------------
 
+void
+uilabel::properties::update_text (void)
+{
+
+}
+
+
+Matrix
+uilabel::properties::get_boundingbox (bool,
+                                      const Matrix& parent_pix_size) const
+{
+  Matrix pos = get_position ().matrix_value ();
+  Matrix parent_size (parent_pix_size);
+
+  if (parent_size.isempty ())
+    {
+      gh_manager& gh_mgr = octave::__get_gh_manager__ ();
+
+      graphics_object go = gh_mgr.get_object (get_parent ());
+
+      if (go.valid_object ())
+        parent_size = go.get_properties ().get_boundingbox (true).extract_n (0, 2, 1, 2);
+      else
+        parent_size = default_figure_position ();
+    }
+
+  pos = convert_position (pos, "pixels", "pixels", parent_size);
+
+  pos(0)--;
+  pos(1)--;
+  pos(1) = parent_size(1) - pos(1) - pos(3);
+
+  return pos;
+}
+
+double
+uilabel::properties::get___fontsize_points__ (double box_pix_height) const
+{
+  double fontsz = get_fontsize ();
+  double parent_height = box_pix_height;
+
+  return convert_font_size (fontsz, "pixels", "points", parent_height);
+}
+
+// ---------------------------------------------------------------------
+
 Matrix
 uibuttongroup::properties::get_boundingbox (bool internal,
                                             const Matrix& parent_pix_size) const
@@ -12598,6 +12662,7 @@
   plist_map["surface"] = surface::properties::factory_defaults ();
   plist_map["light"] = light::properties::factory_defaults ();
   plist_map["hggroup"] = hggroup::properties::factory_defaults ();
+  plist_map["uilabel"] = uilabel::properties::factory_defaults ();
   plist_map["uimenu"] = uimenu::properties::factory_defaults ();
   plist_map["uicontrol"] = uicontrol::properties::factory_defaults ();
   plist_map["uibuttongroup"] = uibuttongroup::properties::factory_defaults ();
@@ -13547,6 +13612,15 @@
   GO_BODY (uibuttongroup);
 }
 
+DEFMETHOD (__go_uilabel__, interp, args, ,
+           doc: /* -*- texinfo -*-
+@deftypefn {} {@var{hui} =} __go_uilabel__ (@var{parent})
+Undocumented internal function.
+@end deftypefn */)
+{
+  GO_BODY (uilabel);
+}
+
 DEFMETHOD (__go_uipanel__, interp, args, ,
            doc: /* -*- texinfo -*-
 @deftypefn {} {@var{hui} =} __go_uipanel__ (@var{parent})
diff -r 95fa3182f00c -r 4c333659fb48 libinterp/corefcn/graphics.in.h
--- a/libinterp/corefcn/graphics.in.h	Mon Sep 19 13:14:16 2022 +0200
+++ b/libinterp/corefcn/graphics.in.h	Tue Sep 20 20:11:14 2022 -0400
@@ -6168,6 +6168,95 @@
 
 // ---------------------------------------------------------------------
 
+class OCTINTERP_API uilabel : public base_graphics_object
+{
+public:
+
+  class OCTINTERP_API properties : public base_properties
+  {
+  public:
+
+    OCTINTERP_API Matrix
+    get_boundingbox (bool internal = false,
+                     const Matrix& parent_pix_size = Matrix ()) const;
+
+    OCTINTERP_API double
+    get___fontsize_points__ (double box_pix_height = 0) const;
+
+    // See the genprops.awk script for an explanation of the
+    // properties declarations.
+    // Programming note: Keep property list sorted if new ones are added.
+
+    BEGIN_PROPERTIES (uilabel)
+      //color_property backgroundcolor , color_property(color_values (0.94, 0.94, 0.94), radio_values ("{none}"))
+      color_property backgroundcolor , color_property (radio_values ("{none}"), color_values (0.94, 0.94, 0.94))
+      radio_property enable , "{on}|inactive|off"
+      radio_property fontangle u , "{normal}|italic"
+      string_property fontname u , OCTAVE_DEFAULT_FONTNAME
+      double_property fontsize u , 10
+      radio_property fontweight u , "{normal}|bold"
+      color_property fontcolor , color_values (0, 0, 0)
+      radio_property horizontalalignment , "{left}|center|right"
+      array_property position , default_uilabel_position()
+      string_array_property text u , "Label"
+      radio_property wordwrap u , "{off}|on"
+      radio_property interpreter u , "tex|{none}|html|latex"
+      string_property tooltip , ""
+      radio_property verticalalignment , "top|{center}|bottom"
+
+      // Octave-specific properties
+      any_property __object__ h , Matrix ()
+    END_PROPERTIES
+
+  private:
+    //std::string m_cached_units;
+
+  protected:
+    void init (void)
+    {
+      m_position.add_constraint (dim_vector (1, 4));
+      m_fontsize.add_constraint ("min", 0.0, false);
+    }
+
+    OCTINTERP_API void update_text(void);
+
+    void update_wordwrap (void) { update_text (); }
+    void update_fontname (void) { update_text (); }
+    void update_fontsize (void) { update_text (); }
+    void update_interpreter (void) { update_text(); }
+    void update_fontangle (void) { update_text (); }
+    void update_fontweight (void) { update_text (); }
+
+  };
+
+private:
+  properties m_properties;
+
+public:
+  uilabel (const graphics_handle& mh, const graphics_handle& p)
+    : base_graphics_object (), m_properties (mh, p)
+  { }
+
+  ~uilabel (void) = default;
+
+  base_properties& get_properties (void) { return m_properties; }
+
+  const base_properties& get_properties (void) const { return m_properties; }
+
+  bool valid_object (void) const { return true; }
+
+  bool has_readonly_property (const caseless_str& pname) const
+  {
+    bool retval = m_properties.has_readonly_property (pname);
+    if (! retval)
+      retval = base_properties::has_readonly_property (pname);
+    return retval;
+  }
+};
+
+// ---------------------------------------------------------------------
+
+
 class OCTINTERP_API uipanel : public base_graphics_object
 {
 public:
diff -r 95fa3182f00c -r 4c333659fb48 scripts/gui/module.mk
--- a/scripts/gui/module.mk	Mon Sep 19 13:14:16 2022 +0200
+++ b/scripts/gui/module.mk	Tue Sep 20 20:11:14 2022 -0400
@@ -37,6 +37,7 @@
   %reldir%/uigetdir.m \
   %reldir%/uigetfile.m \
   %reldir%/uifigure.m \
+  %reldir%/uilabel.m \
   %reldir%/uimenu.m \
   %reldir%/uipanel.m \
   %reldir%/uipushtool.m \
diff -r 95fa3182f00c -r 4c333659fb48 scripts/gui/uilabel.m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/gui/uilabel.m	Tue Sep 20 20:11:14 2022 -0400
@@ -0,0 +1,88 @@
+########################################################################
+##
+## Copyright (C) 2022 The Octave Project Developers
+##
+## See the file COPYRIGHT.md in the top-level directory of this
+## distribution or <https://octave.org/copyright/>.
+##
+## This file is part of Octave.
+##
+## Octave is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## Octave is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with Octave; see the file COPYING.  If not, see
+## <https://www.gnu.org/licenses/>.
+##
+########################################################################
+
+## -*- texinfo -*-
+## @deftypefn  {} {@var{hui} =} uilabel ()
+## @deftypefnx {} {@var{hui} =} uilabel (@var{property}, @var{value}, @dots{})
+## @deftypefnx {} {@var{hui} =} uilabel (@var{parent})
+## @deftypefnx {} {@var{hui} =} uilabel (@var{parent}, @var{property}, @var{value}, @dots{})
+## @deftypefnx {} {} uilabel (@var{h})
+##
+## Create a uilabel object and return a handle to it.
+##
+## A uicontrol object is used to create a label component..
+##
+## If @var{parent} is omitted then a uilabel for the current figure is
+## created.  If no figure is available, a new figure is created first.
+##
+## If @var{parent} is given then a uilabel relative to @var{parent} is
+## created.
+##
+## Any provided property value pairs will override the default values of the
+## created uilabel object.
+##
+## The full list of properties is documented at @ref{Uilabel Properties}.
+##
+## Examples:
+##
+## @example
+## @group
+## ## Create figure and panel on it
+## f = figure;
+## ## Create a label
+## l1 = uilabel (f, "text", "A label here", ...
+##                  "position", [10 10 150 40]);
+## @end group
+## @end example
+##
+## @seealso{figure, uicontrol}
+## @end deftypefn
+
+function hui = uilabel (varargin)
+
+  if (nargin == 1 && isgraphics (varargin{1}, "uicontrol"))
+    set (varargin{1}, "__focus__", "on");
+    return;
+  endif
+
+  [h, args] = __uiobject_split_args__ ("uilabel", varargin,
+                                       {"figure", "uipanel", "uibuttongroup"});
+
+  htmp = __go_uilabel__ (h, args{:});
+
+  if (nargout > 0)
+    hui = htmp;
+  endif
+
+endfunction
+
+
+%!test
+%! hf = figure ("visible", "off");
+%! unwind_protect
+%!   h = uilabel (hf, "text", "Hello World");
+%! unwind_protect_cleanup
+%!   close (hf);
+%! end_unwind_protect
