From 5f688fbf6e9c929580cff7fc39248e03fba1d2e0 Mon Sep 17 00:00:00 2001 From: michael Date: Fri, 1 Mar 2002 23:25:06 +0000 Subject: [PATCH] Improvements for popupbuttons. git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@12946 72102866-910b-0410-8b05-ffd578937521 --- ChangeLog | 33 ++++++++ Headers/gnustep/gui/NSMenuView.h | 1 + Source/NSMenuItemCell.m | 16 +--- Source/NSMenuView.m | 133 ++++++++++++++++++++----------- Source/NSPopUpButtonCell.m | 99 +++++++++++------------ 5 files changed, 170 insertions(+), 112 deletions(-) diff --git a/ChangeLog b/ChangeLog index c13fedba3..e2602cf6a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,36 @@ +2002-03-01 Michael Hanni + + First cut at improving NSPopUpButton/Cell. + + * Source/NSMenuItemCell.m + ([drawBorderAndBackgroundWithFrame:inView:]): Remove special + popupbutton case. + ([drawInteriorWithFrame:inView:]): ditto. + * Headers/NSMenuView.h: added _leftBorderOffset int. + * Source/NSMenuView.m: added static function + _addLeftBorderOffsetToRect() + ([initWithFrame:]): init left offset value to 1 (default for a + menu.) + ([sizeToFit]): change left offset value to 0 if we are a + popup. Use this value to set the correct frame size. Before when + the menuview was sizeToFit'ed the menuWindow was resized by an + additional pixel when we were a popupbutton. + ([innerRect]): use left offset value to return the correct rect. + ([rectOfItemAtIndex:]): ditto. + ([indexOfItemAtPoint:]): use _addLeftBorderOffsetToRect() to + correct the rect we use to calculate the event area for a + menuItemCell in a normal menu. + ([setNeedsDisplayForItemAtIndex:]): ditto. + ([drawRect:]): Modified to not draw a border line on the left side + if we are a popup. + * Source/NSPopUpButtonCell.m: added some comments for further + fixes. + ([drawWithFrame:inView:]): removed all the code in favor of + having the NSMenuItemCell of the selected item draw the + popupbuttoncell. + ([drawInteriorWithFrame:inView:]): removed everything but the + responder status code (this is still a FIXME). + 2002-03-01 Pierre-Yves Rivaille * Source/NSBundleAdditions.m diff --git a/Headers/gnustep/gui/NSMenuView.h b/Headers/gnustep/gui/NSMenuView.h index cb6588007..d45317bf6 100644 --- a/Headers/gnustep/gui/NSMenuView.h +++ b/Headers/gnustep/gui/NSMenuView.h @@ -64,6 +64,7 @@ id _items_link; BOOL _keepAttachedMenus; int _oldHighlightedItemIndex; + int _leftBorderOffset; } + (float)menuBarHeight; diff --git a/Source/NSMenuItemCell.m b/Source/NSMenuItemCell.m index d1f0ef055..40c3a87d3 100644 --- a/Source/NSMenuItemCell.m +++ b/Source/NSMenuItemCell.m @@ -371,11 +371,6 @@ static NSImage *arrowImageH = nil; [controlView lockFocus]; - if (_mcell_belongs_to_popupbutton) - { - cellFrame.origin.x--; - } - if (_cell.is_highlighted && (_highlightsByMask & NSPushInCellMask)) { NSDrawGrayBezel(cellFrame, NSZeroRect); @@ -576,16 +571,7 @@ static NSImage *arrowImageH = nil; // Set cell's background color [_backgroundColor set]; - if (_mcell_belongs_to_popupbutton) - { - cellFrame.origin.x--; - NSRectFill(cellFrame); - cellFrame.origin.x++; - } - else - { - NSRectFill(cellFrame); - } + NSRectFill(cellFrame); /* * Determine the image and the title that will be diff --git a/Source/NSMenuView.m b/Source/NSMenuView.m index 85bcb57d3..c69d20a14 100644 --- a/Source/NSMenuView.m +++ b/Source/NSMenuView.m @@ -36,6 +36,22 @@ @implementation NSMenuView +static NSRect +_addLeftBorderOffsetToRect(NSRect aRect, BOOL isHorizontal) +{ + if (isHorizontal == NO) + { + aRect.origin.x--; + aRect.size.width++; + } + else + { + aRect.origin.y--; + aRect.size.height++; + } + return aRect; +} + /* * Class methods. */ @@ -80,6 +96,12 @@ _highlightedItemIndex = -1; _horizontalEdgePad = 4.; + /* Set the necessary offset for the menuView. That is, how many pixels + * do we need for our left side border line. For regular menus this + * equals 1, for popups it is 0. + */ + _leftBorderOffset = 1; + // Create an array to store our menu item cells. _itemCells = [NSMutableArray new]; @@ -396,6 +418,17 @@ float neededStateImageWidth = 0.0; float accumulatedOffset = 0.0; + /* Set the necessary offset for the menuView. That is, how many pixels + * do we need for our left side border line. For regular menus this + * equals 1, for popups it is 0. + * + * Why here? I could not think of a better place. I figure that everyone + * should sizeToFit their popup/menu before using it so we should get + * this set properly fairly early. + */ + if ([_menu _ownedByPopUp]) + _leftBorderOffset = 0; + // TODO: Optimize this loop. for (i = 0; i < howMany; i++) { @@ -472,13 +505,13 @@ if (_horizontal == NO) { - [self setFrameSize: NSMakeSize(_cellSize.width + 1, + [self setFrameSize: NSMakeSize(_cellSize.width + _leftBorderOffset, (howMany * _cellSize.height))]; } else { [self setFrameSize: NSMakeSize((howMany * _cellSize.width), - _cellSize.height + 1)]; + _cellSize.height + _leftBorderOffset)]; } _needsSizing = NO; @@ -536,13 +569,13 @@ { if (_horizontal == NO) { - return NSMakeRect(_bounds.origin.x + 1, _bounds.origin.y, - _bounds.size.width - 1, _bounds.size.height); + return NSMakeRect(_bounds.origin.x + _leftBorderOffset, _bounds.origin.y, + _bounds.size.width - _leftBorderOffset, _bounds.size.height); } else { - return NSMakeRect(_bounds.origin.x, _bounds.origin.y + 1, - _bounds.size.width, _bounds.size.height - 1); + return NSMakeRect(_bounds.origin.x, _bounds.origin.y + _leftBorderOffset, + _bounds.size.width, _bounds.size.height - _leftBorderOffset); } } @@ -554,19 +587,29 @@ { [self sizeToFit]; } + + /* When we are a normal menu we fiddle with the origin so that the item + * rect is shifted 1 pixel over so we do not draw on the heavy line at + * origin.x = 0. However, for popups we don't want this modification of + * our rect so our _leftBorderOffset = 0 (set in sizeToFit). + */ if (_horizontal == NO) { theRect.origin.y = _bounds.size.height - (_cellSize.height * (index + 1)); - theRect.origin.x = 1; + theRect.origin.x = _leftBorderOffset; } else { theRect.origin.x = _bounds.size.width - (_cellSize.width * (index + 1)); - theRect.origin.y = 1; + theRect.origin.y = _leftBorderOffset; } theRect.size = _cellSize; + /* NOTE: This returns the correct NSRect for drawing cells, but nothing + * else (unless we are a popup). This rect will have to be modified for + * event calculation, etc.. + */ return theRect; } @@ -579,17 +622,13 @@ { NSRect aRect = [self rectOfItemAtIndex: i]; - // Add the border to the rectangle - if (_horizontal == NO) - { - aRect.origin.x--; - aRect.size.width++; - } - else - { - aRect.origin.y--; - aRect.size.height++; - } + /* We need to modify the rect to take into account the modifications + * to origin made by [-rectOfItemAtIndex:] in order to return an + * item clicked at the left hand margin. However, for a popup this + * calculation is unnecessary since we have no extra margin. + */ + if (![_menu _ownedByPopUp]) + aRect = _addLeftBorderOffsetToRect(aRect, _horizontal); if (NSMouseInRect(point, aRect, NO)) return i; @@ -600,18 +639,17 @@ - (void) setNeedsDisplayForItemAtIndex: (int)index { - NSRect aRect = [self rectOfItemAtIndex: index]; + NSRect aRect; - if (_horizontal == NO) - { - aRect.origin.x--; - aRect.size.width++; - } - else - { - aRect.origin.y--; - aRect.size.height++; - } + aRect = [self rectOfItemAtIndex: index]; + + /* We need to modify the rect to take into account the modifications + * to origin made by [-rectOfItemAtIndex:] in order to return an + * item clicked at the left hand margin. However, for a popup this + * calculation is unnecessary since we have no extra margin. + */ + if (![_menu _ownedByPopUp]) + aRect = _addLeftBorderOffsetToRect(aRect, _horizontal); [self setNeedsDisplayInRect: aRect]; } @@ -733,24 +771,29 @@ int i; int howMany = [_itemCells count]; - NSGraphicsContext *ctxt = GSCurrentContext(); + /* For popupButtons we do not want this dark line. */ - // Draw a dark gray line at the left of the menu item cells. - DPSgsave(ctxt); - DPSsetlinewidth(ctxt, 1); - DPSsetgray(ctxt, NSDarkGray); - if (_horizontal == NO) + if (![_menu _ownedByPopUp]) { - DPSmoveto(ctxt, NSMinX(_bounds), NSMinY(_bounds)); - DPSrlineto(ctxt, 0, _bounds.size.height); + NSGraphicsContext *ctxt = GSCurrentContext(); + + // Draw a dark gray line at the left of the menu item cells. + DPSgsave(ctxt); + DPSsetlinewidth(ctxt, 1); + DPSsetgray(ctxt, NSDarkGray); + if (_horizontal == NO) + { + DPSmoveto(ctxt, NSMinX(_bounds), NSMinY(_bounds)); + DPSrlineto(ctxt, 0, _bounds.size.height); + } + else + { + DPSmoveto(ctxt, NSMinX(_bounds), NSMaxY(_bounds)); + DPSrlineto(ctxt, _bounds.size.width, 0); + } + DPSstroke(ctxt); + DPSgrestore(ctxt); } - else - { - DPSmoveto(ctxt, NSMinX(_bounds), NSMaxY(_bounds)); - DPSrlineto(ctxt, _bounds.size.width, 0); - } - DPSstroke(ctxt); - DPSgrestore(ctxt); // Draw the menu cells. for (i = 0; i < howMany; i++) diff --git a/Source/NSPopUpButtonCell.m b/Source/NSPopUpButtonCell.m index 3a1d494a4..c4dac231a 100644 --- a/Source/NSPopUpButtonCell.m +++ b/Source/NSPopUpButtonCell.m @@ -485,67 +485,58 @@ static NSImage *_pbc_image[2]; * What would be nice and natural is to make this drawing using the same code * that is used to draw cells in the menu. * This looks like a mess to do in this framework. + * + * Well, here is an attempt to make this work. + */ +- (void) drawWithFrame: (NSRect)cellFrame inView: (NSView*)controlView +{ + NSMenuItemCell *aCell; + + // Save last view drawn to + if (_control_view != controlView) + _control_view = controlView; + + // Transparent buttons never draw + if (_buttoncell_is_transparent) + return; + + // Do nothing if cell's frame rect is zero + if (NSIsEmptyRect(cellFrame)) + return; + + // Do nothing if the window is deferred + if ([[controlView window] gState] == 0) + return; + + /* Get the NSMenuItemCell of the selected item */ + aCell = [[_menu menuRepresentation] menuItemCellForItemAtIndex: [self indexOfSelectedItem]]; + + /* Turn off highlighting so the NSPopUpButton looks right */ + [aCell setHighlighted: NO]; + + [aCell drawWithFrame: cellFrame inView: controlView]; + + /* Draw our own interior so we pick up our dotted frame */ + [self drawInteriorWithFrame: cellFrame inView: controlView]; + + /* Rehighlight item for consistency */ + [aCell setHighlighted: YES]; +} + +/* FIXME: This needs to be removed in favor of allowing the cell to draw + * our NSDottedRect. */ - (void) drawInteriorWithFrame: (NSRect)cellFrame inView: (NSView*)view { - NSSize size; - NSPoint position; - NSImage *anImage; - - // Save last view drawn to - if (_control_view != view) - _control_view = view; + // Transparent buttons never draw + if (_buttoncell_is_transparent) + return; [view lockFocus]; - // [super drawWithFrame: cellFrame inView: view]; - cellFrame = [self drawingRectForBounds: cellFrame]; - if (_cell.is_bordered || _cell.is_bezeled) - { - cellFrame.origin.x += 3; - cellFrame.size.width -= 6; - cellFrame.origin.y += 1; - cellFrame.size.height -= 2; - } - // Skip 5 points from left side - // cellFrame.origin.x += 5; - // cellFrame.size.width -= 5; - cellFrame.origin.x += 2; - cellFrame.size.width -= 2; - - [self _drawText: [self titleOfSelectedItem] inFrame: cellFrame]; - - cellFrame.origin.x -= 4; - cellFrame.size.width += 4; - - anImage = _pbc_image[_pbcFlags.pullsDown]; - - /* NB: If we are drawing here, then the control can't be selected */ - [anImage setBackgroundColor: [NSColor controlBackgroundColor]]; - - size = [anImage size]; - position.x = cellFrame.origin.x + cellFrame.size.width - size.width; - position.y = MAX(NSMidY(cellFrame) - (size.height/2.), 0.); - /* - * Images are always drawn with their bottom-left corner at the origin - * so we must adjust the position to take account of a flipped view. - */ - if ([view isFlipped]) - position.y += size.height; - [anImage compositeToPoint: position operation: NSCompositeCopy]; - - if (_cell.is_bordered || _cell.is_bezeled) - { - cellFrame.origin.x -= 1; - cellFrame.size.width += 2; - } - - cellFrame.origin.y -= 1; - cellFrame.size.height += 2; - cellFrame.size.width += 2; if (_cell.shows_first_responder && [[view window] firstResponder] == view) NSDottedFrameRect(cellFrame); @@ -553,6 +544,10 @@ static NSImage *_pbc_image[2]; [view unlockFocus]; } +/* FIXME: this method needs to be rewritten to be something like + * NSMenuView's sizeToFit. That way if you call [NSPopUpButton sizeToFit]; + * you will get the absolutely correct cellSize. + */ - (NSSize) cellSize { NSSize s;