From kde-devel Fri Oct 22 17:43:13 2004 From: =?ISO-8859-1?Q?David_H=E9not?= Date: Fri, 22 Oct 2004 17:43:13 +0000 To: kde-devel Subject: Re: Proposal to implement autohide in systemtray. Message-Id: <6a0f0e5204102210436af278e8 () mail ! gmail ! com> X-MARC-Message: https://marc.info/?l=kde-devel&m=109846705302373 MIME-Version: 1 Content-Type: multipart/mixed; boundary="------=_Part_2675_24497794.1098466993467" ------=_Part_2675_24497794.1098466993467 Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit Content-Disposition: inline Hi all, On Fri, 15 Oct 2004 16:27:16 -0600, Aaron Seigo wrote: > you'd need to do it with an event filter that catches all X events for the > geometry covering the systray. this kind of stuff is very, very ugly and > very, very prone to breakage. i'm very, very opposed to this sort of hack. of > course, perhaps someone else can suggest a more elegant way of managing it, > but the basic problem is that the icons are out-of-process and the system > tray has no real way of programatically interacting with them. ergo, the > current systray protocol sucks. this is a known issue. I have been thinking about this problem for some time as I like the possibility of hiding systray icons but I think the way it is done in XP sucks : for newbies I think it is quite confusing when the UI changes without any user action, and for "advanced" users an inactivity-based system is a bit frustrating. For example I may want for a particular icon to be never hidden, so I'm able to quickly know the application is running or not, and when I use a hidden icon I don't want it to wait for several minutes before it is hidden again. Impossible with a XP-like systray. So I thought the best way for me would be to configure which icons should be hidden regardless of their inactivity time. I have tried to implement it and finaly I have something working. I attached a patch with my changes. It still needs some work I think, but I find it quite useful. It adds a configuration dialog to the systray applet with the list of currently active systray icons and you can put them in the hide list. This configuration is saved so you don't have to manualy re-hide the icon next time it is added to the systray. When some icons are hidden you get a button to show/hide these icons. Let me know what you think about it. > btw, sometime before 3.2 or 3.1 i implemented the ability to hide/autohide > icons in the system tray. but because the systray doesn't actually control > the icons they would re-appear at the controling application's whim and do > other crazy and annoying things. Do you have an example which causes this ? Maybe at least we could make sure kde apps don't do such crazy things and disable icon hidding for other apps ? It would still be useful for me as 90% of my systray apps are kde apps and I guess I'm not the only one. David ------=_Part_2675_24497794.1098466993467 Content-Type: text/x-patch; name=systray_hideicons.diff; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="systray_hideicons.diff" Index: systemtrayapplet.cpp =================================================================== RCS file: /home/kde/kdebase/kicker/applets/systemtray/systemtrayapplet.cpp,v retrieving revision 1.49 diff -u -3 -p -r1.49 systemtrayapplet.cpp --- systemtrayapplet.cpp 13 Oct 2004 01:19:33 -0000 1.49 +++ systemtrayapplet.cpp 22 Oct 2004 17:13:08 -0000 @@ -35,6 +35,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE #include #include #include +#include +#include +#include #include "systemtrayapplet.h" #include "systemtrayapplet.moc" @@ -46,8 +49,9 @@ extern "C" KPanelApplet* init(QWidget *parent, const QString& configFile) { KGlobal::locale()->insertCatalogue("ksystemtrayapplet"); + int options = KPanelApplet::Preferences; return new SystemTrayApplet(configFile, KPanelApplet::Normal, - 0, parent, "ksystemtrayapplet"); + options, parent, "ksystemtrayapplet"); } } @@ -58,8 +62,18 @@ SystemTrayApplet::SystemTrayApplet(const hideFrameTimer(0) { loadSettings(); + m_visibleWins = NULL; - m_Wins.setAutoDelete(true); + expandButton = new KPushButton(this, "expandButton"); + expandButton->hide(); + expandButton->setIconSet( KGlobal::iconLoader()->loadIconSet("1leftarrow", KIcon::NoGroup, + KIcon::SizeSmall, false) ); + expandButton->resize(24, 24); + showHidden = false; + connect( expandButton, SIGNAL( clicked() ), this, SLOT( toggleExpanded() ) ); + + m_shownWins.setAutoDelete(true); + m_hiddenWins.setAutoDelete(true); setBackgroundOrigin(AncestorOrigin); kwin_module = new KWinModule(this); @@ -72,7 +86,10 @@ SystemTrayApplet::SystemTrayApplet(const existing = true; } if (existing) + { + updateVisibleWins(); layoutTray(); + } // the KWinModule notifies us when tray windows are added or removed connect( kwin_module, SIGNAL( systemTrayWindowAdded(WId) ), @@ -123,7 +140,9 @@ SystemTrayApplet::SystemTrayApplet(const SystemTrayApplet::~SystemTrayApplet() { - m_Wins.clear(); + delete m_visibleWins; + m_shownWins.clear(); + m_hiddenWins.clear(); KGlobal::locale()->removeCatalogue("ksystemtrayapplet"); } @@ -135,9 +154,8 @@ bool SystemTrayApplet::x11Event( XEvent if ( e->type == ClientMessage ) { if ( e->xclient.message_type == net_system_tray_opcode && e->xclient.data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) { - for (QXEmbed* emb = m_Wins.first(); emb != 0L; emb = m_Wins.next()) - if( emb->embeddedWinId() == (WId)e->xclient.data.l[2] ) // we already manage it - return true; + if( isWinManaged( (WId)e->xclient.data.l[2] ) ) // we already manage it + return true; embedWindow( e->xclient.data.l[2], false ); layoutTray(); emit updateLayout(); @@ -174,6 +192,75 @@ void SystemTrayApplet::leaveEvent( QEven } } +void SystemTrayApplet::preferences() +{ + KDialogBase dialog(this, "systrayconfig", false, i18n("Configure"), KDialogBase::Ok | KDialogBase::Cancel); + KActionSelector selector(&dialog, "systrayselector"); + dialog.setMainWidget(&selector); + + selector.setAvailableLabel(i18n("&Show:")); + selector.setSelectedLabel(i18n("&Hide:")); + selector.setSelectedInsertionPolicy(KActionSelector::AtBottom); + selector.setShowUpDownButtons(false); + selector.setMinimumSize(300, 300); + + QListBox *shownListBox = selector.availableListBox(); + QListBox *hiddenListBox = selector.selectedListBox(); + + for (QXEmbed* emb = m_shownWins.first(); emb != 0L; emb = m_shownWins.next()) + { + QString name = getWinName(emb->embeddedWinId()); + if(!shownListBox->findItem(name, Qt::ExactMatch | Qt::CaseSensitive)) + shownListBox->insertItem(name); + } + for (QXEmbed* emb = m_hiddenWins.first(); emb != 0L; emb = m_hiddenWins.next()) + { + QString name = getWinName(emb->embeddedWinId()); + if(!hiddenListBox->findItem(name, Qt::ExactMatch | Qt::CaseSensitive)) + hiddenListBox->insertItem(name); + } + + if(dialog.exec() == QDialog::Accepted) + { + KConfig *conf = config(); + conf->setGroup("HiddenTrayIcons"); + QString name; + for(uint i = 0; (name = shownListBox->text(i)) != QString::null; i++) + conf->writeEntry(name, false); + for(uint i = 0; (name = hiddenListBox->text(i)) != QString::null; i++) + conf->writeEntry(name, true); + + for(TrayEmbed* emb = m_shownWins.first(); emb != 0L; ) + if(shouldHide(emb->embeddedWinId())) + { + emb = m_shownWins.take(); // Next item becomes current + m_hiddenWins.append(emb); + emb = m_shownWins.current(); + } + else + emb = m_shownWins.next(); + + for(TrayEmbed* emb = m_hiddenWins.first(); emb != 0L; ) + if(!shouldHide(emb->embeddedWinId())) + { + emb = m_hiddenWins.take(); // Next item becomes current + m_shownWins.append(emb); + emb = m_hiddenWins.current(); + } + else + emb = m_hiddenWins.next(); + + if(m_hiddenWins.isEmpty()) + expandButton->hide(); + else + expandButton->show(); + + updateVisibleWins(); + layoutTray(); + emit updateLayout(); + } +} + void SystemTrayApplet::checkFrameVisibility() { if (!geometry().contains( mapFromGlobal( QCursor::pos() ) )) @@ -220,10 +307,10 @@ void SystemTrayApplet::loadSettings() void SystemTrayApplet::systemTrayWindowAdded( WId w ) { - for (QXEmbed* emb = m_Wins.first(); emb != 0L; emb = m_Wins.next()) - if( emb->embeddedWinId() == w ) // we already manage it - return; + if( isWinManaged( w ) ) // we already manage it + return; embedWindow( w, true ); + updateVisibleWins(); layoutTray(); emit updateLayout(); @@ -259,26 +346,118 @@ void SystemTrayApplet::embedWindow( WId return; } - m_Wins.append(emb); connect(emb, SIGNAL(embeddedWindowDestroyed()), SLOT(updateTrayWindows())); emb->resize(24, 24); - emb->show(); + if( shouldHide( w ) ) + { + m_hiddenWins.append(emb); + expandButton->show(); + } + else + m_shownWins.append(emb); + + updateVisibleWins(); +} + +bool SystemTrayApplet::isWinManaged( WId w) +{ + for (QXEmbed* emb = m_shownWins.first(); emb != 0L; emb = m_shownWins.next()) + if( emb->embeddedWinId() == w ) // we already manage it + return true; + for (QXEmbed* emb = m_hiddenWins.first(); emb != 0L; emb = m_hiddenWins.next()) + if( emb->embeddedWinId() == w ) // we already manage it + return true; + return false; +} + +bool SystemTrayApplet::shouldHide( WId w) +{ + KConfig *conf = config(); + conf->setGroup("HiddenTrayIcons"); + + if(conf->readBoolEntry( getWinName(w), false )) + return true; + + return false; +} + +QString SystemTrayApplet::getWinName( WId w ) +{ + char *n; + XFetchName( qt_xdisplay(), w, &n); + QString name(n); + XFree(n); + return name; +} + +void SystemTrayApplet::updateVisibleWins() +{ + if(m_visibleWins) + delete m_visibleWins; + + if(!showHidden) + { + m_visibleWins = new TrayEmbedList(m_shownWins); + for (TrayEmbed* emb = m_hiddenWins.first(); emb != 0L; emb = m_hiddenWins.next()) + emb->hide(); + } + else + { + m_visibleWins = new TrayEmbedList(m_hiddenWins); + for (TrayEmbed* emb = m_shownWins.first(); emb != 0L; emb = m_shownWins.next()) + m_visibleWins->append(emb); + } + + for (TrayEmbed* emb = m_visibleWins->first(); emb != 0L; emb = m_visibleWins->next()) + emb->show(); +} + +void SystemTrayApplet::expand() +{ + showHidden = true; + expandButton->setIconSet( KGlobal::iconLoader()->loadIconSet("1rightarrow", KIcon::NoGroup, + KIcon::SizeSmall, false) ); + updateVisibleWins(); + layoutTray(); + emit updateLayout(); +} + +void SystemTrayApplet::retract() +{ + showHidden = false; + expandButton->setIconSet( KGlobal::iconLoader()->loadIconSet("1leftarrow", KIcon::NoGroup, + KIcon::SizeSmall, false) ); + updateVisibleWins(); + layoutTray(); + emit updateLayout(); } void SystemTrayApplet::updateTrayWindows() { - TrayEmbed* emb = m_Wins.first(); - while ((emb = m_Wins.current()) != 0L) { + TrayEmbed* emb = m_shownWins.first(); + while ((emb = m_shownWins.current()) != 0L) { WId wid = emb->embeddedWinId(); if ((wid == 0) || ( emb->kdeTray() && !kwin_module->systemTrayWindows().contains(wid))) { // emb->setMapAfterRelease( false ); // don't map, they went away on their own - m_Wins.remove(emb); + m_shownWins.remove(emb); } else - m_Wins.next(); + m_shownWins.next(); } + emb = m_hiddenWins.first(); + while ((emb = m_hiddenWins.current()) != 0L) { + WId wid = emb->embeddedWinId(); + if ((wid == 0) + || ( emb->kdeTray() && !kwin_module->systemTrayWindows().contains(wid))) + m_hiddenWins.remove(emb); + else + m_hiddenWins.next(); + } + if(m_hiddenWins.isEmpty()) + expandButton->hide(); + updateVisibleWins(); layoutTray(); emit updateLayout(); } @@ -286,7 +465,15 @@ void SystemTrayApplet::updateTrayWindows int SystemTrayApplet::maxIconWidth() const { int largest = 24; - for (TrayEmbedList::const_iterator emb = m_Wins.begin(); emb != m_Wins.end(); ++emb) + + if( expandButton->isVisible() ) + { + int width = expandButton->size().width(); + if (width > largest) + largest = width; + } + + for (TrayEmbedList::const_iterator emb = m_visibleWins->begin(); emb != m_visibleWins->end(); ++emb) { if (*emb == 0) { @@ -306,7 +493,15 @@ int SystemTrayApplet::maxIconWidth() con int SystemTrayApplet::maxIconHeight() const { int largest = 24; - for (TrayEmbedList::const_iterator emb = m_Wins.begin(); emb != m_Wins.end(); ++emb) + + if( expandButton->isVisible() ) + { + int height = expandButton->size().height(); + if (height > largest) + largest = height; + } + + for (TrayEmbedList::const_iterator emb = m_visibleWins->begin(); emb != m_visibleWins->end(); ++emb) { if (*emb == 0) { @@ -326,6 +521,9 @@ int SystemTrayApplet::maxIconHeight() co int SystemTrayApplet::widthForHeight(int h) const { int iconWidth = maxIconWidth(), iconHeight = maxIconHeight(); + int iconCount = m_visibleWins->count(); + if( expandButton->isVisible() ) + iconCount++; if (h < iconHeight) { @@ -333,7 +531,7 @@ int SystemTrayApplet::widthForHeight(int h = iconHeight; } - int ret = ( ( ( m_Wins.count() - 1 ) / ( h / iconHeight ) ) + 1 ) * iconWidth + 4; + int ret = ( ( ( iconCount - 1 ) / ( h / iconHeight ) ) + 1 ) * iconWidth + 4; if (ret < iconWidth + 4) { @@ -346,13 +544,16 @@ int SystemTrayApplet::widthForHeight(int int SystemTrayApplet::heightForWidth(int w) const { int iconWidth = maxIconWidth(), iconHeight = maxIconHeight(); + int iconCount = m_visibleWins->count(); + if( expandButton->isVisible() ) + iconCount++; if (w < iconWidth) { // avoid div by 0 later w = iconWidth; } - int ret = ( ( ( m_Wins.count() - 1 ) / ( w / iconWidth ) ) + 1 ) * iconHeight + 4; + int ret = ( ( ( iconCount - 1 ) / ( w / iconWidth ) ) + 1 ) * iconHeight + 4; if (ret < iconHeight + 4) { @@ -369,7 +570,11 @@ void SystemTrayApplet::resizeEvent( QRes void SystemTrayApplet::layoutTray() { - if (m_Wins.count() == 0) + int iconCount = m_visibleWins->count(); + if( expandButton->isVisible() ) + iconCount++; + + if (iconCount == 0) return; /* heightWidth = height or width in pixels (depends on orientation()) @@ -388,7 +593,14 @@ void SystemTrayApplet::layoutTray() heightWidth = heightWidth < iconWidth ? iconWidth : heightWidth; // to avoid nbrOfLines=0 nbrOfLines = heightWidth / iconWidth; spacing = ( heightWidth - iconWidth*nbrOfLines ) / ( nbrOfLines + 1 ); - for (emb = m_Wins.first(); emb != 0L; emb = m_Wins.next()) { + if( expandButton->isVisible() ) { + line = i % nbrOfLines; + expandButton->move( spacing*(line+1) + line*iconWidth, 2 + col*iconHeight ); + if ( line + 1 == nbrOfLines ) + col++; + i++; + } + for (emb = m_visibleWins->first(); emb != 0L; emb = m_visibleWins->next()) { line = i % nbrOfLines; emb->move( spacing*(line+1) + line*iconWidth, 2 + col*iconHeight ); if ( line + 1 == nbrOfLines ) @@ -401,7 +613,14 @@ void SystemTrayApplet::layoutTray() heightWidth = heightWidth < iconHeight ? iconHeight : heightWidth; // to avoid nbrOfLines=0 nbrOfLines = heightWidth / iconHeight; spacing = ( heightWidth - iconHeight*nbrOfLines ) / ( nbrOfLines + 1 ); - for (emb = m_Wins.first(); emb != 0L; emb = m_Wins.next()) { + if( expandButton->isVisible() ) { + line = i % nbrOfLines; + expandButton->move( 2 + col*iconWidth, spacing*(line+1) + line*iconHeight ); + if ( line + 1 == nbrOfLines ) + col++; + i++; + } + for (emb = m_visibleWins->first(); emb != 0L; emb = m_visibleWins->next()) { line = i % nbrOfLines; emb->move( 2 + col*iconWidth, spacing*(line+1) + line*iconHeight ); if ( line + 1 == nbrOfLines ) @@ -414,12 +633,20 @@ void SystemTrayApplet::layoutTray() void SystemTrayApplet::paletteChange(const QPalette & /* oldPalette */) { - for (QXEmbed* emb = m_Wins.first(); emb != 0L; emb = m_Wins.next()) { + for (QXEmbed* emb = m_shownWins.first(); emb != 0L; emb = m_shownWins.next()) { emb->hide(); emb->show(); } } +void SystemTrayApplet::toggleExpanded() +{ + if(showHidden) + retract(); + else + expand(); +} + TrayEmbed::TrayEmbed( bool kdeTray, QWidget* parent ) : QXEmbed( parent ), kde_tray( kdeTray ) { Index: systemtrayapplet.h =================================================================== RCS file: /home/kde/kdebase/kicker/applets/systemtray/systemtrayapplet.h,v retrieving revision 1.26 diff -u -3 -p -r1.26 systemtrayapplet.h --- systemtrayapplet.h 13 Oct 2004 01:19:33 -0000 1.26 +++ systemtrayapplet.h 22 Oct 2004 17:13:08 -0000 @@ -27,11 +27,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE #include #include #include +#include #include #include -#include class QTimer; class KWinModule; @@ -62,6 +62,7 @@ protected: bool x11Event( XEvent *e ); void enterEvent( QEvent * ); void leaveEvent( QEvent * ); + void preferences(); protected slots: void systemTrayWindowAdded( WId ); @@ -69,15 +70,26 @@ protected slots: void layoutTray(); void paletteChange(const QPalette & /* oldPalette */); void checkFrameVisibility(); + void toggleExpanded(); private: void embedWindow( WId w, bool kde_tray ); - TrayEmbedList m_Wins; + bool isWinManaged( WId w); + bool shouldHide( WId w); + QString getWinName( WId w ); + void updateVisibleWins(); + void expand(); + void retract(); + TrayEmbedList* m_visibleWins; + TrayEmbedList m_shownWins; + TrayEmbedList m_hiddenWins; KWinModule *kwin_module; Atom net_system_tray_selection; Atom net_system_tray_opcode; bool showFrame; QTimer* hideFrameTimer; + bool showHidden; + KPushButton *expandButton; }; class TrayEmbed : public QXEmbed ------=_Part_2675_24497794.1098466993467 Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: inline >> Visit http://mail.kde.org/mailman/listinfo/kde-devel#unsub to unsubscribe << ------=_Part_2675_24497794.1098466993467--