"Правильный" GUI на OPL: Статьи о Psion

"Правильный" GUI на OPL

Автор: Осирис

В эпоху общей деградации сайта mypsion.ru и сращения его Конференции с „Помойкой” стала возможной статистическая оценка содержания конференции.
Поддерживая саму идею „эмуляторов” и положительно оценивая чуство юмора создателей сего software, я не могу не отметить некоторые отклонения пользовательского интерфейса от принятых в GUI EPOC32 стандартов.

Грубо, в плане пользовательского интерфейса, все приложения для EPOC можно разбить на 4 категории:
  • приложения без пользовательского интерфейса (их очень мало, например, старые версии русификаторов — в новых есть пользовательский интерфейс через контрольную панель) — в основном это системные утилиты, которые не предусматривают вмешательство пользователя;
  • приложения с примитивным консольным пользовательским интерфейсом (всякие hello_world и пр.) — это в основном маленькие программки начинающих программистов, которые ещё не постигли искусства программирования графического интерфейса, либо программы–„однодневки”, написанные для личного пользования, либо для конкретной „узкой” задачи;
  • „оконные” приложения (например, SysBack, FreeMem etc.) — небольшие программки, которые не требуют многозадачности и легко помещаются вместе со всеми управляющими кнопками и прочим в окошко на экране;
  • полноценные “event-driven” приложения (EPOC Word, к примеру), имеющие toolbar, меню, поддерживающие перьевой ввод и пр.
Однако, вернёмся к эмуляторам mypsion.ru. Они, к сожалению, находятся где-то между 2-м и 3-м вариантами — попытка работать с мышью, но не систематизированная, toolbar и меню не поддерживаются, выход по Ctrl+E отсутствует и пр. Все эти недостатки вынудили :) меня ниписать краткое введение в то, как надо писать „правильный” эмулятор... :)

В начале „правильного” эмулятора должны включаться файлы с заголовками функций всех OPX'ов и подгружаемых OPO–файлов, используемых в программе, полезно также включить Const.oph — список полезных констант:
include "Const.oph"
include "toolbar.opj"
include "bmp.oxh"
Удобно также декларировать ещё некоторые константы. Я их включаю во все свои OPL'ные программки:
rem Definition for sidebar keys
const KKeySidebarCut% = 10001
const KKeySidebarInfared% = 10002
const KKeySidebarZoomIn% = 10003
const KKeySidebarZoomOut% = 10004

const Key_Pressed& = &400
const KProgram_To_Foreground& = &401
const KProgram_To_Background& = &402
const KOpen_or_Close_Document& = $404
const KKey_Down& = &406
const KKey_Up& = &407
const KPointer_Used& = &408
const KPointer_Enter = &409
const KPointer_Exit = &40A
const KCmdLetterExit$ = "X"
const Forever = 0

const KErrNotReady% = -62
... и некоторые специфичные для этого примера :)
const KKisaButton% = 1
const KBoardButton% = 2
Если мы хотим сделать полноценную программу, появляющуюся в списке Extras, то надо разкомментировать следующие 4 строчки:
rem app BAA, &101F43B8
rem    caption "BAA", KLangEnglish%
rem    icon "BAA_48.mbm"
rem enda
„Правильный” эмулятор обязан иметь toolbar!
proc Start:
    loadm "z:\system\opl\Toolbar"
    TBarLink:("Main")
endp
Теперь, собственно, основная программа, в которой определяются переменные, общие для всех подпрограмм:
PROC Main:
    global keycode&
    global ScrWid%        rem ширина экрана в пикселах
    global ScrHght%        rem высота экрана в пикселах
    global MenuPos%        rem позиция в меню
    global event&(16)
    global evType&
    global version$(4)    rem версия программы
    global Kisa%            rem ну, типа, Кису эмулируем или конфу

    global IsRevo%, IsGeofox%, IsSeries7%    rem это для поддержки разного hardware
    global IsOsaris%, IsR380%, IsSeries5%
    global Linespacing%        rem расстояние между строками

    InitApp%:    rem прорисовка интерфейса и пр. однократные действия при запуске
