python-3.xgtk3gtkentrygtktextview

How to set Placeholder text in Gtk.TextView


I have been using Gtk Entry till now which offers set_placeholder_text method to set a placeholder text in it however, looking in the documentation , I found no such method for TextView .

Is there any way I can set placeholder text in Gtk Textview ?


Solution

  • I think there was a question almost similar to this one. The idea is to, eg, use the focus-in-event and focus-out-event to check the text buffer content.

    Example:

    1. Set placeholder text as a variable
    2. Set the placeholder text as the default treeview text buffer content
    3. On focus in, if the text buffer equals the placeholder, means no previous input, so delete the text (placeholder text). Otherwise keep the content.
    4. On focus out, if the text buffer has no text then set the content as the placeholder text. Otherwise do nothing.

    The idea is that if no text or the existing text is the placeholder text it means there's no user input.

    Python example

    Glade ui file (save it as placeholder-textview.ui):

    <?xml version="1.0" encoding="UTF-8"?>
    <!-- Generated with glade 3.20.0 -->
    <interface>
      <requires lib="gtk+" version="3.20"/>
      <object class="GtkTextBuffer" id="textbuffer1">
        <property name="text" translatable="yes">Please input text here...</property>
      </object>
      <object class="GtkWindow" id="window1">
        <property name="can_focus">False</property>
        <child>
          <object class="GtkBox">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="orientation">vertical</property>
            <child>
              <object class="GtkButton">
                <property name="label" translatable="yes">button</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">0</property>
              </packing>
            </child>
            <child>
              <object class="GtkEntry">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="placeholder_text" translatable="yes">This is an entry and below is a textview</property>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">1</property>
              </packing>
            </child>
            <child>
              <object class="GtkScrolledWindow">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="shadow_type">in</property>
                <child>
                  <object class="GtkTextView" id="textview1">
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                    <property name="vexpand">False</property>
                    <property name="buffer">textbuffer1</property>
                  </object>
                </child>
              </object>
              <packing>
                <property name="expand">True</property>
                <property name="fill">True</property>
                <property name="position">2</property>
              </packing>
            </child>
          </object>
        </child>
      </object>
    </interface>
    

    Python code:

    import gi
    gi.require_version('Gtk', '3.0')
    from gi.repository import Gtk
    
    
    def onFocusIn(self, event):
        if (textbuf.get_text(textbuf.get_start_iter(), textbuf.get_end_iter(), True) == placeholderStr):
            textbuf.set_text("")
        return False
    
    
    def onFocusOut(self, event):
        if (textbuf.get_text(textbuf.get_start_iter(), textbuf.get_end_iter(), True) == ""):
            textbuf.set_text(placeholderStr)
        return False
    
    placeholderStr = "This is the placeholder text..."
    
    builder = Gtk.Builder()
    builder.add_from_file("placeholder-textview.ui")
    
    window = builder.get_object("window1")
    
    textbuf = builder.get_object("textbuffer1")
    textbuf.set_text(placeholderStr)
    
    textview = builder.get_object("textview1")
    textview.connect("focus-in-event", onFocusIn)
    textview.connect("focus-out-event", onFocusOut)
    
    window.connect ("destroy", Gtk.main_quit)
    window.show_all()
    
    Gtk.main()
    

    Resulting UI:

    enter image description here

    Added a few widgets to keep the initial focus on other widgets.

    Compare the behavior with the Gtk.Entry. It's very similar.