The Artima Developer Community
Sponsored Link

Agile Buzz Forum
Customizing the tab widget

0 replies on 1 page.

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 0 replies on 1 page
James Robertson

Posts: 29924
Nickname: jarober61
Registered: Jun, 2003

David Buck, Smalltalker at large
Customizing the tab widget Posted: Sep 12, 2005 8:14 AM
Reply to this message Reply

This post originated from an RSS feed registered with Agile Buzz by James Robertson.
Original Post: Customizing the tab widget
Feed Title: Cincom Smalltalk Blog - Smalltalk with Rants
Feed URL: http://www.cincomsmalltalk.com/rssBlog/rssBlogView.xml
Feed Description: James Robertson comments on Cincom Smalltalk, the Smalltalk development community, and IT trends and issues in general.
Latest Agile Buzz Posts
Latest Agile Buzz Posts by James Robertson
Latest Posts From Cincom Smalltalk Blog - Smalltalk with Rants

Advertisement

With the new tab support in BottomFeeder, I thought I should explain one of the customizations - the tab menus. As it happens, the tab widget in VisualWorks doesn't support menus out of the box. So how did I add them? Well, first off, I noticed that Typeless has tab menus (Typeless also ships as a BottomFeeder plugin, which is how I use it). So, I loaded TL from the Public Store and started having a look around. It turns out that Michael extended the widget in the most natural fashion, by creating a new controller subclass. Normally, the tab bar is managed by a class called TabControlBarController. So, first thing - he created this class:


Smalltalk.UI defineClass: #TabControlBarControllerWithMenu
	superclass: #{UI.TabControlBarController}
	indexedType: #none
	private: false
	instanceVariableNames: 'menuHolder performer owner mouseDownIndex '
	classInstanceVariableNames: ''
	imports: ''
	category: 'UIBasics-Controllers'

Then you have to modify the view class, so that it uses this controller (or, at the time you create the view, replace it. This code just replaces the default controller) - here's TabControlBarView>>defaultControllerClass:


defaultControllerClass
	^TabControlBarControllerWithMenu

That makes sure that we always get menu support when we create a tab widget - assuming that the new controller class is implemented properly. Here's what's going on there:

  • First, add some new instance variables to handle menus and the state required for them: menuHolder performer owner mouseDownIndex
  • implement #yellowButtonPressedEvent: so as to know when to pop the menu
  • implement all the other supporting methods

There's a small side story in the name of #yellowButtonPressedEvent: - back in the day, at Xerox PARC, the mouse actually had colored buttons - red, yellow, and blue. Those names still exist in the code for VW (and, I think, for Squeak). Anyway - the method:


yellowButtonPressedEvent: event 
	"we about to bring up a menu, unlock the event
	queue so that we can process expose events."
	| index |
	view numberOfElements = 0 ifTrue: [^self]. 
	index := self findElementFor: (self sensor cursorPointFor: event).
	(self owner respondsTo: #adjustTabbarMenuFor:) ifTrue:
		[self owner adjustTabbarMenuFor: index].

	self sensor windowSensor queueLocked: false.
	self processMenuAt: event globalPoint centered: true.
	^nil

The reference to #adjustTabbarMenuFor: is something I missed out of the gate. I had to implement that in my class, in order to ensure that the menus were set up correctly - it looks like this:

adjustTabbarMenuFor: anIndex 
	| tabModel sub tabbed menu |
	anIndex = 0 ifTrue: [^self].
	tabModel := self browserTabs at: anIndex.
	sub := self widgetAt: #feedID.
	tabbed := self getComponentFromSubcanvas: sub withID: #TwoflowerHTML1.
	menu := self class tabMenu.
	(tabbed widget tabBar component controller)
		menuHolder: (ValueHolder with: menu);
		performer: tabModel;
		owner: self.
	self checkTabMenuEnablement: menu

All of which does the following - make sure that a new tab has a menu, and that it's attached to that menu. As well, make sure that menu items are in the right enablement state. There's a few other setup methods, but you can see all of that by browsing the package RSSExtensions in the Public Store. The next important thing was setting the tabs up properly in my UI class. In my #postOpenWith: method (which fires after the UI opens, I added the following:


self presetTabs.
tab := self changedTab.
self setupTabMenu: tab

presetTabs
	| browserTab |
	browserTabs := OrderedCollection new.
	browserTab := RSSZoomItem new.
	browserTabs add: browserTab.
	self tabs list add: browserTab displayString.
	self tabs selectionIndex: 1.
	self setupEventHandlingForTab: browserTab

changedTab
	"changed to a new tab; adjust"

	| index  browserTab sub tabbed |
	index := self tabs selectionIndex max: 1.
	browserTab := self browserTabs at: index.
	sub := self widgetAt: #feedID.
	tabbed := self getComponentFromSubcanvas: sub withID: #TwoflowerHTML1.
	tabbed widget client: self htmlModel spec: #windowSpec builder: self builder.
	(browserTab feed isNil or: [browserTab feed isFake])
		ifFalse: [self focusOnItem: browserTab item].
	self tabs selectionIndex = 0
		ifTrue: [self tabs selectionIndex: 1].
	self setCurrentTabDetails.
	self setupHTMLPane.
	self setBrowserEvents.
	self restoreHistoryFrom: browserTab.
	^browserTab

setupTabMenu: aZoomItem
	| tabbed sub menu |
	sub := self widgetAt: #feedID.
	tabbed := self getComponentFromSubcanvas: sub withID: #TwoflowerHTML1.
	menu := self class tabMenu.
	(tabbed widget tabBar component controller)
		menuHolder: (ValueHolder with: menu);
		performer: aZoomItem;
		owner: self.
	self checkTabMenuEnablement: menu

Those three methods are the heart of the support. The first one sets the tabs up - with the proper domain model (in this case, an object that holds the selected feed, the selected item, and the history), and sets the current tab index. The second method, #changedTab, fires whenever a tab is selected. It ensures that the correct stuff gets displayed, and that state stays correct. The last one, #setupTabMenu: makes sure that menus are set up for selected tabs.

There's some infrastructure there - event handling, and some application specific stuff - but that's the gist of it. If you need help doing something like this, just send me an email.

Read: Customizing the tab widget

Topic: Re: XML Generation in VisualWorks Previous Topic   Next Topic Topic: Microclimates and Software Development

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use