rem    event-driven программа должна быть зациклена!
    do
        run:
    until forever
endp
Прорисовка интерфейса и прочие однократные действия при старте программы:
proc InitApp%:
    version$="0.01"
    ScrWid% = gWidth
    ScrHght% = gHeight
    Kisa% = KTrue%
rem    setHardware: rem        ну, это для debugging'а
    getHardware:    rem определение размера экрана и пр.
    initTBar:    rem создание toolbar'а и управление им
    GIPRINT "BAA v" + version$
    alert("Эта программа поясняет, как надо", "писать эмуляторы Кисы с Осей :)")
    if Kisa%
        Kisa% = KFalse%
        cmdK%:
    else
        Kisa% = KTrue%
        cmdB%:
    endif
endp
Собственно, основной цикл, который „слушает” (GetEvent32) нажатия на кнопки и на экран и передаёт результат подпрограмме event_handler&:, которая, проанализировав ввод, решает, какая подпрограмма будет его обрабатывать
proc run:
    do
        busy off
        GetEvent32 event&()
        evType& = event&(KEvaType%)
        event_handler&:
rem        keycode& = event_handler&: rem для обработки „обычных” символов,
rem               без Ctrl, нужно отдельно обрабатывать значение, выдаваемое event_handler&:
rem        if keycode& = 0    rem впрочем, можно и иначе реализовать
rem            continue        rem для простоты, опустим это
rem        endif
    until forever    
endp
Ну, и собственно, сам обработчик клавиатурного и экранного ввода (упрощённая версия, без drag & drop и прочего):
proc event_handler&:
    local menukey&
    if evType& = KEvCommand&
        system_event:    rem обработчик системного обращения к программе,
		                 rem например „убивание” из списка задач, или требование открытия файла
        return 0
    elseif evType& = KProgram_To_Background&
rem        background: rem если нужно сделать что-то перед уходом на задний план
        return 0
    elseif evType& = KProgram_To_Foreground&
rem        foreground:    rem то же при выходе на передний план
        return 0
    elseif evType& = KPointer_Used& rem обработка нажатий на экран
        if TBarOffer%:(event&(KEvAPtrOplWindowId%), event&(KEvAPtrType%), event&(KEvAPtrPositionX%), event&(KEvAPtrPositionY%))
               rem обращения к toolbar'у

            return KTrue%
        elseif event&(KEvAPtrType%) = 1
rem            pointer_event:(event&(KEvAPtrOplWindowId%), event&(KEvAPtrPositionX%), event&(KEvAPtrPositionY%))
               rem прочие нажатия стилусом

            return 0
        else
            return 0
        endif
    elseif (evType& = KKey_Up&)
        return 0
    elseif (evType& = KKey_Down&)
        return 0
    elseif (evType& = KKeyMenu32%) or (evType& = KKeySidebarMenu32%)
	      rem нажатия на клавишу „Menu” или на соответствующую картинку на границе touch screen'а
        menukey& = display_menu&:    rem вызов и предварительная обработка меню
        if menukey& < %A
            return 0
        elseif menukey& <= %Z
            process_ctrl_command%:(menukey& + 32, &6)
        elseif menukey&
            process_ctrl_command%:(menukey&, &4)
        endif
        return 0
    elseif evType& = KKeySidebarZoomIn%
rem        cmdM%:
        return 0
    elseif evType& = KKeySidebarZoomOut%
