1
votes

I try do build a window with gtkmm in which I have two text views. The text views should be arranged as a vertical split screen. Like that: Split screen

Later I want be able to split the screen vertical and horizontal again and again and resize the split areas, like in emacs.

I thought a simple split screen should be easy but I'm stuck already there. I thought about using a Gtk::Grid as layout container and every time the user wants to split the screen, I thought about adding a row or column and add a new text view in the newly created area.

Here is my code:

main.cc

#include <gtkmm/application.h>

#include "examplewindow.h"

int main(int argc, char *argv[])
{
  auto app = Gtk::Application::create(argc, argv, "org.gtkmm.example");

  ExampleWindow window;

  //Shows the window and returns when it is closed.
  return app->run(window);
}

examplewindow.h

#ifndef GTKMM_EXAMPLEWINDOW_H
#define GTKMM_EXAMPLEWINDOW_H

#include <gtkmm.h>

class ExampleWindow : public Gtk::Window
{
public:
  ExampleWindow();
  virtual ~ExampleWindow();

protected:
  Gtk::Grid main_grid;
  Gtk::ScrolledWindow scrolled_window1;
  Gtk::ScrolledWindow scrolled_window2;
  Gtk::TextView text_view1;
  Gtk::TextView text_view2;

  Glib::RefPtr<Gtk::TextBuffer> text_buffer1, text_buffer2;

  void fill_buffers();
};

#endif //GTKMM_EXAMPLEWINDOW_H

examplewindow.cc

#include "examplewindow.h"

ExampleWindow::ExampleWindow() {
  set_title("Gtk splitted textviews");
  set_border_width(12);

  add(main_grid);

  scrolled_window1.add(text_view1);
  scrolled_window1.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
  scrolled_window2.add(text_view2);
  scrolled_window1.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);

  main_grid.insert_column(0);
  main_grid.attach(scrolled_window1, 0, 0, 1, 1);
  //scrolled_window1.set_hexpand(true);
  //scrolled_window1.set_vexpand(true);

  main_grid.attach(scrolled_window2, 1, 0, 1, 1);
  //scrolled_window1.set_hexpand(true);
  //scrolled_window1.set_vexpand(true);

  fill_buffers();
  text_view1.set_buffer(text_buffer1);
  text_view2.set_buffer(text_buffer2);

  show_all_children();
}

ExampleWindow::~ExampleWindow() {}

void ExampleWindow::fill_buffers() {
  text_buffer1 = Gtk::TextBuffer::create();
  text_buffer1->set_text("This is the text from TextBuffer #1.");

  text_buffer2 = Gtk::TextBuffer::create();
  text_buffer2->set_text(
          "This is some alternative text, from TextBuffer #2.");

}

build with:

g++ examplewindow.cc main.cc -o splittv `pkg-config gtkmm-3.0 --cflags --libs`

This produces that result: Too small text views

The text views are obviously to small. If I set hexpand and vexpand to true on both text views, text_view1 repressed text_view2.

1
For splitting screens I would start looking into GtkPanedGerhardh
You have a typo in your code. You set hexpand and vexpand twice for scrolled_window1. Perhaps you want to change second to scrolled_window2.JohnKoch

1 Answers

1
votes

As @Gerhardh suggested in a comment you could use GtkPaned

#include <gtkmm.h>
#include <memory>
#include <string>

struct Body
{
    inline static int i=0;
    Gtk::Box Box;
    Gtk::Button SplitHButton, SplitVButton, CloseButton;
    Gtk::Label Label;
    Body()
    {
        SplitHButton.set_label("h");
        SplitVButton.set_label("v");
        Label.set_text(std::to_string(i++));
        CloseButton.set_label("c");
        Box.add(SplitHButton);
        Box.add(SplitVButton);
        Box.add(Label);
        Box.add(CloseButton);
        Box.show_all();
    }
};

struct Pane
{
    Gtk::Paned PaneWidget;
    std::shared_ptr<Pane> ChildPane1, ChildPane2;
    Body Body1, Body2;
    Pane(Gtk::Orientation orientation=Gtk::ORIENTATION_HORIZONTAL):
        PaneWidget(orientation)
    {
        PaneWidget.add1(Body1.Box);
        PaneWidget.add2(Body2.Box);

        Body1.SplitHButton.signal_clicked().connect([this]{Split(ChildPane1, Gtk::ORIENTATION_HORIZONTAL, Body1, true);});
        Body1.SplitVButton.signal_clicked().connect([this]{Split(ChildPane1, Gtk::ORIENTATION_VERTICAL, Body1, true);});
        Body2.SplitHButton.signal_clicked().connect([this]{Split(ChildPane2, Gtk::ORIENTATION_HORIZONTAL, Body2, false);});
        Body2.SplitVButton.signal_clicked().connect([this]{Split(ChildPane2, Gtk::ORIENTATION_VERTICAL, Body2, false);});

        PaneWidget.show_all();
    }

    void Split(std::shared_ptr<Pane>& pane, Gtk::Orientation orientation, Body& body, bool leftTop)
    {
        pane = std::make_shared<Pane>(orientation);
        PaneWidget.remove(body.Box);
        if(leftTop)
            PaneWidget.add1(pane->PaneWidget);
        else
            PaneWidget.add2(pane->PaneWidget);

        auto lambda = [&]{
                PaneWidget.remove(pane->PaneWidget);
                if(leftTop)
                    PaneWidget.add1(body.Box);
                else
                    PaneWidget.add2(body.Box);
                PaneWidget.show_all();
                pane.reset();
        };

        pane->Body1.CloseButton.signal_clicked().connect(lambda);
        pane->Body2.CloseButton.signal_clicked().connect(lambda);
    }
};

int main()
{
    auto GtkApp = Gtk::Application::create();
    Gtk::Window w;
    Pane p;
    w.add(p.PaneWidget);
    w.resize(800,600);
    w.show_all();
    GtkApp->run(w);
    return 0;
}

enter image description here