Quick Keyboard Navigation (1 Viewer)

A

Anonymous

Guest
For large lists it is convenient to use keyboard shortcuts to jump to an item in the list (like windows explorer).

To test this in media portal I added some code to the GUIListControl.cs and GUIThumbnailPanel.cs which handles all otherwise unhandled keys.
Maybe something like this can be added to the main code line? Well, here are the code snippets.

This is not much tested yet, but seems to work for 'My Music' and for me.

Thanks for MediaPortal,
Wog


GUIThumbnailPanell.cs
Code:
		case Action.ActionType.ACTION_KEY_PRESSED : 
		// <WOG>
		{  
			OnJump( action.m_key.KeyChar );
			m_bRefresh = true;
		} 
		// </WOG>
		break;
Code:
		// <WOG>
		/// <summary>
		/// Implementation of the OnJump action. If an (unhandled) key is pressed, the action tries to find 
		/// an item which start with the same letter. If an item is found it changes selection to point to it. 
		/// </summary>
		protected static DateTime tsLastKeyStroke = DateTime.Now; // time stamp last key pressed
		protected static double maxTimeSpan = 500.0;			  // max time between two strokes
		protected static String sKey = "";						  // jump string

		protected void OnJump(int key)
		{
			if (m_iSelect != GUIListControl.ListType.CONTROL_LIST) 
			{
				// don't do anything...
				return;
			}

			// convert to lower case string. 
			// in case less than 0.5 seconds have been passed since the last keys troke, append the new character.
			DateTime tsThisKeyStroke = DateTime.Now;
			TimeSpan ts = tsThisKeyStroke - tsLastKeyStroke ;
			tsLastKeyStroke = tsThisKeyStroke; 
			double ms = ts.TotalMilliseconds;
			char cKey = (char) key;
			if ( ms > maxTimeSpan )
			{
				sKey = "";
			}
			sKey += cKey.ToString().ToLower();

			// search the next item starting with this letter

			int startIndex =  m_iCursorY  * m_iColumns + m_iOffset + m_iCursorX;
			for ( int idx = 1; idx < (int)m_vecItems.Count; idx++ )
			{
				int index = (startIndex + idx) % (int)m_vecItems.Count;
				GUIListItem pItem = (GUIListItem)m_vecItems[index];
				String strLabel = pItem.Label.ToLower();
				if ( strLabel.StartsWith(sKey) )
				{
					m_iCursorX = index % m_iColumns;
					int iOffsetY = index / m_iColumns;

					// selection in currently displayed area -> just change selection
					if (m_iOffset / m_iColumns <= iOffsetY && m_iOffset / m_iColumns  + m_iRows > iOffsetY )
					{
						m_iCursorY = iOffsetY - m_iOffset / m_iColumns ;
						// m_iOffset = m_iOffset; is unchanged...
					} 
					else if ( iOffsetY < m_iRows )
					{
						// one of the first few items. start display with first line
						m_iCursorY = iOffsetY;
						m_iOffset = 0;
					} 
					else if ( iOffsetY >  (m_vecItems.Count + m_iRows - 1) / m_iColumns  - m_iRows )
					{
						// one of the last few items.
						int iOffRow = (m_vecItems.Count + m_iRows - 1) / m_iColumns  - m_iRows ;
						m_iOffset = iOffRow * m_iColumns;
						m_iCursorY = iOffsetY - iOffRow;
					}
					else
					{
						// center it
						int iOffRow = iOffsetY - m_iRows / 2;
						m_iOffset = iOffRow * m_iColumns;
						m_iCursorY = iOffsetY - iOffRow;
					}

					OnSelectionChanged();
					return;
				}
			}
		}
		// </WOG>


GUIListControl.cs
Code:
		case Action.ActionType.ACTION_KEY_PRESSED : 
		// <WOG>
		{  
			OnJump( action.m_key.KeyChar );
			m_bRefresh = true;
		} 
		// </WOG>
		break;
