[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kwin
Subject:    [Kwin] PATCH: KStep titlebar button placement
From:       Keunwoo Lee <klee () cs ! washington ! edu>
Date:       2002-04-23 18:59:30
[Download RAW message or body]

Problem:
  1. KStep is the only KWin style that follows Fitts's Law (buttons are 
     active to the edge of the screen).
  2. KStep does not honor custom titlebar button placement.
  3. Hence: I cannot stand any decorations other than KStep's, but I 
     cannot get rid of the sticky button, which annoys me.

Solution:
  Attached patch to make KStep honor custom titlebar buttons.  This 
  patch should be run within kwin/clients/kstep/

Let me know if there are bugs, or if you need anything else from me.

BTW for the help bitmap, I copied the question mark from the default
window style.  For the maximize bitmap, none of the existing buttons from
other styles was suitable (for one thing, KStep's minimize button looks a
lot like typical maximize buttons).  So, I drew one up quickly.  I like
the one I chose---I think it's clean and fits well with the other KStep
window buttons---but feedback is welcome.  Some alternate button designs
are shown in the comments.

~k.lee

(CC: me on replies, please; I'm not subscribed to the list.)


["kstep-window-buttons.diff" (TEXT/PLAIN)]

? debug.log
? kstep-window-buttons.diff
? nextclient-klee.cpp
? nextclient-klee.h
Index: nextclient.cpp
===================================================================
RCS file: /home/kde/kdebase/kwin/clients/kstep/nextclient.cpp,v
retrieving revision 1.18
diff -u -3 -p -c -r1.18 nextclient.cpp
*** nextclient.cpp	30 Sep 2001 20:54:35 -0000	1.18
--- nextclient.cpp	23 Apr 2002 18:43:24 -0000
***************
*** 1,11 ****
  #include "nextclient.h"
  #include <qabstractlayout.h>
! #include <qlayout.h>
  #include <qdrawutil.h>
  #include <qpainter.h>
- #include <kpixmapeffect.h>
- #include <klocale.h>
  #include <qbitmap.h>
  #include "../../workspace.h"
  #include "../../options.h"
  
--- 1,13 ----
  #include "nextclient.h"
  #include <qabstractlayout.h>
! #include <qdatetime.h>
  #include <qdrawutil.h>
+ #include <qlayout.h>
  #include <qpainter.h>
  #include <qbitmap.h>
+ #include <kdebug.h>
+ #include <klocale.h>
+ #include <kpixmapeffect.h>
  #include "../../workspace.h"
  #include "../../options.h"
  
*************** static unsigned char iconify_bits[] = {
*** 21,26 ****
--- 23,32 ----
    0xff, 0x03, 0xff, 0x03, 0xff, 0x03, 0xff, 0x03, 0x03, 0x03, 0x03, 0x03,
    0x03, 0x03, 0x03, 0x03, 0xff, 0x03, 0xff, 0x03};
  
+ static unsigned char question_bits[] = {
+   0x00, 0x00, 0x78, 0x00, 0xcc, 0x00, 0xc0, 0x00, 0x60, 0x00, 0x30, 0x00,
+   0x00, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00};
+ 
  static unsigned char sticky_bits[] = {
    0x00, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0xfe, 0x01, 0xfe, 0x01,
    0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00};
*************** static unsigned char unsticky_bits[] = {
*** 29,34 ****
--- 35,71 ----
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x01, 0xfe, 0x01,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
  
+ static unsigned char maximize_bits[] = {
+    0x30, 0x00, 0x78, 0x00, 0xfc, 0x00, 0xfe, 0x01, 0x00, 0x00, 0xfe, 0x01,
+    0x02, 0x01, 0x84, 0x00, 0x48, 0x00, 0x30, 0x00 };
+ 
+ // If the maximize graphic above (which I did quickly in about a
+ // minute, just so I could have something) doesn't please, maybe one
+ // of the following would be better.  IMO it doesn't matter, as long
+ // as it's not offensive---people will get used to whatever you use.
+ // True NeXT fans won't turn on the maximize button anyway.
+ //
+ // static unsigned char maximize_bits[] = {
+ //    0xcf, 0x03, 0x87, 0x03, 0xcf, 0x03, 0xfd, 0x02, 0x48, 0x00, 0x48, 0x00,
+ //    0xfd, 0x02, 0xcf, 0x03, 0x87, 0x03, 0xcf, 0x03 };
+ //
+ // static unsigned char maximize_bits[] = {
+ //    0xcf, 0x03, 0x87, 0x03, 0x87, 0x03, 0x79, 0x02, 0x48, 0x00, 0x48, 0x00,
+ //    0x79, 0x02, 0x87, 0x03, 0x87, 0x03, 0xcf, 0x03 };
+ //
+ // static unsigned char maximize_bits[] = {
+ //    0x87, 0x03, 0x03, 0x03, 0xfd, 0x02, 0x84, 0x00, 0x84, 0x00, 0x84, 0x00,
+ //    0x84, 0x00, 0xfd, 0x02, 0x03, 0x03, 0x87, 0x03 };
+ //
+ // static unsigned char maximize_bits[] = {
+ //    0x30, 0x00, 0x78, 0x00, 0xcc, 0x00, 0x86, 0x01, 0x33, 0x03, 0x79, 0x02,
+ //    0xcd, 0x02, 0x87, 0x03, 0x03, 0x03, 0x01, 0x02 };
+ //
+ // static unsigned char maximize_bits[] = {
+ //    0x30, 0x00, 0x78, 0x00, 0x78, 0x00, 0xfc, 0x00, 0xfc, 0x00, 0xfe, 0x01,
+ //    0xfe, 0x01, 0xff, 0x03, 0xff, 0x03, 0xff, 0x03 };
+ 
+ 
  static KPixmap *aTitlePix=0;
  static KPixmap *iTitlePix=0;
  static KPixmap *aFramePix=0;
*************** static void delete_pixmaps()
*** 168,187 ****
      pixmaps_created = false;
  }
  
- void NextClient::slotReset()
- {
-     button[0]->reset();
-     button[1]->reset();
-     button[2]->reset();
- }
- 
  NextButton::NextButton(Client *parent, const char *name,
                         const unsigned char *bitmap, int bw, int bh,
                         const QString& tip)
!     : KWinButton(parent, name, tip)
  {
      setBackgroundMode( NoBackground );
-     client = parent;
      resize(18, 18);
  
      if(bitmap)
--- 205,217 ----
      pixmaps_created = false;
  }
  
  NextButton::NextButton(Client *parent, const char *name,
                         const unsigned char *bitmap, int bw, int bh,
                         const QString& tip)
!     : KWinButton(parent, name, tip),
!       deco(NULL), client(parent), last_button(NoButton)
  {
      setBackgroundMode( NoBackground );
      resize(18, 18);
  
      if(bitmap)
*************** void NextButton::reset()
*** 195,202 ****
  
  void NextButton::setBitmap(const unsigned char *bitmap, int w, int h)
  {
!     deco = QBitmap(w, h, bitmap, true);
!     deco.setMask(deco);
      repaint();
  }
  
--- 225,232 ----
  
  void NextButton::setBitmap(const unsigned char *bitmap, int w, int h)
  {
!     deco = new QBitmap(w, h, bitmap, true);
!     deco->setMask(*deco);
      repaint();
  }
  
*************** void NextButton::drawButton(QPainter *p)
*** 207,216 ****
      else
          p->drawPixmap(0, 0, isDown() ? *iBtnDown : *iBtn);
  
!     p->setPen(*btnForeground);
!     p->drawPixmap(isDown()? 5 : 4, isDown() ? 5 : 4, deco);
  }
  
  NextClient::NextClient( Workspace *ws, WId w, QWidget *parent,
                              const char *name )
      : Client( ws, w, parent, name, WResizeNoErase )
--- 237,270 ----
      else
          p->drawPixmap(0, 0, isDown() ? *iBtnDown : *iBtn);
  
!     // If we have a decoration, draw it; otherwise, we have the menu
!     // button (remember, we set the bitmap to NULL).
!     if (deco) {
!         p->setPen(*btnForeground);
!         p->drawPixmap(isDown()? 5 : 4, isDown() ? 5 : 4, *deco);
!     } else {
!         KPixmap btnpix = client->miniIcon();
!         p->drawPixmap( 0, 0, btnpix );
!     }
! }
! 
! void NextButton::mousePressEvent( QMouseEvent* e )
! {
!     last_button = e->button();
!     QMouseEvent me( e->type(), e->pos(), e->globalPos(),
!                     LeftButton, e->state() );
!     KWinButton::mousePressEvent( &me );
  }
  
