[rkward/work/split_views] rkward: Support proper window splitting of script windows and help windows.

Thomas Friedrichsmeier null at kde.org
Mon Jun 19 20:08:13 UTC 2017


Git commit d642a6136c64cb2ec3e8dd88062588503fa43cd2 by Thomas Friedrichsmeier.
Committed on 19/06/2017 at 20:06.
Pushed by tfry into branch 'work/split_views'.

Support proper window splitting of script windows and help windows.

Only main window supported for now.
TODO: Also support data editor, output, and object viewer windows.

A  +21   -0    rkward/pages/rkward_split_views.rkh
M  +1    -1    rkward/rkward.cpp
M  +32   -12   rkward/windows/rkworkplace.cpp
M  +3    -0    rkward/windows/rkworkplace.h
M  +32   -22   rkward/windows/rkworkplaceview.cpp
M  +6    -2    rkward/windows/rkworkplaceview.h

https://commits.kde.org/rkward/d642a6136c64cb2ec3e8dd88062588503fa43cd2

diff --git a/rkward/pages/rkward_split_views.rkh b/rkward/pages/rkward_split_views.rkh
new file mode 100644
index 00000000..189dde88
--- /dev/null
+++ b/rkward/pages/rkward_split_views.rkh
@@ -0,0 +1,21 @@
+<!DOCTYPE rkhelp>
+<document>
+	<title>Split views</title>
+
+	<summary>
+	Split views can be used to partition the main window into several view areas.
+
+	They can be used to make better used of screen space, but also to view far-apart portions of a single file, simultaniously.
+	</summary>
+
+	<section title="Splitting views" id="introduction">
+	Splitting views is currently supported for the main window, only. Use Window->Split Vertical or Window->Split Horizontal to split the current view area vertically, or horizontally. For windows that support it (e.g. scripts, help pages), the newly created view area will contain a copy of the current window. When splitting other windows (such as onscreen graphics devices), the new view area will be initialized to show this help page.
+
+	To add further files to a view area, simply open them the normal way (from "File Browser" window, the menu, or from the "R Console" window). Simply make sure that the view area where you want them new window to open has focus / is active.
+	</section>
+
+	<section title="Closing split views" id="closing">
+	To remove a view area, simply close all windows inside it.
+	</section>
+</document>
+ 
diff --git a/rkward/rkward.cpp b/rkward/rkward.cpp
index 52aac6d6..0e6ba333 100644
--- a/rkward/rkward.cpp
+++ b/rkward/rkward.cpp
@@ -760,7 +760,7 @@ bool RKWardMainWindow::doQueryQuit () {
 				return false;
 			}
 		}
-		lockGUIRebuild (false);
+//		lockGUIRebuild (false);  // No need to update GUI anymore (and doing so is potentially asking for trouble, anyway)
 	}
 
 	return true;