Code:
		// <WOG>
		/// <summary>
		/// Implementation of the OnJump action. If an overwise unhandled key is pressed, the action tries to find 
		/// an item which start with the same letter. If an item is found it changes selection to point to it. 
		/// </summary>
		protected static DateTime tsLastKeyStroke = DateTime.Now; // time stamp last key pressed
		protected static double maxTimeSpan = 500.0;			  // max time between two strokes
		protected static String sKey = "";						  // jump string
		
		protected void OnJump(int key)
		{

			if (m_iSelect != ListType.CONTROL_LIST) 
			{
				// don't do anything...
				return;
			}

			// convert to lower case string. 
			// in case less than 0.5 seconds have been passed since the last keys troke, append the new character.
			DateTime tsThisKeyStroke = DateTime.Now;
			TimeSpan ts = tsThisKeyStroke - tsLastKeyStroke ;
			tsLastKeyStroke = tsThisKeyStroke; 
			double ms = ts.TotalMilliseconds;
			char cKey = (char) key;
			if ( ms > maxTimeSpan )
			{
				sKey = "";
			}
			sKey += cKey.ToString().ToLower();
			// search the next item starting with this letter

			int startIndex = m_iOffset + m_iCursorY;
			for ( int idx = 1; idx < (int)m_vecItems.Count; idx++ )
			{
				int index = (startIndex + idx) % (int)m_vecItems.Count;
				GUIListItem pItem = (GUIListItem)m_vecItems[index];
				String strLabel = pItem.Label.ToLower();
				if ( strLabel.StartsWith(sKey) )
				{
					// selection in currently displayed area -> just change selection
					if (m_iCursorY + idx < m_iItemsPerPage)
					{
						m_iCursorY += idx;
						OnSelectionChanged();
						return;
					}
	
					// selection not visible. center displayed area aorund selected item
					int iCursor = m_iItemsPerPage / 2;
					int iOffset = index - iCursor;
					if ( index < m_iItemsPerPage / 2 )
					{
						// one of the first few (m_iItemsPerPage / 2) items. start display with first
						iCursor = index;
						iOffset = 0;
					} 
					else if ( index >  m_vecItems.Count - m_iItemsPerPage )
					{
						// one of the last few items.
						iOffset = m_vecItems.Count - m_iItemsPerPage;
						iCursor = index - iOffset;
					}

					m_iCursorY = iCursor;
					m_iOffset = iOffset;
					OnSelectionChanged();
					return;
				}
			}
		}
		// </WOG>
 
A

Anonymous

Guest
Nice, I had mentioned this in the IRC channel once before. Now, to make sure Frodo and crew see this so they can add it. :)

Edit:

Maybe users should need to type / to start so that you can still use all letters, like x and y, etc. . .
 
A

Anonymous

Guest
Thanks. I just downloaded MediaPortal over the weekend so I don't know exactly how everything works, but this is what I figured out so far:

The default keys like B or P still work. The default actions are executed first and the selection changes afterwards. Of course this is not very nice. I guess there are a couple of ways to solve this. Maybe a hotkey which turns this behavior on and the other hot keys off. This could be stored persistently with some folders- I really need this for a couple of folders only, e.g. for a list of artists. If there are only a couple of items in a folder, I can easily browse using the cursor keys.

I'll investigate ...
 

Mr.Mitchell

Retired Team Member
  • Premium Supporter
  • May 13, 2004
    227
    0
    the Netherlands
    I have been thinking about something similar in the past. I made a small proof of concept, but I got stuck on somthing (can't remember exactly what). But I used "SMS style" navigation. Which basically means that pressing 2 will cycle through ABC, 3 will cycle through DEF etc.. Just like on your phone. 1 could be used for cycling though numbers.

    I decided to use the numbers because some letters are used the specific MP actions like play/pause etc.. and all the required number keys are free. 0, which is delete, does not have to be used. And this way it would also be useable for remote control users.

    Maybe you could also implement the SMS behaviour.
     
    A

    Anonymous

    Guest
    nice idea, especially thinking of people using a TV remote control. Coding is easy - doing it like this you can search both ways:

    Using lookup table (not complete here) mapping numbers to keys:
    Code:
    		protected static String [] aCode = new String[3] { "2", "22", "222" };
    		protected static String [] aLetter = new String[3]{"a", "b", "c" };
    and changing thes earch to look for sSearch instead of sKey, with sSearch being
    Code:
    			String sSearch = sKey;
    			int nCode = Array.BinarySearch( aCode, sKey );
    			if ( nCode >= 0 )
    			{
    				sSearch = aLetter[ nCode ];
    			}
    			.... 
    			if ( strLabel.StartsWith(sSearch) )
     

    Users who are viewing this thread

    Top Bottom