+ void NextButton::mouseReleaseEvent( QMouseEvent* e )
+ {
+     last_button = e->button();
+     QMouseEvent me( e->type(), e->pos(), e->globalPos(),
+                     LeftButton, e->state() );
+     KWinButton::mouseReleaseEvent( &me );
+ }
+ 
+ 
  NextClient::NextClient( Workspace *ws, WId w, QWidget *parent,
                              const char *name )
      : Client( ws, w, parent, name, WResizeNoErase )
*************** NextClient::NextClient( Workspace *ws, W
*** 230,255 ****
      windowLayout->addWidget(windowWrapper(), 1);
      windowLayout->addSpacing(1);
  
  
!     button[0] = new NextButton(this, "close", close_bits, 10, 10, i18n("Close"));
!     button[1] = new NextButton(this, "sticky", NULL, 0, 0, i18n("Sticky"));
!     stickyChange(isSticky());
!     button[2] = new NextButton(this, "iconify", iconify_bits, 10, 10, i18n("Minimize"));
! 
!     connect( button[0], SIGNAL( clicked() ), this, ( SLOT( closeWindow() ) ) );
!     connect( button[1], SIGNAL( clicked() ), this, ( SLOT( toggleSticky() ) ) );
!     connect( button[2], SIGNAL( clicked() ), this, ( SLOT( iconify() ) ) );
!     titleLayout->addWidget( button[2] );
      titlebar = new QSpacerItem(10, 16, QSizePolicy::Expanding,
                                 QSizePolicy::Minimum );
      titleLayout->addItem(titlebar);
!     titleLayout->addWidget( button[1] );
!     titleLayout->addWidget( button[0] );
!     for ( int i = 0; i < 3; i++) {
!         button[i]->setMouseTracking( TRUE );
!         button[i]->setFixedSize( 18, 18 );
      }
  
  }
  
  void NextClient::resizeEvent( QResizeEvent* e)
