[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