rem        cmdsM%:
        return 0
    elseif evType& = KKeyUpArrow32%        rem стрелка вверх
        return 0
    elseif evType& = KKeyDownArrow32%    rem стрелка вниз
        return 0
    elseif evType& = KKeyRightArrow32%    rem стрелка вправо
        return 0
    elseif evType& = KKeyLeftArrow32%    rem стрелка влево
        return 0
    elseif evType& = KKeyPageUp32% rem PgUp
        return 0
    elseif evType& = KKeyPageDown32% rem PgDn
        return 0
    elseif ((evType& and Key_Pressed&) = 0)
        if event&(KEvAKMod%) and KKmodControl%
            process_ctrl_command%:(evType& - 1 + %a, event&(KEvAKMod%))
            return 0
        else
            return evType&
        endif
    endif
    return 0
endp
Если программу „убьют” извне, она должна благополучно закрыться:
proc system_event:
    local command$(KMaxStringLen%), cmdLetter$(1)
    command$ = getcmd$
    cmdLetter$ = left$(command$, 1)
    command$ = right$(command$, len(command$) - 1)
    if cmdLetter$ = "X"
        cmdE%:
    endif
endp
Один из главных элементов обработки клавиатурного ввода — обработка нажатий Ctrl+<буква> и Ctrl+Shift+<буква>. Для каждой буквы, которая будет использоваться, необходимо написать процедуру с имеенем cmd<буквы>%: или cmdS<буквы>%:, например, для Ctrl+B — cmdB%:, а для Ctrl+Shift+B — cmdSB%:. Теже самые фукции вызывает и toolbar и меню (!), таким образом можно легко соотносить между собой нажатия на toolbar, выбор в меню и нажатие на „горячую клавишу”:
proc process_ctrl_command%:(key&, modif&)
    local cmdRoot$(4)
    if modif& and KKmodControl%
        cmdRoot$ = "cmd"
        if modif& and KKmodShift%
            cmdRoot$ = "cmds"
        endif
        onerr eNotCmd::
        @%(cmdRoot$ + chr$(key&)):
        return KTrue%
    endif
eNotCmd::    rem обработчик оибок нужен для корректной обработки неиспользуемых клавиш,
             rem для которых нет функции cmd*%:
    onErr off
    if err <> KErrNoProc%
rem        giprint "Bug: proc " + cmdRoot$ + chr$(key&) + "%:, " + err$(err)
        return KTrue%
    endif
    return KFalse%
endp
proc PrintText%:(y%, text$)
    local lenght%
    lenght% = gTWIDTH(text$)
    gat (ScrWid% - lenght% + TbVis% * TbWidth%)/2, y%
    gprintb text$, lenght%, 3
    return Ktrue%
endp
Обработка Ctrl+E
proc cmdE%:
    stop
endp
Обработка Ctrl+K — выбор эмулятора Кисы и Оси :)
proc cmdK%:
    local string$(250), lenght%
    if not Kisa%
        gcls
        TBarLatch:(KKisaButton%)    rem эффект „залипания” кнопки toolbar'а — 
		     rem  если мы смотрим эмулятор Кисы — залипает „Кисина” кнопка, а если Конфы,
			  rem  то, соответственно, другая
        Kisa% = KTrue%
        alert("Здесь рисуем Кису с Осей", "и их эмулятор :)")
        gfont KFontArialNormal22&
        gStyle 1
        PrintText%:(ScrHght%/2 - 2 * Linespacing%, "Киса и Ося были тут, " + Mid$(DATIM$, 17, 5) + "!!!")
        gStyle 0
        gfont KFontArialNormal15&
        PrintText%:(ScrHght%/2 - Linespacing%, "Для перехода в эмулятор конфы")
