From 8ade23537a682d4b0c9709d533b25702bde2ee23 Mon Sep 17 00:00:00 2001 From: Felix Domke Date: Fri, 28 Jan 2005 02:39:09 +0000 Subject: [PATCH] - add listbox - add listbox example to python (press space, use 1..5) - fix label text positions --- components.py | 12 +- lib/gdi/grc.cpp | 19 ++- lib/gdi/grc.h | 10 ++ lib/gui/Makefile.am | 2 +- lib/gui/ebutton.cpp | 2 +- lib/gui/elabel.cpp | 4 +- lib/gui/elistbox.cpp | 261 +++++++++++++++++++++++++++++++++++++ lib/gui/elistbox.h | 66 ++++++++++ lib/gui/eslider.cpp | 2 +- lib/gui/ewindowstyle.cpp | 36 ++++- lib/gui/ewindowstyle.h | 20 ++- lib/python/enigma_python.i | 2 + mytest.py | 7 +- screens.py | 1 + skin.py | 5 +- 15 files changed, 429 insertions(+), 20 deletions(-) create mode 100644 lib/gui/elistbox.cpp create mode 100644 lib/gui/elistbox.h diff --git a/components.py b/components.py index 4c26c0a0..8115ecf4 100644 --- a/components.py +++ b/components.py @@ -97,8 +97,10 @@ class GUIComponent: i = self.GUIcreateInstance(self, parent, skindata) priv["instance"] = i self.notifier.append(i) - if self.notifierAdded: + try: self.notifierAdded(i) + except: + pass # GUIdelete must delete *all* references to the current component! def GUIdelete(self, priv): @@ -221,3 +223,11 @@ class VolumeBar(HTMLComponent, GUIComponent, VariableValue): g = eSlider(parent) g.setRange(0, 100) return g + +class MenuList(HTMLComponent, GUIComponent): + def __init__(self): + GUIComponent.__init__(self) + + def GUIcreateInstance(self, priv, parent, skindata): + g = eListbox(parent) + return g diff --git a/lib/gdi/grc.cpp b/lib/gdi/grc.cpp index cb2a6de9..137e92dc 100644 --- a/lib/gdi/grc.cpp +++ b/lib/gdi/grc.cpp @@ -342,8 +342,23 @@ void gDC::exec(gOpcode *o) ePtr para = new eTextPara(o->parm.renderText->area); assert(m_current_font); para->setFont(m_current_font); - para->renderString(o->parm.renderText->text, o->parm.renderText->flags); - para->blit(*this, m_current_offset, getRGB(m_background_color), getRGB(m_foreground_color)); + para->renderString(o->parm.renderText->text, 0); + + if (o->parm.renderText->flags & gPainter::RT_HALIGN_RIGHT) + para->realign(eTextPara::dirRight); + else if (o->parm.renderText->flags & gPainter::RT_HALIGN_CENTER) + para->realign(eTextPara::dirCenter); + else if (o->parm.renderText->flags & gPainter::RT_HALIGN_BLOCK) + para->realign(eTextPara::dirBlock); + + ePoint offset = m_current_offset; + + if (o->parm.renderText->flags & gPainter::RT_VALIGN_CENTER) + { + eRect bbox = para->getBoundBox(); + offset += ePoint(0, (o->parm.renderText->area.height() - bbox.height()) / 2); + } + para->blit(*this, offset, getRGB(m_background_color), getRGB(m_foreground_color)); delete o->parm.renderText; break; } diff --git a/lib/gdi/grc.h b/lib/gdi/grc.h index d6a3cd70..5eb81314 100644 --- a/lib/gdi/grc.h +++ b/lib/gdi/grc.h @@ -174,7 +174,17 @@ public: void setForegroundColor(const gColor &color); void setFont(gFont *font); + /* flags only THESE: */ + enum + { + // todo, make mask. you cannot align both right AND center AND block ;) + RT_HALIGN_RIGHT = 1, + RT_HALIGN_CENTER = 2, + RT_HALIGN_BLOCK = 4, + RT_VALIGN_CENTER = 8 + }; void renderText(const eRect &position, const std::string &string, int flags=0); + void renderPara(eTextPara *para, ePoint offset=ePoint(0, 0)); void fill(const eRect &area); diff --git a/lib/gui/Makefile.am b/lib/gui/Makefile.am index 1b175c9a..670d15fc 100644 --- a/lib/gui/Makefile.am +++ b/lib/gui/Makefile.am @@ -5,5 +5,5 @@ INCLUDES = \ noinst_LIBRARIES = libenigma_gui.a libenigma_gui_a_SOURCES = \ - ebutton.cpp elabel.cpp eslider.cpp ewidget.cpp ewidgetdesktop.cpp ewindow.cpp ewindowstyle.cpp + ebutton.cpp elabel.cpp eslider.cpp ewidget.cpp ewidgetdesktop.cpp ewindow.cpp ewindowstyle.cpp elistbox.cpp diff --git a/lib/gui/ebutton.cpp b/lib/gui/ebutton.cpp index e24abd66..06cfda07 100644 --- a/lib/gui/ebutton.cpp +++ b/lib/gui/ebutton.cpp @@ -21,7 +21,7 @@ int eButton::event(int event, void *data, void *data2) getStyle(style); eLabel::event(event, data, data2); - style->drawButtonFrame(painter, eRect(ePoint(0, 0), size())); + style->drawFrame(painter, eRect(ePoint(0, 0), size()), eWindowStyle::frameButton); return 0; } diff --git a/lib/gui/elabel.cpp b/lib/gui/elabel.cpp index f9dcf31b..281a9d27 100644 --- a/lib/gui/elabel.cpp +++ b/lib/gui/elabel.cpp @@ -20,8 +20,8 @@ int eLabel::event(int event, void *data, void *data2) gPainter &painter = *(gPainter*)data2; ePtr fnt = new gFont("Arial", 14); painter.setFont(fnt); - style->setForegroundStyle(painter); - painter.renderText(eRect(0, 0, size().width(), size().height()), m_text); + style->setStyle(painter, eWindowStyle::styleLabel); + painter.renderText(eRect(0, 0, size().width(), size().height()), m_text, gPainter::RT_HALIGN_CENTER|gPainter::RT_VALIGN_CENTER); return 0; } diff --git a/lib/gui/elistbox.cpp b/lib/gui/elistbox.cpp new file mode 100644 index 00000000..c2af3d32 --- /dev/null +++ b/lib/gui/elistbox.cpp @@ -0,0 +1,261 @@ +#include + +/* + The basic idea is to have an interface which gives all relevant list + processing functions, and can be used by the listbox to browse trough + the list. + + The listbox directly uses the implemented cursor. It tries hard to avoid + iterating trough the (possibly very large) list, so it should be O(1), + i.e. the performance should not be influenced by the size of the list. + + The list interface knows how to draw the current entry to a specified + offset. Different interfaces can be used to adapt different lists, + pre-filter lists on the fly etc. + + cursorSave/Restore is used to avoid re-iterating the list on redraw. + The current selection is always selected as cursor position, the + cursor is then positioned to the start, and then iterated. This gives + at most 2x m_items_per_page cursor movements per redraw, indepenent + of the size of the list. + + Although cursorSet is provided, it should be only used when there is no + other way, as it involves iterating trough the list. + */ + +class eListboxTestContent: public virtual iListboxContent +{ + DECLARE_REF; +public: + void cursorHome(); + void cursorEnd(); + int cursorMove(int count=1); + int cursorValid(); + int cursorSet(int n); + int cursorGet(); + + void cursorSave(); + void cursorRestore(); + int size(); + + RESULT connectItemChanged(const Slot0 &itemChanged, ePtr &connection); + + // void setOutputDevice ? (for allocating colors, ...) .. requires some work, though + void setSize(const eSize &size); + + /* the following functions always refer to the selected item */ + void paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected); +private: + int m_cursor, m_saved_cursor; + eSize m_size; +}; + +DEFINE_REF(eListboxTestContent); + +void eListboxTestContent::cursorHome() +{ + m_cursor = 0; +} + +void eListboxTestContent::cursorEnd() +{ + m_cursor = size(); +} + +int eListboxTestContent::cursorMove(int count) +{ + m_cursor += count; + + if (m_cursor < 0) + cursorHome(); + else if (m_cursor > size()) + cursorEnd(); + return 0; +} + +int eListboxTestContent::cursorValid() +{ + return m_cursor < size(); +} + +int eListboxTestContent::cursorSet(int n) +{ + m_cursor = n; + + if (m_cursor < 0) + cursorHome(); + else if (m_cursor > size()) + cursorEnd(); + return 0; +} + +int eListboxTestContent::cursorGet() +{ + return m_cursor; +} + +void eListboxTestContent::cursorSave() +{ + m_saved_cursor = m_cursor; +} + +void eListboxTestContent::cursorRestore() +{ + m_cursor = m_saved_cursor; +} + +int eListboxTestContent::size() +{ + return 10; +} + +RESULT eListboxTestContent::connectItemChanged(const Slot0 &itemChanged, ePtr &connection) +{ + return 0; +} + +void eListboxTestContent::setSize(const eSize &size) +{ + m_size = size; +} + +void eListboxTestContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected) +{ + ePtr fnt = new gFont("Arial", 14); + painter.clip(eRect(offset, m_size)); + style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal); + painter.clear(); + + if (cursorValid()) + { + painter.setFont(fnt); + char string[10]; + sprintf(string, "%d.)", m_cursor); + + ePoint text_offset = offset + (selected ? ePoint(2, 2) : ePoint(1, 1)); + + painter.renderText(eRect(text_offset, m_size), string); + + if (selected) + style.drawFrame(painter, eRect(offset, m_size), eWindowStyle::frameListboxEntry); + } + + painter.clippop(); +} + +eListbox::eListbox(eWidget *parent): eWidget(parent) +{ + setContent(new eListboxTestContent()); + m_content->cursorHome(); + m_top = 0; + m_selected = 0; +} + +void eListbox::setContent(iListboxContent *content) +{ + m_content = content; +} + +void eListbox::moveSelection(int dir) +{ + /* we need the old top/sel to see what we have to redraw */ + int oldtop = m_top; + int oldsel = m_selected; + + /* first, move cursor */ + switch (dir) + { + case moveUp: + m_content->cursorMove(-1); + break; + case moveDown: + m_content->cursorMove(1); + /* ok - we could have reached the end. we just go one back then. */ + if (!m_content->cursorValid()) + m_content->cursorMove(-1); + break; + case moveTop: + m_content->cursorHome(); + m_top = 0; /* align with top, speeds up process */ + break; + case moveEnd: + /* move to last existing one ("end" is already invalid) */ + m_content->cursorEnd(); m_content->cursorMove(-1); + + m_top = m_content->cursorGet() - m_items_per_page + 1; + if (m_top < 0) + m_top = 0; + break; + } + + /* note that we could be on an invalid cursor position, but we don't + care. this only happens on empty lists, and should have almost no + side effects. */ + + /* now, look wether the current selection is out of screen */ + m_selected = m_content->cursorGet(); + if (m_selected < m_top) + { + m_top -= m_items_per_page; + if (m_top < 0) + m_top = 0; + } else if (m_selected >= m_top + m_items_per_page) + { + /* m_top should be always valid here as it's selected */ + m_top += m_items_per_page; + } + + if (m_top != oldtop) + invalidate(); + else + { + /* redraw the old and newly selected */ + gRegion inv = eRect(0, m_itemheight * (m_selected-m_top), size().width(), m_itemheight); + inv |= eRect(0, m_itemheight * (oldsel-m_top), size().width(), m_itemheight); + + invalidate(inv); + } +} + +int eListbox::event(int event, void *data, void *data2) +{ + switch (event) + { + case evtPaint: + { + ePtr style; + + assert(m_content); + recalcSize(); // move to event + + getStyle(style); + + if (!m_content) + return 0; + + gPainter &painter = *(gPainter*)data2; + + m_content->cursorSave(); + m_content->cursorMove(m_top - m_selected); + + for (int y = 0, i = 0; i < m_items_per_page; y += m_itemheight, ++i) + { + m_content->paint(painter, *style, ePoint(0, y), m_selected == m_content->cursorGet()); + m_content->cursorMove(+1); + } + + m_content->cursorRestore(); + + return 0; + } + default: + return eWidget::event(event, data, data2); + } +} + +void eListbox::recalcSize() +{ + m_itemheight = 20; + m_content->setSize(eSize(size().width(), m_itemheight)); + m_items_per_page = size().height() / m_itemheight; +} diff --git a/lib/gui/elistbox.h b/lib/gui/elistbox.h new file mode 100644 index 00000000..ac45a332 --- /dev/null +++ b/lib/gui/elistbox.h @@ -0,0 +1,66 @@ +#ifndef __lib_listbox_h +#define __lib_listbox_h + +#include +#include + +class iListboxContent: public iObject +{ +public: + /* indices go from 0 to size(). + the end is reached when the cursor is on size(), + i.e. one after the last entry (this mimics + stl behaviour) + + cursors never invalidate - they can become invalid + when stuff is removed. Cursors will always try + to stay on the same data, however when the current + item is removed, this won't work. you'll be notified + anyway. */ + + virtual void cursorHome()=0; + virtual void cursorEnd()=0; + virtual int cursorMove(int count=1)=0; + virtual int cursorValid()=0; + virtual int cursorSet(int n)=0; + virtual int cursorGet()=0; + + virtual void cursorSave()=0; + virtual void cursorRestore()=0; + + virtual int size()=0; + + virtual RESULT connectItemChanged(const Slot0 &itemChanged, ePtr &connection)=0; + + // void setOutputDevice ? (for allocating colors, ...) .. requires some work, though + virtual void setSize(const eSize &size)=0; + + /* the following functions always refer to the selected item */ + virtual void paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)=0; +}; + +class eListbox: public eWidget +{ +public: + eListbox(eWidget *parent); + void setContent(iListboxContent *content); + + void moveSelection(int how); + enum { + moveUp, + moveDown, + moveTop, + moveEnd + }; +protected: + int event(int event, void *data=0, void *data2=0); + void recalcSize(); +private: + int m_top, m_selected; + int m_itemheight; + int m_items_per_page; + ePtr m_content; +}; + + +#endif diff --git a/lib/gui/eslider.cpp b/lib/gui/eslider.cpp index 20b3ab47..665c6c1c 100644 --- a/lib/gui/eslider.cpp +++ b/lib/gui/eslider.cpp @@ -16,7 +16,7 @@ int eSlider::event(int event, void *data, void *data2) getStyle(style); style->paintBackground(painter, ePoint(0, 0), size()); - style->setForegroundStyle(painter); + style->setStyle(painter, eWindowStyle::styleLabel); // TODO - own style painter.fill(m_currently_filled); return 0; diff --git a/lib/gui/ewindowstyle.cpp b/lib/gui/ewindowstyle.cpp index 8599cfd7..64adfb58 100644 --- a/lib/gui/ewindowstyle.cpp +++ b/lib/gui/ewindowstyle.cpp @@ -57,17 +57,43 @@ void eWindowStyleSimple::paintBackground(gPainter &painter, const ePoint &offset painter.clear(); } -void eWindowStyleSimple::setForegroundStyle(gPainter &painter) +void eWindowStyleSimple::setStyle(gPainter &painter, int what) { - painter.setForegroundColor(gColor(0x1F)); + switch (what) + { + case styleLabel: + painter.setForegroundColor(gColor(0x1F)); + break; + case styleListboxSelected: + painter.setForegroundColor(gColor(0x1F)); + painter.setBackgroundColor(gColor(0x1A)); + break; + case styleListboxNormal: + painter.setForegroundColor(gColor(0x1C)); + painter.setBackgroundColor(m_background_color); + break; + } } -void eWindowStyleSimple::drawButtonFrame(gPainter &painter, const eRect &frame) +void eWindowStyleSimple::drawFrame(gPainter &painter, const eRect &frame, int what) { - painter.setForegroundColor(m_border_color_tl); + gColor c1, c2; + switch (what) + { + case frameButton: + c1 = m_border_color_tl; + c2 = m_border_color_br; + break; + case frameListboxEntry: + c1 = m_border_color_br; + c2 = m_border_color_tl; + break; + } + + painter.setForegroundColor(c2); painter.line(frame.topLeft1(), frame.topRight1()); painter.line(frame.topRight1(), frame.bottomRight1()); - painter.setForegroundColor(m_border_color_br); + painter.setForegroundColor(c1); painter.line(frame.bottomRight1(), frame.bottomLeft1()); painter.line(frame.bottomLeft1(), frame.topLeft1()); } diff --git a/lib/gui/ewindowstyle.h b/lib/gui/ewindowstyle.h index d5da5a34..74ff88d5 100644 --- a/lib/gui/ewindowstyle.h +++ b/lib/gui/ewindowstyle.h @@ -13,9 +13,21 @@ public: virtual void handleNewSize(eWindow *wnd, const eSize &size) = 0; virtual void paintWindowDecoration(eWindow *wnd, gPainter &painter, const std::string &title) = 0; virtual void paintBackground(gPainter &painter, const ePoint &offset, const eSize &size) = 0; - virtual void setForegroundStyle(gPainter &painter) = 0; - virtual void drawButtonFrame(gPainter &painter, const eRect &frame) = 0; + virtual void setStyle(gPainter &painter, int what) = 0; + enum { + styleLabel, + styleListboxSelected, + styleListboxNormal + }; + + virtual void drawFrame(gPainter &painter, const eRect &frame, int type) = 0; + + enum { + frameButton, + frameListboxEntry + }; virtual ~eWindowStyle() = 0; + }; class eWindowStyleSimple: public eWindowStyle @@ -31,8 +43,8 @@ public: void handleNewSize(eWindow *wnd, const eSize &size); void paintWindowDecoration(eWindow *wnd, gPainter &painter, const std::string &title); void paintBackground(gPainter &painter, const ePoint &offset, const eSize &size); - void setForegroundStyle(gPainter &painter); - void drawButtonFrame(gPainter &painter, const eRect &frame); + void setStyle(gPainter &painter, int what); + void drawFrame(gPainter &painter, const eRect &frame, int what); }; #endif diff --git a/lib/python/enigma_python.i b/lib/python/enigma_python.i index 367d8f8e..cbfd3ece 100644 --- a/lib/python/enigma_python.i +++ b/lib/python/enigma_python.i @@ -51,6 +51,7 @@ is usually caused by not marking PSignals as immutable. #include #include #include +#include extern void runMainloop(); @@ -79,6 +80,7 @@ extern PSignal1 &keyPressedSignal(); %include %include %include +%include template class PSignal0 { diff --git a/mytest.py b/mytest.py index b3d1701c..5233d85e 100644 --- a/mytest.py +++ b/mytest.py @@ -100,7 +100,12 @@ class Session: self.currentWindow = None def keyEvent(self, code): - self.currentDialog.data["okbutton"]["instance"].push() + print "code " + str(code) + if code == 32: + self.currentDialog.data["okbutton"]["instance"].push() + + if code >= 0x30 and code <= 0x39: + self.currentDialog.data["menu"]["instance"].moveSelection(code - 0x31) def close(self): self.delayTimer.start(0, 1) diff --git a/screens.py b/screens.py index a44f825e..50a9ddac 100644 --- a/screens.py +++ b/screens.py @@ -27,6 +27,7 @@ class testDialog(Screen): b.onClick = [ self.testDialogClick ] self["okbutton"] = b self["title"] = Header("Test Dialog - press ok to leave!") + self["menu"] = MenuList() self.tries = 0 diff --git a/skin.py b/skin.py index ab5fa369..2996edb0 100644 --- a/skin.py +++ b/skin.py @@ -14,8 +14,9 @@ dom = xml.dom.minidom.parseString( """ - - + + + -- 2.30.2