--- 284,459 ----
      windowLayout->addWidget(windowWrapper(), 1);
      windowLayout->addSpacing(1);
  
+     initializeButtonsAndTitlebar(titleLayout);
+ }
  
! /**
!    Preconditions:
!        + this->button is an array of length MAX_NUM_BUTTONS
! 
!    Postconditions:
!        + Title bar and buttons have been initialized and laid out
!        + for all i in 0..(MAX_NUM_BUTTONS-1), button[i] points to
!          either (1) a valid NextButton instance, if the corresponding
!          button is selected in the current button scheme, or (2) null
!          otherwise.
!  */
! void NextClient::initializeButtonsAndTitlebar(QHBoxLayout* titleLayout)
! {
!     // Null the buttons to begin with (they are not guaranteed to be null).
!     for (int i=0; i<MAX_NUM_BUTTONS; i++) {
!         button[i] = NULL;
!     }
! 
!     // The default button positions for other styles do not match the
!     // behavior of older versions of KStep, so we have to set these
!     // manually when customButtonPositions isn't enabled.
!     QString left, right;
!     if (options->customButtonPositions()) {
!         left = options->titleButtonsLeft();
!         right = options->titleButtonsRight();
!     } else {
!         left = QString("I");
!         right = QString("SX");
!     }
! 
!     // Do actual creation and addition to titleLayout
!     addButtons(titleLayout, left);
      titlebar = new QSpacerItem(10, 16, QSizePolicy::Expanding,
                                 QSizePolicy::Minimum );
      titleLayout->addItem(titlebar);
!     addButtons(titleLayout, right);
! 
!     // Finally, activate all live buttons
!     for ( int i = 0; i < MAX_NUM_BUTTONS; i++) {
!         if (button[i]) {
!             button[i]->setMouseTracking( TRUE );
!             button[i]->setFixedSize( 18, 18 );
!         }
!     }
! }
! 
! /** Adds the buttons for one side of the title bar, based on the spec
!  * string; see the KWinInternal::Options class, methods
!  * titleButtonsLeft and titleBUttonsRight. */
! void NextClient::addButtons(QHBoxLayout* titleLayout, const QString& spec)
! {
!     for (unsigned int i=0; i<spec.length(); i++) {
!         switch (spec[i].latin1()) {
!         case 'A':
!             if (isMaximizable()) {
!                 button[MAXIMIZE_IDX] =
!                     new NextButton(this, "maximize", maximize_bits, 10, 10,
!                                    i18n("Maximize"));
!                 titleLayout->addWidget( button[MAXIMIZE_IDX] );
!                 connect( button[MAXIMIZE_IDX], SIGNAL(clicked()),
!                          this, SLOT(maximizeButtonClicked()) );
!                 break;
!             }
! 
!         case 'H':
!             button[HELP_IDX] =
!                 new NextButton(this, "help", question_bits, 10, 10,
!                                i18n("Help"));
!             titleLayout->addWidget( button[HELP_IDX] );
!             connect( button[HELP_IDX], SIGNAL(clicked()),
!                      this, SLOT(contextHelp()) );
!             break;
! 
!         case 'I':
!             if (isMinimizable()) {
!                 button[ICONIFY_IDX] =
!                     new NextButton(this, "iconify", iconify_bits, 10, 10,
!                                    i18n("Minimize"));
!                 titleLayout->addWidget( button[ICONIFY_IDX] );
!                 connect( button[ICONIFY_IDX], SIGNAL(clicked()),
!                          this, SLOT(iconify()) );
!                 break;
!             }
!             
!         case 'M':
!             button[MENU_IDX] =
!                 new NextButton(this, "menu", NULL, 10, 10, i18n("Menu"));
!             titleLayout->addWidget( button[MENU_IDX] );
!             // NOTE DIFFERENCE: capture pressed(), not clicked()
!             connect( button[MENU_IDX], SIGNAL(pressed()),
!                      this, SLOT(menuButtonPressed()) );
!             break;
! 
!         case 'S':
!             button[STICKY_IDX] =
!                 new NextButton(this, "sticky", NULL, 0, 0, i18n("Sticky"));
!             titleLayout->addWidget( button[STICKY_IDX] );
!             connect( button[STICKY_IDX], SIGNAL(clicked()),
!                      this, SLOT(toggleSticky()) );
!             // NOTE DIFFERENCE: set the pixmap separately (2 states)
!             stickyChange(isSticky());
!             break;
! 
!         case 'X':
!             button[CLOSE_IDX] =
!                 new NextButton(this, "close", close_bits, 10, 10,
!                                i18n("Close"));
!             titleLayout->addWidget( button[CLOSE_IDX] );
!             connect( button[CLOSE_IDX], SIGNAL(clicked()),
!                      this, SLOT(closeWindow()) );
!             break;
! 
!         case '_':
!             // TODO: Add spacer handling
!             break;
! 
!         default:
!             kdDebug() << " Can't happen: unknown button code "
!                       << QString(spec[i]);
!             break;
!         }
!     }
! }
! 
! // Make sure the menu button follows double click conventions set in kcontrol
! // (Note: this was almost straight copy and paste from KDEDefaultClient.)
! void NextClient::menuButtonPressed()
! {
!     static QTime* t = 0;
!     static NextClient* tc = 0;
!     if ( !t ) {
!         t = new QTime;
!     }
! 
!     if ( tc != this || t->elapsed() > QApplication::doubleClickInterval() )
!     {
!         // Probably don't need this null check, but we might as well.
!         if (button[MENU_IDX]) {
!             QPoint menupoint ( button[MENU_IDX]->rect().bottomLeft().x()-1,
!                                button[MENU_IDX]->rect().bottomLeft().y()+2 );
!             workspace()->clientPopup(this)->popup(
!                 button[MENU_IDX]->mapToGlobal( menupoint ));
!         }
!     } else {
!         closeWindow();
      }
  
+     t->start();
+     tc = this;
+ }
+ 
+ // Copied, with minor edits, from KDEDefaultClient::slotMaximize()
+ void NextClient::maximizeButtonClicked()
+ {
+     if (button[MAXIMIZE_IDX]) {
+         switch (button[MAXIMIZE_IDX]->lastButton()) {
+         case MidButton:
+             maximize( MaximizeVertical );
+             break;
+         case RightButton:
+             maximize( MaximizeHorizontal );
+             break;
+         default:
+             maximize();
+             break;
+         }
+     }
  }
  
  void NextClient::resizeEvent( QResizeEvent* e)
