Members of the KDE Community are recommended to subscribe to the kde-community mailing list at https://mail.kde.org/mailman/listinfo/kde-community to allow them to participate in important discussions and receive other important announcements

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

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);
......
Markdown is supported
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