1

using urwid, I'm trying to separate the highlight/walk and cursor functionality of a Pile widget. How can I use up/down to change which widget is highlighted, while keeping the cursor in a different widget?

anon01
  • 10,618
  • 8
  • 35
  • 58

2 Answers2

2

The default focus behavior couples the cursor with attribute (highlighting) behavior. The example below shows one way to decouple these, where a list of SelectableIcons retains the highlight feature, while the cursor is moved to a separate Edit widget. It does this via:

  • overriding the keypress method to update the focus where the cursor is not
  • wrapping each SelectableIcon in AttrMap that change their attribute based on their Pile's focus_position
  • after changing the SelectableIcon attributes, the focus (cursor) is set back to the Edit widget via focus_part='body'
  • self._w = ... is called to update all widgets on screen

There may be more concise ways of doing this, but this should be rather flexible.

import urwid

def main():
    my_widget = MyWidget()
    palette = [('unselected', 'default', 'default'),
               ('selected', 'standout', 'default', 'bold')]

    urwid.MainLoop(my_widget, palette=palette).run()


class MyWidget(urwid.WidgetWrap):

    def __init__(self):

        n = 10       
        labels = ['selection {}'.format(j) for j in range(n)]

        self.header = urwid.Pile([urwid.AttrMap(urwid.SelectableIcon(label), 'unselected', focus_map='selected') for label in labels])

        self.edit_widgets = [urwid.Edit('', label + ' edit_text') for label in labels]

        self.body = urwid.Filler(self.edit_widgets[0])

        super().__init__(urwid.Frame(header=self.header, body=self.body, focus_part='body'))

        self.update_focus(new_focus_position=0)

    def update_focus(self, new_focus_position=None):
        self.header.focus_item.set_attr_map({None: 'unselected'})

        try:
            self.header.focus_position = new_focus_position
            self.body = urwid.Filler(self.edit_widgets[new_focus_position])

        except IndexError:
            pass

        self.header.focus_item.set_attr_map({None: 'selected'})

        self._w = urwid.Frame(header=self.header, body=self.body, focus_part='body')

    def keypress(self, size, key):

        if key == 'up':
            self.update_focus(new_focus_position=self.header.focus_position - 1)

        if key == 'down':
            self.update_focus(new_focus_position=self.header.focus_position + 1)

        if key in {'Q', 'q'}:
            raise urwid.ExitMainLoop()

        super().keypress(size, key)

main()
anon01
  • 10,618
  • 8
  • 35
  • 58
  • niiice, you did it!! that's a pretty cool trick, thanks for sharing! =) – Elias Dorneles Mar 27 '18 at 21:13
  • Honestly i'm not that experienced (built some games + a simple drum machine), but your approach seems pretty reasonable, yeah! It may feel a bit hackish, but as I see it, what determines if it's a good design is your needs. I mean, a good approach is what fits best your requirements (and future ones you anticipate). :) – Elias Dorneles Mar 27 '18 at 21:38
  • Urwid is more of a library than a framework (even though it comes w/ a bunch of widgets + default loop), so in the end it's up to you to decide what is best to do with it. So don't worry too much and have fun! :) – Elias Dorneles Mar 27 '18 at 21:40
1

If you really need this, it probably makes sense to write your own widgets -- maybe based on some classes extending urwid.Text and urwid.Button

There is no real "highlight" feature in the widgets that come with urwid, there is only a "focus" feature, and it doesn't seem to be easy to decouple the focus highlight from the focus behavior.

You probably want to implement your own widgets with some sort of secondary highlighting.

Elias Dorneles
  • 22,556
  • 11
  • 85
  • 107
  • 1
    FWIW, I ended up wrapping a `SelectableIcon` in an `AttrMap`, and changing the AttrMap based on keypresses - it's a reasonable solution. I'll post it if I can condense the code into an example. Thanks for your input – anon01 Mar 26 '18 at 22:04
  • cool, sounds like a decent approach, +1! yeah, it would be cool if you could share, I'd love to see the example code :) – Elias Dorneles Mar 27 '18 at 09:46