rem        PrintText%:(ScrHght%/2, "нажмите на 2-ю иконку toolBAAr'а,")
        PrintText%:(ScrHght%/2 + Linespacing%, "выберите соответствующий пункт в меню,")
        PrintText%:(ScrHght%/2 + 2 * Linespacing%, "или нажмите Ctrl+B")
        lenght% = gTWIDTH("нажмите на 2-ю иконку toolr'a,")
        gStyle 1
        gat (ScrWid% - lenght% - gTWIDTH("BAA") + TbVis% * TbWidth%)/2, ScrHght%/2
        gStyle 0
        gprint "нажмите на 2-ю иконку tool" rem, gTWIDTH("нажмите на 2 иконку tool", 2
        gStyle 1
        gprint "BAA"
        gstyle 0
        gprint "r'a,"
    endif
rem    return Kisa%:
endp
Обработка Ctrl+B — вызов на передний план эмулятора Конференции
proc cmdB%:
    local string$(250), lenght%
    if Kisa%
        gcls
        TBarLatch:(KBoardButton%)
        Kisa% = KFalse%
        alert("Здесь рисуем эмулятор конференции")
        gfont KFontArialNormal22&
        gStyle 1
        PrintText%:(ScrHght%/2 - 2 * Linespacing%, "Киса — ламер!!!")
        gStyle 0
        gfont KFontArialNormal15&
        PrintText%:(ScrHght%/2 - Linespacing%, "Для перехода в эмулятор Кисы и Оси")
        PrintText%:(ScrHght%/2, "нажмите на 1-ю иконку toolBAAr'а,")
        PrintText%:(ScrHght%/2 + Linespacing%, "выберите соответствующий пункт в меню,")
        PrintText%:(ScrHght%/2 + 2 * Linespacing%, "или нажмите Ctrl+K")
    endif
rem    return Kisa%:
endp
Обработка Ctrl+A — сведения о программе:
proc cmdA%:
    busy off
    dinit "BAA v." + Version$
    dtext "", "Baa рулит!!!", $202
    dtext "", "© 2001, Osiris", $202
    dbuttons "Continue", KKeyEnter% + $100
    dialog
    return KTrue%
endp
Обработка Ctrl+T — включение/выключение toolbar'а:
rem Toolbar on/off
proc cmdT%:
    if TbVis%
        TBarHide:
    else
        TBarShow:
    endif
rem    Kisa% = -(Kisa% + 1)
    if Kisa%
        cmdK%:
    else
        cmdB%:
    endif
endp
Собственно, инициализация toolbar'а. На Revo должно появиться 3 кнопки, на остальных машинках — 4. Первые 2 организованы в группу и поочерёдно „залипают”, в зависимости от того, эмулятор чего используется в данный момент. 3-я и 4-я не требуют разъяснений.
proc initTBar:
    local x%(6), KisaID&, BoardID&, AboutID&, CloseID&
    local KisaIDm&, BoardIDm&, AboutIDm&, CloseIDm&
    local MBM$(250)
    MBM$ = PARSE$("BAA.mbm", left$(cmd$(1),len(cmd$(1))-4), x%())
    KisaID& = bitmapload&:(MBM$, 0)
    KisaIDm& = bitmapload&:(MBM$, 1)
    BoardID& = bitmapload&:(MBM$, 2)
    BoardIDm& = bitmapload&:(MBM$, 3)
    AboutID& = bitmapload&:(MBM$, 4)
    AboutIDm& = bitmapload&:(MBM$, 5)
    CloseID& = bitmapload&:(MBM$, 6)
    CloseIDm& = bitmapload&:(MBM$, 7)

    TBarInit:("BAA", ScrWid%, ScrHght%)
    TBarButt:("k", KKisaButton%, "Киса" + chr$(10) + "и Ося", 0, KisaID&, KisaIDm&, KTbFlgLatchStart% or - (KTbFlgLatched% * Kisa%))
    TBarButt:("b", KBoardButton%,"Конфа", 0, BoardID&, BoardIDm&, KTbFlgLatchEnd% or KTbFlgLatched% * (Kisa% + 1))
    if IsRevo%
        TBarButt:("e", 3, "Close", 0, CloseID&, CloseIDm&, 0)
    else
        TBarButt:("a", 3, "About", 0, AboutID&, AboutIDm&, 0)
        TBarButt:("e", 4, "Close", 0, CloseID&, CloseIDm&, 0)
    endif
    if ScrWid% < 640
        TBarHide:
    else
        TBarShow:
    endif
endp
Определение меню. Опять же, Киса&Ося с Конфой объединены в группу, лишь один элемент которой может быть активным. А „Show toolbar” включает и выключает галку...
proc display_menu&:
    local m&
    mInit
    mCard "File", "Close", %e
rem    mCard "View", "Zoom in", %m, "Zoom out", -%M, "Show toolbar", %t or TbMenuSym%
    mCard "View", "Киса & Ося", %k + KMenuOptionStart% - (KMenuSymbolOn% * Kisa%), "Конфа", -(%b + KMenuOptionEnd% + (KMenuSymbolOn% * (Kisa% + 1))), "Show toolbar", %t or TbMenuSym%
    mCard "Tools", "About", %a
    m& = menu(MenuPos%)
    return m&
endp
Следующая подпрограмма полезна лишь для грубой прикидки того, как программа будет выглядеть на устройствах другого размера
proc setHardware:
    local HT%
    local Draftoffset%
    HT% = 3
    dinit "Hardware Type"
    dchoice HT%, "Screen size:", "Osaris,Revo,Series5,GeoFox,Series7"
    dbuttons "Continue", KKeyEnter% + $100
    if dialog
        if HT% = 1
            ScrWid% = 320 rem 480
            ScrHght% = 200
            Draftoffset% = 160
        elseif HT% = 2
            ScrWid% = 480 rem 560
            ScrHght% = 160
            Draftoffset% = 80
        elseif HT% = 3
            ScrWid% = 640
            ScrHght% = 240
            Draftoffset% = 0
        elseif HT% = 4
            ScrWid% = 640
            ScrHght% = 320
            Draftoffset% = 0
        else
            ScrWid% = 640
            ScrHght% = 480
            Draftoffset% = 0
        endif
    else
        ScrWid% = gWidth
        ScrHght% = gHeight
        Draftoffset% = 0
    endif
endp
И последняя подпрограмма в данном примере должна дать нам ответ о размерах экрана использзуемого устройства. Если нужно что-то большее, например, заблокировать эмулятор Кисы и Оси на Mako :), то можно проанализировать версию ROM и т.п.
proc getHardware:
    IsRevo% = KFalse%
    IsGeofox% = KFalse%
    IsSeries7% = KFalse%
    IsOsaris% = KFalse%
    IsR380% = KFalse%
    IsSeries5% = KFalse%
    if ScrWid% = 480
        IsRevo% = KTrue%            rem 480 x 160 =  76800
    elseif ScrHght% = 320
        IsGeofox% = KTrue%        rem 640 x 320 = 204800
    elseif ScrHght% = 480
        IsSeries7% = KTrue%        rem 640 x 480 = 307200
    elseif ScrHght% = 200
        IsOsaris% = KTrue%        rem 320 x 200 =  64000
    elseif ScrHght% = 120
        IsR380% = KTrue%            rem 360 x 120 =  43200
    else
        IsSeries5% = KTrue%        rem 640 x 240 = 153600
    endif
    Linespacing% = 22
endp

Надеюсь, авторы эмуляторов (KM и BAA) найдут таки время для приведения своих славных продуктов к знакомому нам всем „стандартному” EPOC'овскому look & feel. :)

Source code, MBM и OPO файлы прилагаются (для корректной работы MBM и OPO файлы должны быть в одной папке). App я компилировать не стал, но сделать это тривиально.

Дата статьи: 1 ноября 2001 г

« Назад в каталог | Обсудить в конференции »
[На главную] [Обновления] [Новости] [Клуб] [Обзоры программ] [Советы] [Обзоры железа] [SIBO] [Конференция] [Статьи] [Базы] [Ссылки] [Поиск]
http://stomatology-best.com/ способ 3 отбеливание зубов.