*************** void NextClient::mouseDoubleClickEvent( 
*** 325,332 ****
  
  void NextClient::stickyChange(bool on)
  {
!     button[1]->setBitmap( on ? unsticky_bits : sticky_bits, 10, 10);
!     button[1]->setTipText( on ? i18n("Un-Sticky") : i18n("Sticky") );
  }
  
  
--- 529,538 ----
  
  void NextClient::stickyChange(bool on)
  {
!     if (NextButton * b = button[STICKY_IDX]) {
!         b->setBitmap( on ? unsticky_bits : sticky_bits, 10, 10);
!         b->setTipText( on ? i18n("Un-Sticky") : i18n("Sticky") );
!     }
  }
  
  
*************** void NextClient::init()
*** 338,349 ****
  void NextClient::activeChange(bool)
  {
      repaint(false);
!     button[0]->reset();
!     button[1]->reset();
!     button[2]->reset();
  }
  
!   Client::MousePosition
  NextClient::mousePosition( const QPoint& p ) const
  {
    MousePosition m = Nowhere;
--- 544,562 ----
  void NextClient::activeChange(bool)
  {
      repaint(false);
!     slotReset();
! }
! 
! void NextClient::slotReset()
! {
!     for (int i=0; i<MAX_NUM_BUTTONS; i++) {
!         if (button[i]) {
!             button[i]->reset();
!         }
!     }
  }
  
! Client::MousePosition
  NextClient::mousePosition( const QPoint& p ) const
  {
    MousePosition m = Nowhere;
Index: nextclient.h
===================================================================
RCS file: /home/kde/kdebase/kwin/clients/kstep/nextclient.h,v
retrieving revision 1.9
diff -u -3 -p -c -r1.9 nextclient.h
*** nextclient.h	18 Aug 2001 23:51:08 -0000	1.9
--- nextclient.h	23 Apr 2002 18:43:24 -0000
***************
*** 4,9 ****
--- 4,10 ----
  #include <qvariant.h>
  #include <qbitmap.h>
  #include <kpixmap.h>
+ #include <qlayout.h>
  #include "../../client.h"
  #include "../../kwinbutton.h"
  class QLabel;
*************** public:
*** 19,30 ****
                 const QString& tip=NULL);
      void setBitmap(const unsigned char *bitmap, int bw, int bh);
      void reset();
  protected:
      virtual void drawButton(QPainter *p);
      void drawButtonLabel(QPainter *){;}
      KPixmap aBackground, iBackground;
!     QBitmap deco;
      Client *client;
  };
  
  class NextClient : public KWinInternal::Client
--- 20,37 ----
                 const QString& tip=NULL);
      void setBitmap(const unsigned char *bitmap, int bw, int bh);
      void reset();
