Commit 46d8b876 authored by Martin Flöser's avatar Martin Flöser
Browse files

Move TabGroup functionality from Client to AbstractClient

Only setClientShown remains in Client. This might need a dedicated
implementation for ShellClient.
parent 7defd930
......@@ -114,14 +114,10 @@ bool AbstractClient::isTransient() const
return false;
}
TabGroup *AbstractClient::tabGroup() const
{
return nullptr;
}
void AbstractClient::setTabGroup(TabGroup* group)
{
Q_UNUSED(group)
tab_group = group;
emit tabGroupChanged();
}
void AbstractClient::setClientShown(bool shown)
......@@ -131,16 +127,83 @@ void AbstractClient::setClientShown(bool shown)
bool AbstractClient::untab(const QRect &toGeometry, bool clientRemoved)
{
Q_UNUSED(toGeometry)
Q_UNUSED(clientRemoved)
TabGroup *group = tab_group;
if (group && group->remove(this)) { // remove sets the tabgroup to "0", therefore the pointer is cached
if (group->isEmpty()) {
delete group;
}
if (clientRemoved)
return true; // there's been a broadcast signal that this client is now removed - don't touch it
setClientShown(!(isMinimized() || isShade()));
bool keepSize = toGeometry.size() == size();
bool changedSize = false;
if (quickTileMode() != QuickTileMode(QuickTileFlag::None)) {
changedSize = true;
setQuickTileMode(QuickTileFlag::None); // if we leave a quicktiled group, assume that the user wants to untile
}
if (toGeometry.isValid()) {
if (maximizeMode() != MaximizeRestore) {
changedSize = true;
maximize(MaximizeRestore); // explicitly calling for a geometry -> unmaximize
}
if (keepSize && changedSize) {
setGeometryRestore(geometry()); // checkWorkspacePosition() invokes it
QPoint cpoint = Cursor::pos();
QPoint point = cpoint;
point.setX((point.x() - toGeometry.x()) * geometryRestore().width() / toGeometry.width());
point.setY((point.y() - toGeometry.y()) * geometryRestore().height() / toGeometry.height());
auto geometry_restore = geometryRestore();
geometry_restore.moveTo(cpoint-point);
setGeometryRestore(geometry_restore);
} else {
setGeometryRestore(toGeometry); // checkWorkspacePosition() invokes it
}
setGeometry(geometryRestore());
checkWorkspacePosition();
}
return true;
}
return false;
}
bool AbstractClient::isCurrentTab() const
bool AbstractClient::tabTo(AbstractClient *other, bool behind, bool activate)
{
Q_ASSERT(other && other != this);
if (tab_group && tab_group == other->tabGroup()) { // special case: move inside group
tab_group->move(this, other, behind);
return true;
}
GeometryUpdatesBlocker blocker(this);
const bool wasBlocking = signalsBlocked();
blockSignals(true); // prevent client emitting "retabbed to nowhere" cause it's about to be entabbed the next moment
untab();
blockSignals(wasBlocking);
TabGroup *newGroup = other->tabGroup() ? other->tabGroup() : new TabGroup(other);
if (!newGroup->add(this, other, behind, activate)) {
if (newGroup->count() < 2) { // adding "c" to "to" failed for whatever reason
newGroup->remove(other);
delete newGroup;
}
return false;
}
return true;
}
void AbstractClient::syncTabGroupFor(QString property, bool fromThisClient)
{
if (tab_group)
tab_group->sync(property.toAscii().data(), fromThisClient ? this : tab_group->current());
}
bool AbstractClient::isCurrentTab() const
{
return !tab_group || tab_group->current() == this;
}
xcb_timestamp_t AbstractClient::userTime() const
{
return XCB_TIME_CURRENT_TIME;
......
......@@ -272,6 +272,10 @@ class KWIN_EXPORT AbstractClient : public Toplevel
* considered unresponsive. This usually indicates that the application froze or crashed.
*/
Q_PROPERTY(bool unresponsive READ unresponsive NOTIFY unresponsiveChanged)
/**
* The "Window Tabs" Group this Client belongs to.
**/
Q_PROPERTY(KWin::TabGroup* tabGroup READ tabGroup NOTIFY tabGroupChanged SCRIPTABLE false)
public:
virtual ~AbstractClient();
......@@ -422,11 +426,34 @@ public:
return m_minimized;
}
virtual void setFullScreen(bool set, bool user = true) = 0;
virtual TabGroup *tabGroup() const;
virtual void setTabGroup(TabGroup* group);
// Tabbing functions
Q_INVOKABLE inline bool tabBefore(AbstractClient *other, bool activate) { return tabTo(other, false, activate); }
Q_INVOKABLE inline bool tabBehind(AbstractClient *other, bool activate) { return tabTo(other, true, activate); }
/**
* Syncs the *dynamic* @param property @param fromThisClient or the @link currentTab() to
* all members of the @link tabGroup() (if there is one)
*
* eg. if you call:
* client->setProperty("kwin_tiling_floats", true);
* client->syncTabGroupFor("kwin_tiling_floats", true)
* all clients in this tabGroup will have ::property("kwin_tiling_floats").toBool() == true
*
* WARNING: non dynamic properties are ignored - you're not supposed to alter/update such explicitly
*/
Q_INVOKABLE void syncTabGroupFor(QString property, bool fromThisClient = false);
TabGroup *tabGroup() const;
/**
* Set tab group - this is to be invoked by TabGroup::add/remove(client) and NO ONE ELSE
*/
void setTabGroup(TabGroup* group);
virtual void setClientShown(bool shown);
Q_INVOKABLE virtual bool untab(const QRect &toGeometry = QRect(), bool clientRemoved = false);
virtual bool isCurrentTab() const;
Q_INVOKABLE bool untab(const QRect &toGeometry = QRect(), bool clientRemoved = false);
/*
* When a click is done in the decoration and it calls the group
* to change the visible client it starts to move-resize the new
* client, this function stops it.
*/
bool isCurrentTab() const;
virtual QRect geometryRestore() const = 0;
virtual MaximizeMode maximizeMode() const = 0;
void maximize(MaximizeMode);
......@@ -732,6 +759,11 @@ Q_SIGNALS:
void hasApplicationMenuChanged(bool);
void applicationMenuActiveChanged(bool);
void unresponsiveChanged(bool);
/**
* Emitted whenever the Client's TabGroup changed. That is whenever the Client is moved to
* another group, but not when a Client gets added or removed to the Client's ClientGroup.
**/
void tabGroupChanged();
protected:
AbstractClient();
......@@ -1031,6 +1063,8 @@ protected:
void finishWindowRules();
void discardTemporaryRules();
bool tabTo(AbstractClient *other, bool behind, bool activate);
private:
void handlePaletteChange();
QSharedPointer<TabBox::TabBoxClientImpl> m_tabBoxClient;
......@@ -1108,6 +1142,7 @@ private:
QKeySequence _shortcut;
WindowRules m_rules;
TabGroup* tab_group = nullptr;
static bool s_haveResizeEffect;
};
......@@ -1175,6 +1210,11 @@ inline void AbstractClient::setPendingGeometryUpdate(PendingGeometry_t update)
m_pendingGeometryUpdate = update;
}
inline TabGroup* AbstractClient::tabGroup() const
{
return tab_group;
}
}
Q_DECLARE_METATYPE(KWin::AbstractClient*)
......
......@@ -108,7 +108,6 @@ Client::Client()
, shadeHoverTimer(NULL)
, m_colormap(XCB_COLORMAP_NONE)
, in_group(NULL)
, tab_group(NULL)
, ping_timer(NULL)
, m_killHelperPID(0)
, m_pingTimestamp(XCB_TIME_CURRENT_TIME)
......@@ -167,6 +166,17 @@ Client::Client()
}
});
connect(this, &Client::tabGroupChanged, this,
[this] {
auto group = tabGroup();
if (group) {
unsigned long data[] = {qHash(group)}; //->id();
m_client.changeProperty(atoms->kde_net_wm_tab_group, XCB_ATOM_CARDINAL, 32, 1, data);
}
else
m_client.deleteProperty(atoms->kde_net_wm_tab_group);
});
// SELI TODO: Initialize xsizehints??
}
......@@ -1506,95 +1516,6 @@ void Client::fetchIconicName()
}
}
bool Client::tabTo(Client *other, bool behind, bool activate)
{
Q_ASSERT(other && other != this);
if (tab_group && tab_group == other->tabGroup()) { // special case: move inside group
tab_group->move(this, other, behind);
return true;
}
GeometryUpdatesBlocker blocker(this);
const bool wasBlocking = signalsBlocked();
blockSignals(true); // prevent client emitting "retabbed to nowhere" cause it's about to be entabbed the next moment
untab();
blockSignals(wasBlocking);
TabGroup *newGroup = other->tabGroup() ? other->tabGroup() : new TabGroup(other);
if (!newGroup->add(this, other, behind, activate)) {
if (newGroup->count() < 2) { // adding "c" to "to" failed for whatever reason
newGroup->remove(other);
delete newGroup;
}
return false;
}
return true;
}
bool Client::untab(const QRect &toGeometry, bool clientRemoved)
{
TabGroup *group = tab_group;
if (group && group->remove(this)) { // remove sets the tabgroup to "0", therefore the pointer is cached
if (group->isEmpty()) {
delete group;
}
if (clientRemoved)
return true; // there's been a broadcast signal that this client is now removed - don't touch it
setClientShown(!(isMinimized() || isShade()));
bool keepSize = toGeometry.size() == size();
bool changedSize = false;
if (quickTileMode() != QuickTileMode(QuickTileFlag::None)) {
changedSize = true;
setQuickTileMode(QuickTileFlag::None); // if we leave a quicktiled group, assume that the user wants to untile
}
if (toGeometry.isValid()) {
if (maximizeMode() != MaximizeRestore) {
changedSize = true;
maximize(MaximizeRestore); // explicitly calling for a geometry -> unmaximize
}
if (keepSize && changedSize) {
geom_restore = geometry(); // checkWorkspacePosition() invokes it
QPoint cpoint = Cursor::pos();
QPoint point = cpoint;
point.setX((point.x() - toGeometry.x()) * geom_restore.width() / toGeometry.width());
point.setY((point.y() - toGeometry.y()) * geom_restore.height() / toGeometry.height());
geom_restore.moveTo(cpoint-point);
} else {
geom_restore = toGeometry; // checkWorkspacePosition() invokes it
}
setGeometry(geom_restore);
checkWorkspacePosition();
}
return true;
}
return false;
}
void Client::setTabGroup(TabGroup *group)
{
tab_group = group;
if (group) {
unsigned long data[] = {qHash(group)}; //->id();
m_client.changeProperty(atoms->kde_net_wm_tab_group, XCB_ATOM_CARDINAL, 32, 1, data);
}
else
m_client.deleteProperty(atoms->kde_net_wm_tab_group);
emit tabGroupChanged();
}
bool Client::isCurrentTab() const
{
return !tab_group || tab_group->current() == this;
}
void Client::syncTabGroupFor(QString property, bool fromThisClient)
{
if (tab_group)
tab_group->sync(property.toAscii().data(), fromThisClient ? this : tab_group->current());
}
void Client::setClientShown(bool shown)
{
if (deleting)
......
......@@ -70,10 +70,6 @@ class KWIN_EXPORT Client
* Because of that no changed signal is provided.
*/
Q_PROPERTY(QSize basicUnit READ basicUnit)
/**
* The "Window Tabs" Group this Client belongs to.
**/
Q_PROPERTY(KWin::TabGroup* tabGroup READ tabGroup NOTIFY tabGroupChanged SCRIPTABLE false)
/**
* A client can block compositing. That is while the Client is alive and the state is set,
* Compositing is suspended and is resumed when there are no Clients blocking compositing any
......@@ -250,27 +246,6 @@ public:
StrutRects strutRects() const;
bool hasStrut() const override;
// Tabbing functions
TabGroup* tabGroup() const override; // Returns a pointer to client_group
Q_INVOKABLE inline bool tabBefore(Client *other, bool activate) { return tabTo(other, false, activate); }
Q_INVOKABLE inline bool tabBehind(Client *other, bool activate) { return tabTo(other, true, activate); }
/**
* Syncs the *dynamic* @param property @param fromThisClient or the @link currentTab() to
* all members of the @link tabGroup() (if there is one)
*
* eg. if you call:
* client->setProperty("kwin_tiling_floats", true);
* client->syncTabGroupFor("kwin_tiling_floats", true)
* all clients in this tabGroup will have ::property("kwin_tiling_floats").toBool() == true
*
* WARNING: non dynamic properties are ignored - you're not supposed to alter/update such explicitly
*/
Q_INVOKABLE void syncTabGroupFor(QString property, bool fromThisClient = false);
Q_INVOKABLE bool untab(const QRect &toGeometry = QRect(), bool clientRemoved = false) override;
/**
* Set tab group - this is to be invoked by TabGroup::add/remove(client) and NO ONE ELSE
*/
void setTabGroup(TabGroup* group) override;
/*
* If shown is true the client is mapped and raised, if false
* the client is unmapped and hidden, this function is called
......@@ -278,12 +253,6 @@ public:
* client.
*/
void setClientShown(bool shown) override;
/*
* When a click is done in the decoration and it calls the group
* to change the visible client it starts to move-resize the new
* client, this function stops it.
*/
bool isCurrentTab() const override;
/**
* Whether or not the window has a strut that expands through the invisible area of
......@@ -407,12 +376,6 @@ Q_SIGNALS:
void clientManaging(KWin::Client*);
void clientFullScreenSet(KWin::Client*, bool, bool);
/**
* Emitted whenever the Client's TabGroup changed. That is whenever the Client is moved to
* another group, but not when a Client gets added or removed to the Client's ClientGroup.
**/
void tabGroupChanged();
/**
* Emitted whenever the Client want to show it menu
*/
......@@ -497,8 +460,6 @@ private:
void updateInputWindow();
bool tabTo(Client *other, bool behind, bool activate);
Xcb::Property fetchShowOnScreenEdge() const;
void readShowOnScreenEdge(Xcb::Property &property);
/**
......@@ -562,7 +523,6 @@ private:
xcb_colormap_t m_colormap;
QString cap_normal, cap_iconic, cap_suffix;
Group* in_group;
TabGroup* tab_group;
QTimer* ping_timer;
qint64 m_killHelperPID;
xcb_timestamp_t m_pingTimestamp;
......@@ -634,11 +594,6 @@ inline Group* Client::group()
return in_group;
}
inline TabGroup* Client::tabGroup() const
{
return tab_group;
}
inline bool Client::isShown(bool shaded_is_shown) const
{
return !isMinimized() && (!isShade() || shaded_is_shown) && !hidden &&
......
......@@ -370,7 +370,7 @@ bool Client::manage(xcb_window_t w, bool isMapped)
}
}
}
if (!(tab_group || isMapped || session)) {
if (!(tabGroup() || isMapped || session)) {
// Attempt to automatically group similar windows
Client* similar = findAutogroupCandidate();
if (similar && !similar->noBorder()) {
......
......@@ -150,6 +150,7 @@ Q_SIGNALS:
void maxSizeChanged();
private:
friend class AbstractClient;
friend class Client;
// friend bool Client::tabTo(Client*, bool, bool);
bool add(KWin::AbstractClient *c, AbstractClient *other, bool behind, bool activateC);
......
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