Commit d642a613 authored by Thomas Friedrichsmeier's avatar Thomas Friedrichsmeier
Browse files

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.
parent c1303235
<!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>
......@@ -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;
......
......@@ -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 ///////////////////////
......
......@@ -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);
......
......@@ -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 ());
}
}
......@@ -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
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment