Статьи

Glimmer — Использование Ruby для создания пользовательских интерфейсов SWT

Glimmer — это JRuby DSL, который позволяет легко и эффективно создавать пользовательские интерфейсы с помощью надежной независимой от платформы библиотеки Eclipse SWT. Glimmer поставляется со встроенной поддержкой привязки данных, что значительно упрощает синхронизацию пользовательского интерфейса с моделями доменов. Цель проекта Glimmer — создать инфраструктуру JRuby на основе технологий Eclipse, чтобы обеспечить простое и эффективное создание приложений для настольных ПК с использованием языка Ruby. Сейчас, когда Glimmer стал проектом Eclipse, самое время узнать больше.

философия

Философия дизайна Glimmer может быть обобщена следующим образом:

  • Краткий и сухой
  • Запрашивает минимум информации, необходимой для выполнения задачи
  • Соглашение по конфигурации
  • Как можно более предсказуемо для существующих разработчиков SWT

Условные обозначения

Поскольку Glimmer опирается на Ruby, он отличается по своему синтаксису и соглашениям от того, чего ожидают типичные разработчики Java SWT: 

  • Скобки метода являются необязательными
  • Пример Java-vs-Ruby: show () => show
  • Имена методов следуют подчеркнутому синтаксису
  • Пример Java-vs-Ruby: addListener => add_listener
  • Классы создаются с использованием метода new (…) (в отличие от ключевого слова new):
  • Пример Java-vs-Ruby: new GridLayout () => GridLayout.new

Скачать

Пожалуйста, загрузите Glimmer с RubyForge: https://rubyforge.org/projects/glimmer/

ПРИМЕЧАНИЕ: Glimmer переходит на Eclipse.org. Пожалуйста, посетите http://andymaleh.blogspot.com для получения последних новостей о движении и о предстоящем месте загрузки на веб-сайте Eclipse.

Установка

Распакуйте zip-файл Glimmer и следуйте инструкциям по установке в файле README.

ПРИМЕЧАНИЕ. Хотя Glimmer не зависит от платформы, его функциональность была проверена только в Windows. Отзывы пользователей Mac и Linux будут очень благодарны.

Руководство

Давайте начнем с очень простого примера Glimmer Hello World:

shell {
  label { text “Hello World!” }
}

Это сделает следующее:

[Img_assist | NID = 3586 | название = | убывание = | ссылка = нет | ALIGN = не определено | ширина = 126 | высота = 48] 

В библиотеке SWT оболочка представляет окно приложения. Он действует как рамка вокруг виджетов приложения, которые являются визуальными компонентами, которые отображают информацию и / или обеспечивают взаимодействие с пользователем.

Один виджет, который использовался в примере Hello World, — это виджет метки, который просто отображает текст на экране. Оболочка также считается виджетом, за исключением того, что это особый вид виджетов, называемый составным.

За ключевым словом shell, объявившим оболочку приложения, следовал блок кода, заключенный в фигурные скобки. Этот блок содержит объявления содержимого оболочки, такие как метка Hello World. За ключевым словом метки также следовал блок кода. Однако этот блок содержал объявление свойства для метки, в котором указывалось, что текстовое значение равно «Hello World!»

Итак, чтобы объявить виджет, просто укажите его имя, а затем блок кода. Блок может указывать значения свойств или вкладывать другие объявления виджетов для составных виджетов.

Теперь давайте перейдем к более сложному примеру:

shell {
  text "User Profile"
  composite {
    layout GridLayout.new(2, false)
    group {
      text "Name"
      layout GridLayout.new(2, false)
      layout_data GridData.new(fill, fill, true, true)
      label {text "First"}; text {text "Bullet"}
      label {text "Last"}; text {text "Tooth"}  
    }
    group {
      layout_data GridData.new(fill, fill, true, true)
      text "Gender"
      button(radio) {text "Male"; selection true}
      button(radio) {text "Female"}  
    }
    group {
      layout_data GridData.new(fill, fill, true, true)
      text "Role"
      button(check) {text "Student"; selection true}
      button(check) {text "Employee"; selection true}  
    }
    group {
      text "Experience"
      layout RowLayout.new
      layout_data GridData.new(fill, fill, true, true)
      spinner {selection 5}; label {text "years"}
    }
    button {
      text "save"
      layout_data GridData.new(right, center, true, true)
    }
    button {
      text "close"
      layout_data GridData.new(left, center, true, true)
    }
  }
}.open

Это сделает следующее:

[Img_assist | NID = 3587 | название = | убывание = | ссылка = нет | ALIGN = не определено | ширина = 195 | высота = 209]

Пример содержит вариант виджетов от SWT:


  • Составной: виджет, который может просто содержать другие виджеты и управлять их макетом

  • Группа: Аналогична Composite за исключением того, что обычно она имеет рамку и заголовок.

  • Текстовое поле: позволяет пользователю вводить текстовую информацию

  • Кнопка флажка: позволяет пользователю сделать выбор из различных вариантов

  • Переключатель: позволяет пользователю сделать выбор между вариантами, которые являются взаимоисключающими

  • Spinner: позволяет пользователю вводить числовую информацию или вращать выделение номера с помощью мыши.

  • Кнопка: позволяет пользователю инициировать действия

Учитывая, что Glimmer использует библиотеку Eclipse SWT, разработчики могут обратиться к API SWT в качестве справочного материала по всем виджетам, включая их свойства и параметры макета: http://help.eclipse.org/stable/nftopic/org.eclipse.platform .doc.isv / ссылка / API / index.html

При чтении SWT API учитывайте следующие правила:


  • Любой виджет, доступный в SWT, включая пользовательские виджеты, написанные разработчиками, может быть доступен из Glimmer путем уменьшения или подчеркивания имени виджета (например, Composite -> Composite, LabledText -> Labeled_text).

  • Свойства, доступные в виджетах SWT, указываются путем перечисления их, за которыми следуют их значения, каждое в строке или разделенные точками с запятой в блоке виджета (например, label {text «Username:»; font some_font}) Имена свойств также сокращаются / подчеркиваются в Glimmer. ,

У виджетов SWT должно быть указано значение стиля, которое является константой, доступной в классе «SWT». Glimmer обычно скрывает это, полагаясь на умные значения по умолчанию.

Вот список значений по умолчанию, настроенных в Glimmer:


текст:

таблица SWT :: BORDER

: счетчик SWT :: BORDER
: кнопка SWT :: BORDER
: SWT :: PUSH

Тем не менее, для настройки виджета значение стиля может быть необязательно указано в скобках после имени виджета. Например, «кнопка (SWT :: RADIO)» отображает переключатель, а «кнопка (SWT :: CHECK)» отображает кнопку-флажок.

Синтаксис Glimmer также имеет синтаксический сахар для определения стиля. Просто укажите имя стиля в стандартном формате Ruby с уменьшенным или уменьшенным подчеркиванием без префикса «SWT ::». Например, кнопка (SWT :: RADIO) становится кнопкой (радио).

Составные виджеты SWT, такие как оболочка, составной элемент и группа, могут иметь менеджер макета, который размещает дочерние виджеты в соответствии с определенным шаблоном без необходимости явно указывать позицию (x, y) каждого дочернего виджета. Менеджеры макетов бывают разных видов, например GridLayout, предлагая макет в виде сетки; FillLayout, позволяющий дочерним виджетам заполнять всю доступную область; и RowLayout — рендеринг дочерних виджетов один за другим в строке по умолчанию.

Glimmer также настроен с умными настройками по умолчанию для менеджеров по расположению:


shell: FillLayout

составной: GridLayout с одним столбцом;

группа: GridLayout с одним столбцом.

GridLayout — это особенно полезная компоновка SWT, поэтому здесь я расскажу об этом чуть подробнее. GridLayout позволяет размещать виджеты в виде сетки, аналогичной таблицам HTML. Чтобы создать экземпляр пользовательского GridLayout, необходимо указать количество столбцов и указать, имеют ли они одинаковую ширину или нет. Вот блок кода, демонстрирующий групповой блок с GridLayout с 2 столбцами неравной ширины:

group {
 layout GridLayout.new(2, false)
}

Теперь предположим, что мы добавили четыре элемента в этот групповой блок:

group {
  layout GridLayout.new(2, false)
  label {text "First"}; 
  text {text "Bullet"}
  label {text "Last"}; 
  text {text "Tooth"}
}

 Указанный GridLayout будет размещать дочерние виджеты в сетке слева направо и сверху вниз:


  • Метка с текстом «Первый» войдет в 1-й столбец 1-го ряда.

  • Текстовое поле с текстом «Bullet» войдет во 2-й столбец 1-го ряда.

  • Метка с текстом «Последний» войдет в 1-й столбец 2-го ряда.

  • Текстовое поле с текстом «Зуб» войдет во 2-й столбец 2-го ряда.

Группа фактически была частью продвинутого примера, проиллюстрированного ранее. Ему был присвоен заголовок (с указанием атрибута text), а объявления виджетов были написаны таким образом, чтобы визуально отображать их отображение на экране. Обратите внимание, что объявления текстовых полей находятся на одной строке с объявлениями меток, поскольку и метка, и текстовое поле находятся под одной строкой, что помогает улучшить читаемость и удобство сопровождения кода:

group {
  text "Name"
  layout GridLayout.new(2, false)
  layout_data GridData.new(fill, fill, true, true)
  label {text "First"}; text {text "Bullet"}
  label {text "Last"}; text {text "Tooth"}  
}

Это делает следующее:

[Img_assist | NID = 3588 | название = | убывание = | ссылка = нет | ALIGN = не определено | ширина = 89 | высота = 73]

Макет определенных виджетов может быть дополнительно настроен путем указания данных макета. Для GridLayout данные макета указываются через объекты GridData.

Например, мы можем решить, чтобы текстовые поля в предыдущем примере имели большую ширину:

group {
  layout GridLayout.new(2, false)
  
  label {text "First"}; 
  text {
    text "Bullet"
    layout_data GridData.new(100, default)
  }

  label {text "Last"}; 
  text {
    text "Tooth"
    layout_data GridData.new(100, default)
  }
}

Это делает следующее:

[Img_assist | NID = 3589 | название = | убывание = | ссылка = нет | ALIGN = не определено | ширина = 125 | высота = 73]

The used GridData constructor takes two parameters: width hint and height hint.

The width was set to 100 pixels for both text boxes. The height was kept at the default value (SWT::DEFAULT)

For more details about GridLayout, GridData, and other layout managers, please refer to the SWT API documentation.

So far we have covered  how to construct user-interfaces that can display data and gather input from the user. Next, we will demonstrate how to perform work based on actions taken by the user.

SWT widgets can be monitored for certain user-interface events, such as mouse clicks, focus gain and loss, and key presses.

With the original SWT API, events can be monitored by adding listeners to widgets. For example, to monitor the push of a button, you would add a SelectionListener that does some work in its widgetSelected event method.

With Glimmer, events can be monitored by declaring their name (following Ruby conventions) prefixed by “on”

Here is an example of how to monitor button selection:

import org.eclipse.swt.widgets.MessageBox

@shell1 = shell {
  composite {
    button {
      text 'Save'
      on_widget_selected {
        message_box = MessageBox.new(@shell1.widget, SWT::NULL)
 message_box.text = 'Information'
        message_box.message = 'Saved!'
        message_box.open
      }
    } 
  }
}
@shell1.open

This renders the following:

[img_assist|nid=3590|title=|desc=|link=none|align=undefined|width=170|height=145]

On click of the button, a  message box is opened to let the user know that the information entered is saved.

MessageBox is a class from SWT that represents message dialogs. It was imported using the JRuby import method. Its constructor takes a parent and style. To obtain the parent, we assigned the shell object to a Ruby class variable @shell1. Since Glimmer wraps all SWT constructed objects with Glimmer decorators ( e.g. Shell is wrapped with RShell,) to obtain the SWT Shell class and pass it as the parent to the MessageBox constructor, the widget method was called (e.g. @shell1.widget.)

In the original SWT API, MessageBox has setter methods to set its text and message attributes. However in JRuby, the developer has the option to set them following the Ruby attribute conventions (e.g. message_box.text = ‘value’) because JRuby automatically enhances all Java objects with methods that follow the Ruby convention.

 

Another example that benefits from event monitoring is field validation on loss of focus. For example, let’s say we are validating the ZIP code on an address form, and we would like to display an error message if its value does not have a valid ZIP code format (e.g. 12345 or 12345-1234,) here is how we would do it with Glimmer (please add the following code before the button in the previous example):

import org.eclipse.swt.widgets.MessageBox

@shell1 = shell {
  composite {
    label { text "ZIP Code" }
    text {
      on_focus_lost { |focus_event|
        zip_code = focus_event.widget.text
        unless zip_code =~ /^\d{5}([-]\d{4})?$/
          message_box = MessageBox.new(@shell1.widget, SWT::NULL)
          message_box.text = 'Validation Error'
          message_box.message = 'Format must match ##### or #####-####'
          message_box.open
          focus_event.widget.set_focus
        end
      }
    }
    button {
      text 'Save'
      on_widget_selected {
        message_box = MessageBox.new(@shell1.widget, SWT::NULL)
        message_box.message = 'Saved!'
        message_box.open
      }
    } 
  }
}
@shell1.open

Here is what it produces:

[img_assist|nid=3591|title=|desc=|link=none|align=undefined|width=346|height=151]

Notice how the on_focus_lost block has a FocusEvent object as a parameter. This parameter may be specified optionally whenever some information is needed from the event object.

Again, this maps to the focusLost method on the FocusListener class in the original SWT API, which also takes a FocusEvent object as a parameter.

While widgets in the original SWT API have a setFocus event to grab the user interface focus, in JRuby set_focus may be used instead following the Ruby naming conventions.

Now, in order to cleanly separate event-driven behavior from user-interface code, we can rely on Glimmer’s data-binding support. Stay tuned for the next tutorial, which will cover data-binding and how to achieve clean code separation with the Model-View-Presenter pattern.

References:

Glimmer Eclipse Technology Project Proposal: http://www.eclipse.org/proposals/glimmer/

Glimmer Newsgroup: http://www.eclipse.org/newsportal/thread.php?group=eclipse.technology.glimmer

Glimmer at RubyForge: http://rubyforge.org/projects/glimmer/

Author Blog: http://andymaleh.blogspot.com

Andy Maleh (andy at obtiva.com), Senior Consultant, Obtiva Corp.