diff --git a/rkward/windows/rkworkplace.cpp b/rkward/windows/rkworkplace.cpp
index 659a6327..f5a0fe1a 100644
--- a/rkward/windows/rkworkplace.cpp
+++ b/rkward/windows/rkworkplace.cpp
@@ -438,13 +438,15 @@ RKMDIWindow* RKWorkplace::openScriptEditor (const QUrl &url, const QString& enco
 
 // is this url already opened?
 	if (!url.isEmpty ()) {
-	  	RKWorkplaceObjectList script_windows = getObjectList (RKMDIWindow::CommandEditorWindow, RKMDIWindow::AnyWindowState);
-		for (RKWorkplaceObjectList::const_iterator it = script_windows.constBegin (); it != script_windows.constEnd (); ++it) {
-			  QUrl ourl = static_cast<RKCommandEditorWindow *> (*it)->url ();
-			  if (url == ourl) {
-				  (*it)->activate ();
-				  return (*it);
-			  }
+		RKWorkplaceObjectList script_windows = getObjectList (RKMDIWindow::CommandEditorWindow, RKMDIWindow::AnyWindowState);
+		for (int i = 0; i < script_windows.size (); ++i) {
+			QUrl ourl = static_cast<RKCommandEditorWindow *> (script_windows[i])->url ();
+			if (url == ourl) {
+				if (view ()->windowInActivePane (script_windows[i])) {
+					script_windows[i]->activate ();
+					return (script_windows[i]);
+				}
+			}
 		}
 	}
 
@@ -473,10 +475,12 @@ RKMDIWindow* RKWorkplace::openHelpWindow (const QUrl &url, bool only_once) {
 
 	if (only_once) {
 		RKWorkplaceObjectList help_windows = getObjectList (RKMDIWindow::HelpWindow, RKMDIWindow::AnyWindowState);
-		for (RKWorkplaceObjectList::const_iterator it = help_windows.constBegin (); it != help_windows.constEnd (); ++it) {
-			if (static_cast<RKHTMLWindow *> (*it)->url ().matches (url, QUrl::StripTrailingSlash | QUrl::NormalizePathSegments)) {
-				(*it)->activate ();
-				return (*it);
+		for (int i = 0; i < help_windows.size (); ++i) {
+			if (static_cast<RKHTMLWindow *> (help_windows[i])->url ().matches (url, QUrl::StripTrailingSlash | QUrl::NormalizePathSegments)) {
+				if (view ()->windowInActivePane (help_windows[i])) {
+					help_windows[i]->activate ();
+					return (help_windows[i]);
+				}
 			}
 		}
 	}
@@ -840,7 +844,8 @@ void RKWorkplace::restoreWorkplace (const QStringList &description) {
 			RObject *object = RObjectList::getObjectList ()->findObject (specification);
 			if (object) win = editObject (object);
 		} else if (type == "script") {
-			win = openScriptEditor (checkAdjustRestoredUrl (specification, base));
+			QUrl url = checkAdjustRestoredUrl (specification, base);
+			win = openScriptEditor (url, QString (), RKSettingsModuleCommandEditor::matchesScriptFileFilter (url.fileName()));
 		} else if (type == "output") {
 			win = openOutputWindow (checkAdjustRestoredUrl (specification, base));
 		} else if (type == "help") {
@@ -869,6 +874,21 @@ void RKWorkplace::restoreWorkplace (const QStringList &description) {
 	RKWardMainWindow::getMain ()->lockGUIRebuild (false);
 }
 
+void RKWorkplace::duplicateAndAttachWindow (RKMDIWindow* source) {
+	RK_TRACE (APP);
+	RK_ASSERT (source);
+
+	if (source->isType (RKMDIWindow::CommandEditorWindow)) {
+		QUrl url = static_cast<RKCommandEditorWindow*> (source)->url ();
+		openScriptEditor (url, QString (), RKSettingsModuleCommandEditor::matchesScriptFileFilter (url.fileName()));
+	} else if (source->isType (RKMDIWindow::HelpWindow)) {
+		openHelpWindow (static_cast<RKHTMLWindow*> (source)->url ());
+	} else {
+		openHelpWindow (QUrl ("rkward://page/rkward_split_views"));
+	}
+}
+
+
 ///////////////////////// END RKWorkplace ////////////////////////////
 ///////////////////// BEGIN RKMDIWindowHistory ///////////////////////
 
diff --git a/rkward/windows/rkworkplace.h b/rkward/windows/rkworkplace.h
index abcd1d6e..2fb7a8fb 100644
--- a/rkward/windows/rkworkplace.h
+++ b/rkward/windows/rkworkplace.h
@@ -177,6 +177,9 @@ Has no effect, if RKSettingsModuleGeneral::workplaceSaveMode () != RKSettingsMod
 /** Inserts the given message widget above the central area. While technically, the workplace becomes the parent widget of the message widget, it is the caller's responsibility to
  *  delete the widget, when appropriate. */
 	void addMessageWidget (KMessageWidget *message);
+
+/** For window splitting: Copy the given window (or, if that is not possible, create a placeholder window), and attach it to the main view. */
+	void duplicateAndAttachWindow (RKMDIWindow *source);
 signals:
 /** emitted when the workspace Url has changed */
 	void workspaceUrlChanged (const QUrl &url);
diff --git a/rkward/windows/rkworkplaceview.cpp b/rkward/windows/rkworkplaceview.cpp
index 98e38bcd..2e889915 100644
--- a/rkward/windows/rkworkplaceview.cpp
+++ b/rkward/windows/rkworkplaceview.cpp
@@ -58,8 +58,8 @@ RKWorkplaceViewPane::RKWorkplaceViewPane (RKWorkplaceView* parent) : QTabWidget
 	connect (tabBar (), &QWidget::customContextMenuRequested, this, &RKWorkplaceViewPane::showContextMenu);
 
 	KAcceleratorManager::setNoAccel (tabBar ());	// TODO: This is a WORKAROUND for a bug in kdelibs where tabs named "a0.txt", "a1.txt", etc. will steal the Alt+0/1... shortcuts
-	tabBar ()->hide ();  // initially
-	connect (this, &QTabWidget::currentChanged, workplace_view, &RKWorkplaceView::currentPageChanged);
+//	tabBar ()->hide ();  // initially
+	connect (this, &QTabWidget::currentChanged, this, &RKWorkplaceViewPane::currentPageChanged);
 }
 
 RKWorkplaceViewPane::~RKWorkplaceViewPane () {
@@ -113,7 +113,7 @@ void RKWorkplaceViewPane::closePage (QWidget* page) {
 void RKWorkplaceViewPane::tabRemoved (int index) {
 	RK_TRACE (APP);
 	QTabWidget::tabRemoved (index);
-	if (count () < 2) tabBar ()->hide ();
+//	if (count () < 2) tabBar ()->hide ();
 	if (count () < 1) emit (becameEmpty (this));
 	workplace_view->updateActions ();
 }
@@ -121,7 +121,7 @@ void RKWorkplaceViewPane::tabRemoved (int index) {
 void RKWorkplaceViewPane::tabInserted (int index) {
 	RK_TRACE (APP);
 	QTabWidget::tabInserted (index);
-	if (count () > 1) tabBar ()->show ();
+//	if (count () > 1) tabBar ()->show ();
 	workplace_view->updateActions ();
 }
 
@@ -153,18 +153,32 @@ void RKWorkplaceViewPane::contextMenuDetachWindow () {
 	RKWorkplace::mainWorkplace ()->detachWindow (static_cast<RKMDIWindow*> (widget (tab)));
 }
 
+void RKWorkplaceViewPane::currentPageChanged (int page) {
+	RK_TRACE (APP);
+
+	RKMDIWindow *w = static_cast<RKMDIWindow*> (currentWidget ());
+	if (w) {
+		workplace_view->setCaption (w->shortCaption ());
+		w->activate ();		// not always automatically active
+	} else {
+		// happens when empty
+		workplace_view->setCaption (QString ());
+	}
+}
+
 
 
 RKWorkplaceViewPane* RKWorkplaceView::createPane () {
 	RK_TRACE (APP);
-	RKWorkplaceViewPane *pane = new RKWorkplaceViewPane (this);
-	QObject::connect (pane, &RKWorkplaceViewPane::becameEmpty, this, &RKWorkplaceView::purgePane);
-	return pane;
+	newpane = new RKWorkplaceViewPane (this);
+	QObject::connect (newpane, &RKWorkplaceViewPane::becameEmpty, this, &RKWorkplaceView::purgePane);
+	return newpane;
 }
 
 RKWorkplaceView::RKWorkplaceView (QWidget *parent) : QSplitter (parent) {
 	RK_TRACE (APP);
 
+	newpane = 0;
 	RKWorkplaceViewPane *pane = createPane ();
 	addWidget (pane);
 	panes.append (pane);
@@ -178,6 +192,8 @@ RKWorkplaceView::~RKWorkplaceView () {
 RKWorkplaceViewPane* RKWorkplaceView::activePane () const {
 	RK_TRACE (APP);
 
+	if (newpane) return newpane;
+
 	for (int i = 0; i < panes.size (); ++i) {
 		if (panes[i]->isActive ()) return panes[i];
 	}
@@ -295,7 +311,7 @@ void RKWorkplaceView::splitView (Qt::Orientation orientation, RKWorkplaceViewPan
 	const int index = splitter->indexOf (pane);
 	const int lindex = panes.indexOf (pane);
 
-	RKWorkplaceViewPane *newpane = createPane ();
+	newpane = createPane ();
 	panes.insert (lindex + 1, newpane);
 
 	// If there is only one child (left) in the current splitter, we can just set the orientation as needed.
@@ -322,8 +338,9 @@ void RKWorkplaceView::splitView (Qt::Orientation orientation, RKWorkplaceViewPan
 		newsplitter->setSizes (subsizes);
 	}
 	newpane->show ();
-	newpane->addTab (active, "Test");
-	newpane->currentWidget ()->setFocus ();
+	// "copy" the "split" window to the new pane.
+	// TODO: line below will only work for the main window, for the time being. We need to rethink this, if we want to enable window splitting for detached windows, too.
+	RKWorkplace::mainWorkplace ()->duplicateAndAttachWindow (active);
 
 	splitter->setSizes (sizes);
 	setUpdatesEnabled (true);
@@ -340,6 +357,7 @@ void RKWorkplaceView::addWindow (RKMDIWindow *widget) {
 
 	RKWorkplaceViewPane *pane = activePane ();
 	RK_ASSERT (pane);
+	newpane = 0; // next window will get default treatment
 	id = pane->addTab (widget, icon, widget->shortCaption ());
 
 	connect (widget, &RKMDIWindow::captionChanged, this, &RKWorkplaceView::childCaptionChanged);
@@ -366,6 +384,10 @@ bool RKWorkplaceView::hasWindow (RKMDIWindow *widget) const {
 	return (findWindow (widget) != 0);
 }
 
+bool RKWorkplaceView::windowInActivePane (RKMDIWindow *widget) const {
+	return (findWindow (widget) == activePane ());
+}
+
 void RKWorkplaceView::removeWindow (RKMDIWindow *widget, bool destroyed) {
 	RK_TRACE (APP);
 
@@ -425,16 +447,4 @@ void RKWorkplaceView::setCaption (const QString &caption) {
 	emit (captionChanged (caption));
 }
 
-void RKWorkplaceView::currentPageChanged (int) {
-	RK_TRACE (APP);
-
-	RKMDIWindow *w = activePage ();
-	if (w) {
-		setCaption (w->shortCaption ());
-		w->activate ();		// not always automatically active
-	} else {
-		setCaption (QString ());
-	}
-}
-
 
diff --git a/rkward/windows/rkworkplaceview.h b/rkward/windows/rkworkplaceview.h
index 0a204ee3..f4394e8b 100644
--- a/rkward/windows/rkworkplaceview.h
+++ b/rkward/windows/rkworkplaceview.h
@@ -52,6 +52,8 @@ private slots:
 	void contextMenuClosePage ();
 /** handle detach request from context menu */
 	void contextMenuDetachWindow ();
+/** Internal function to ensure proper focus and update caption, when the current page has changed. */
+	void currentPageChanged (int page);
 };
 
 /** This is mostly a QTabWidget with some extras such as updating the caption, a context menu, etc.
@@ -75,6 +77,8 @@ public:
 	bool hasWindow (RKMDIWindow *widget) const;
 /** show the given page (does not set focus) */
 	void showWindow (RKMDIWindow *widget);
+/** Returns true if the given window is in the active pane of this view. */
+	bool windowInActivePane (RKMDIWindow *widget) const;
 
 /** @returns the currently active window */
 	RKMDIWindow *activePage () const;
@@ -90,8 +94,6 @@ signals:
 @param new_caption the new caption */
 	void captionChanged (const QString &new_caption);
 private slots:
-/** Internal function to update caption and actions, when the current page has changed. */
-	void currentPageChanged (int page);
 /** called when the caption of a window changes. Updates the tab-label, and - if appropriate - the caption of this widget */
 	void childCaptionChanged (RKMDIWindow *widget);
 /** Active the page left of the current tab */
@@ -116,6 +118,8 @@ private:
 
 	QList<RKWorkplaceViewPane*> panes;
 	RKWorkplaceViewPane *activePane () const;
+/** Newly added pane. Pointer needed so the first "new" window will go here. */
+	RKWorkplaceViewPane *newpane;
 };
 
 #endif



More information about the rkward-tracker mailing list