+     ButtonState lastButton() { return last_button; }
+ 
  protected:
+     void mousePressEvent( QMouseEvent* e );
+     void mouseReleaseEvent( QMouseEvent* e );
      virtual void drawButton(QPainter *p);
      void drawButtonLabel(QPainter *){;}
+ 
      KPixmap aBackground, iBackground;
!     QBitmap* deco;
      Client *client;
+     ButtonState last_button;
  };
  
  class NextClient : public KWinInternal::Client
*************** class NextClient : public KWinInternal::
*** 32,38 ****
      Q_OBJECT
  public:
      NextClient( Workspace *ws, WId w, QWidget *parent=0, const char *name=0 );
!     ~NextClient(){;}
  protected:
      void resizeEvent( QResizeEvent* );
      void paintEvent( QPaintEvent* );
--- 39,45 ----
      Q_OBJECT
  public:
      NextClient( Workspace *ws, WId w, QWidget *parent=0, const char *name=0 );
!     ~NextClient() {;}
  protected:
      void resizeEvent( QResizeEvent* );
      void paintEvent( QPaintEvent* );
*************** protected:
*** 47,55 ****
  
  protected slots:
      void slotReset();
  private:
!     NextButton* button[3];
      QSpacerItem* titlebar;
  };                      
  
  };
--- 54,80 ----
  
  protected slots:
      void slotReset();
+     void menuButtonPressed();
+     void maximizeButtonClicked();
+ 
  private:
!     void initializeButtonsAndTitlebar(QHBoxLayout* titleLayout);
!     void addButtons(QHBoxLayout* titleLayout, const QString& buttons);
! 
      QSpacerItem* titlebar;
+ 
+     // Helpful constants for buttons in array
+     static const int CLOSE_IDX    = 0;
+     static const int HELP_IDX     = 1;
+     static const int ICONIFY_IDX  = 2;
+     static const int MAXIMIZE_IDX = 3;
+     static const int MENU_IDX     = 4;
+     static const int STICKY_IDX   = 5;
+     static const int MAX_NUM_BUTTONS = STICKY_IDX + 1;
+ 
+     // WARNING: button[i] may be null for any given i.  Make sure you
+     // always check for null before doing button[i]->foo().
+     NextButton* button[MAX_NUM_BUTTONS];
  };                      
  
  };

_______________________________________________
Kwin mailing list
Kwin@mail.kde.org
http://mail.kde.org/mailman/listinfo/kwin

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic