mouseover detection in NSTableView's NSCell? - objective-c

I am wanting to change the text background color on a tableview's cell when it is hovered upon, similar to how AddressBook "highlights" the label of a contact's element when you mouseover the label names. However I cannot figure out how to accomplish...
detecting a mouseover on a particular NSCell and...
After detecting the cell his hovered upon, highlighting the text in that cell (not highlighting the entire row as if the user selected that row)
As NSCell is not a subclass of NSView this seems to be a very difficult task.
Any example of this or explanation on how this might be done would be greatly appreciated.
Thanks!

I actually got it working using another method. I got it from the example posted here... http://www.cocoadev.com/index.pl?NSTableViewRollover
https://web.archive.org/web/20111013060111/http://cocoadev.com/index.pl?NSTableViewRollover
Instead of using NSCell's tracking mechanism, I am tracking mouseEntered/mouseExited and mouseMoved within my subclassed NSTableView.
When the tableview awakeFromNib method is called, I create a trackingRect from the visible portion of the tableview
I have a BOOL ivar that is set to YES when the mouse is within the tracking area(mouseEntered) and NO when it is not (mouseExited)
Within the mouseMoved method, I determine the current row the mouse cursor is on and set it to an NSInteger ivar and then call the tableview's setNeedsDisplayInRect: passing the rect of the row that the mouse is on.
I also override resetCursorRects to remove the old tracking rect and add a new one...this method is called when the tableview is scrolled upon so that it's tracking the latest visible rect.
Finally in my tableview's delegate, I determine the selected row (by retrieving the row index from the NSInteger ivar of the table view and change the cell's text color (or anything you want) if the currently drawn cell matches the row the mouse cursor is on. All this is done in the delegate method: tableView:(NSTableView *)aTableView willDisplayCell:(id)aCell forTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
I hope this helps others, as this was a bit tricky. It is also probably important to make sure that tableview is the firstResponder when the view loads, just makes things a bit more streamlined and cleaner.
Btw, is there a way to make a specific control in a view always be the firstResponder with nothing else possible as being the firstResponder? Even a method such as the iPhones... viewWillAppear method will help as I could set the first responder each time the view is visible...but i'm not aware of such a method on the Mac.

Overall, it's not a simple task as you noticed.
To track the mouse in an NSCell, subclass NSCell and override
-[NSCell startTrackingAt:inView:]
and
-[NSCell stopTracking:at:inView:mouseIsUp:]
Once you've detected the mouse is tracking inside a cell, you can find out which cell you are in the table with [tableView rowAtPoint:point] and [tableView columnAtPoint:point], and then find your frame with [tableView frameOfCellAtColumn:column row:row]
Then, you can change the way your cell is drawn by changing some property of the cell or changing the way it's drawn directly by overriding drawInteriorWithFrame:inView:.
Here's documentation on subclassing NSCell:
http://developer.apple.com/mac/library/documentation/cocoa/conceptual/ControlCell/Tasks/SubclassingNSCell.html

I achieved something similar by making use of addGlobalMonitorForEventsMatchingMask: handler: of NSEvent within my NSTableView subclass for the NSMouseMovedMask. using this along with columnAtPoint and rowAtPoint of NSTableView I was able to figure out if the position of the mouse was within a given cell.
Using this information I was able to bring up a PopOver when the mouse was over a particular cell.

Related

Expansion for a view-based NSTableView

I have a view-based NSTableView for which some cells have enough content that it doesn't fit. I would like to be able to have the cell automatically expand when a user hovers their cursor over the cell.
In searching for this, it seems that this is the default behavior for a cell-based NSTableView. There are even methods like shouldShowCellExpansionForTableColumn, which the doc says is for cell-based table views only. Somewhere else in the doc implies that this expansion behavior is even on by default for cell-based table views?
- (BOOL)tableView:(NSTableView *)tableView shouldShowCellExpansionForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
I get the impression that I'm missing something obvious.
Goal:
Be able to put multiple rows of NSTextField objects in a view-based cell (more than there is pace to handle)
If the content overflows, put a visual indicator into the cell
When the user does a tool-tip style hover on the cell, expand the view to show all the content
You seem to be on the right track as this should work for cell based NSTableViews. You need to put tableView:shouldShowCellExpansionForTableColumn:row: in the tableView's delegate. You could reply YES for the column of interest.
With a view based table where you use NSTextFields these scroll, truncate or wrap but there is not an expand on hover option. It is possible to set the tooltip text to be the same as the content which might be a reasonable solution.
Did you try to change the row height (tableView:heightOfRow:) triggered by some mouse action? You might have to reload the tableview.

Drawing table view cell when drag and drop

I have dragged an item into my tableview object.
When the dragged item hover over an item in tableview,
the item is redrawn with selection background. The image is as under
The row of the tableview is not selected, when i checked selectedRow method.
My requirement is when an item hover any item i should control its selection
and the background selection thereof.
Thanks,
iSight
To 'control' the selection of a TableViewCell when 'hovering' over such an item you will have to call this method:
- (NSIndexPath *)indexPathForRowAtPoint:(CGPoint)point
It's a method from the UITableView Class and uses the local coordinate system of your tableView. As is mentioned in the Apple Doc: http://developer.apple.com/library/ios/#documentation/uikit/reference/UITableView_Class/Reference/Reference.html
When the method is called you will have a NSIndexPath returned. With it you can select the cell/item at that particular path.
Mind, selecting a path manually doesn't call the didSelectRowAtIndexPath delegate-method so if you want a certain method called from that delegate you will have to do that manually as well!
This method is prolly called from the touchedDidMove method (where you also do the drag&drop) so getting the point needed for the method shouldn't be a problem for you.
You will figure it out :)
Good luck.

Adding properties to UIControls without subclassing

I have embedded UIButtons in my TableViewCells. In order to track which cell the button belongs to, I would like to add an NSIndexPath property to UIButton. I do not want to subclass a UIButton. Is there a way I can do this with categories?
EDIT: I believe the idea of setting tags will not work if I have multiple sections in the table view. Another approach of accessing the button's superview's superview to determine the cell seems more like a hack. Looking for a cleaner way of doing this.
There are several approaches. If you just really need a single number rather than a full index path, you can use setTag:. It's inflexible, but it's readily available.
The next best solution is associative references. These can hang data onto any object, with proper memory management. I usually create a category that adds a property implemented using objc_setAssociatedObject and objc_getAssociatedObject.
You can find out which cell a clicked button was in without having to associate tags or index paths with the buttons, which can get messy when re-using cells. Tags do work with sectioned tables but you have to mess about with numbers, e.g. 10001 is section 1, row 1, so you've got to convert at one end and convert back at the other, too fragile for my liking.
A nicer method is to use UITableView's indexPathForRowAtPoint: method. When your button's action is activated (assuming the target is in your table view controller:
CGPoint point = [sender convertPoint:sender.center toView:self.tableView];
NSIndexPath *path = [self.tableView indexPathForRowAtPoint:point];
sender here is cast to UIButton.
In order to track which cell the button belongs to
You already know which cell the button belongs to: it's the cell that the button is a subview of.
Start with a reference to the button. (If you are the button, this is self. If you're the button's target for its action, then when the button is tapped and emits an action message, it passes itself along as parameter to the action message, usually called sender.)
Look up the view hierarchy (superview) to find the UIViewTableCell containing the button.
UIView* v = theButton;
while (![v isKindOfClass: [UITableViewCell class]]) v = v.superview;
Now you can do whatever you like with that info. For example, you seem to want an NSIndexPath; then call -[UITableView indexPathForCell:].

Force redraw as resizing NSTableColumn in NSTableView?

I've implemented - (CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row in my NSTableView's delegate to resize the height of my table's rows as the width of the leftmost column changes. The problem is that only that column redraws during the resizing (and any column that slide into view during the resize).
This results in the funky visual below after resizing. What I'd like is a way to tell the table view to completely redraw while the user is resizing a column. Right now the most I've been able to do is call setNeedsDisplay after that column finishes resizing.
Check out the NSTableView method noteHeightOfRowsWithIndexesChanged:. You'll need to make an NSIndexSet of the rows whose heights have changed.
update:
In order to have a safe place to call this, you can subclass NSTableColumn to override the setWidth: method. You can then post a custom notification that your table delegate can observe, or you can override the table view also and have the column tell the table to tell its delegate directly.
It may not be the only way, but giving the table view a Core Animation layer fixed it. It's not 100% smooth, but the average user would probably never notice.

NSTextFieldCell Coordinates

I'm trying to get the NSPoint cooridnates of an NSTextFieldCell, but NSTextFieldCell doesn't inherit from NSView, and therefore doesn't have the frame method. Any ideas on how to get around this?
I'm trying to use Matt Gemmel's MAAttachedWindow to attach little helper popups to certain elements. I want to attach it to an area on the window, and other than hacking it together by manually messing with the x and y coordinates, I'm not sure how to get the coordinates.
You can call [theCell controlView] to get a reference to the control that owns a particular cell. If the NSTextFieldCell object is part of a simple control, such as an NSTextField, the following should be sufficient:
NSRect cellFrame = [[theCell controlView] frame];
NSPoint origin = cellFrame.origin;
//..
If, however, the NSTextFieldCell is part of a more complex control, such as an NSTableView, where a single cell is used in multiple places, you will need more information in order to determine the proper rectangle. NSCell offers the method representedObject, which can help you to determine which object in the NSTableView is represented by the cell at that particular moment. Without knowing more about your specific case, I don't know how much more detail to provide in that regard.
Here is one possible solution, assuming you are able to discern the row and column information from the object stored in representedObject:
NSTableView * tableView = [theCell controlView];
id cellObject = [theCell representedObject];
NSInteger row = //... determine from representedObject
NSInteger col = //... determine from representedObject
NSRect cellFrame = [tableView frameOfCellAtColumn:col row:row];
A cell is a reusable object that's owned by an NSControl (an NSView subclass). It doesn't have an origin point because it doesn't actually represent a place on the screen, it's up to the control (which has a specific frame rectangle) to draw it where and when it's needed. Think about a table view for example, it might use a single cell that's re-drawn in several places for each row.
The cell is passed a frame rectangle by the control when it's drawn onto the screen, in most cases that should be all you need. You can also take advantage of NSCell's sizing methods, which can tell you how much room it needs to display its content.
For using MAAttachedWindow, could you instead use the control's frame (if it's a single-cell control), or in the case of a table view with a specific row one of the NSTableView layout methods? rectOfRow: or rectOfColumn:, for example. You could override NSTextFieldCell and position the window in one of the drawing methods, but I'd save that for my absolute last choice if possible.
It doesn't have co-ordinates. There are none to get. The way cells work is that the view tells the cell “draw here”.
So, if you're not in one of the cell's drawing methods and you really, really need co-ordinates, you need to ask the view that owns the cell. Usually, this is [cell controlView].
(Perhaps you're thinking of UIKit? UITableViewCells are very different from NSCells.)