# MenuBar (Vala)

Tiffany Antopolski
tiffany.antopolski@gmail.com 2012
GtkMenuItem 위젯을 가진 위젯
조성호
shcho@gnome.org 2017-2019.

# MenuBar

XML과 GtkBuilder로 만든 MenuBar입니다.

# XML로 MenuBar 만들기

XML로 메뉴 표시줄을 만들려면:

원하는 텍스트 편집기로 menubar.ui 파일을 만드십시오.

다음 줄을 파일 상단에 입력합니다:

<?xml version="1.0"? encoding="UTF-8"?>

메뉴 표시줄과 하위 메뉴를 넣을 인터페이스를 만들겠습니다. 메뉴 표시줄에는 파일, 편집, 선택 and 도움말 하위 메뉴를 넣겠습니다. 다음 XML 코드를 파일에 넣겠습니다:

<?xml version="1.0" encoding="UTF-8"?> <interface> <menu id="menubar"> <submenu> <attribute name="label">File</attribute> </submenu> <submenu> <attribute name="label">Edit</attribute> </submenu> <submenu> <attribute name="label">Choices</attribute> </submenu> <submenu> <attribute name="label">Help</attribute> </submenu> </menu> </interface>

이제 .vala 파일을 만들고 방금 추가한 menubar.ui 파일을 GtkBuilder를 사용하여 임포팅하겠습니다.

# GtkBuilder로 창에 MenuBar 추가하기

public class MyWindow : Gtk.ApplicationWindow { internal MyWindow (MyApplication app) { Object (application: app, title: "MenuBar Example"); this.set_default_size (200, 200); } }

class MyApplication: Gtk.Application { protected override void activate () { new MyWindow (this).show (); } protected override void startup () { base.startup ();

/* Setup menubar. */ /* Get the UI file. */ var builder = new Gtk.Builder (); try { builder.add_from_file ("menubar_basis.ui"); /* Handle the exception. */ } catch (Error e) { error ("Unable to load file: %s", e.message); }

/* Get the menubar from the builder. */ this.menubar = builder.get_object ("menubar") as MenuModel; } }

/* main creates and runs the application. */ public int main (string[] args) { return new MyApplication ().run (args); }

이제 vala 파일을 컴파일하고 실행하십시오. 프로그램은 페이지 상단의 사진과 같은 모습이 나타납니다.

# 항목을 메뉴에 추가하기

파일 메뉴에 새로 만들기와 끝내기 메뉴 항목 2개 추가로 시작하겠습니다. `파일` 하위 메뉴에 이 항목과 `section`을 추가하면 됩니다. menubar.ui 파일은 다음과 같이 나타나야 합니다(6번째 줄부터 13번째 줄까지 새로 추가한 섹션 구성이 들어갑니다):

<?xml version="1.0" encoding="UTF-8"?> <interface> <menu id="menubar"> <submenu> <attribute name="label">File</attribute> <section> <item> <attribute name="label">New</attribute> </item> <item> <attribute name ="label">Quit</attribute> </item> </section> </submenu> <submenu> <attribute name="label">Edit</attribute> </submenu> <submenu> <attribute name="label">Choices</attribute> </submenu> <submenu> <attribute name="label">Help</attribute> </submenu> </menu> </interface>

이 방식을 따라 `편집` 하위 메뉴로 `복사` 와 `붙여넣기` 를 추가하고, `Help` 하위 메뉴에 `정보`를 추가할 수 있습니다. 다음 따라하기 지침을 진행할 때까지 선택 하위 메뉴로의 항목 추가를 보류하겠습니다.

UI 파일만 바꾸었다면 vala 프로그램을 다시 컴파일할 필요는 없습니다. 그냥 이전에 컴파일한 프로그램을 실행만 하시면 사용자 인터페이스를 반영합니다.

# 설정 동작

세 단계로 이루어집니다.

우선 MyApplication 클래스에 ActionEntry 배열을 만들겠습니다. ActionEntry 구성은 다음과 같습니다:

"동작 이름"(필수)

동작의 "activate" 시그널에 연결하는 콜백 함수(가능하다면)

동작에 대한 활성화 함수로 전달할 매개변수 형식(가능하다면)

동작의 초기 상태(가능하다면)

"change-state" 시그널에 연결할 콜백 함수(가능하다면)

const ActionEntry[] actions = { { "new", new_cb }, // {"action name", callback_function} { "quit", quit_cb } };

두번째로 연결할 동작의 콜백 함수를 만들겠습니다.

void new_cb (SimpleAction action, Variant? parameter) { print ("You clicked \"New\"\n"); //new MyWindow (this).show (); }

void quit_cb (SimpleAction action, Variant? parameter) { print ("You clicked \"Quit\"\n"); this.quit (); }

마지막으로, "action" 속성을 추가하여 XML 파일의 동작에 메뉴 항목을 연결하겠습니다:

<item> <attribute name="label">New</attribute> <attribute name="action">app.new</attribute> </item> <item> <attribute name="label">Quit</attribute> <attribute name="action">app.quit</attribute> </item>

# 상태가 들어간 선택 하위 메뉴 및 항목

30줄부터 80줄 까지 가 들어간 부분에서 선택 메뉴 사용자 인터페이스를 만드는 XML 코드를 시연하고 있습니다.

# 동작: 프로그램 또는 창?

위에서 MyApplication 클래스의 "new"와 "open" 동작을 만들었습니다. 프로그램 자체를 다루는 "quit" 같은 동작은 비슷하게 만듭니다.

"copy"나 "paste" 같은 창을 다루는 일부 동작은, 프로그램에 넣지 않습니다. 창 동작은 창 클래스 부분에 만들어야합니다.

완전한 예제 파일에는 프로그램 동작과 창 프로그램이 들어있습니다. 창 동작은 보통 프로그램 메뉴에도 들어있습니다. 창 동작을 프로그램 메뉴에 넣는건 별로 좋은 습관이 아닙니다. 시연 목적으로 "New"와 "Open" 항목이 들어간 프로그램 메뉴를 만드는 UI 파일에 들어간 XML의 내용을 따르며, 동일한 이름을 지닌 메뉴 표시줄 항목 처럼 동일한 동작을 실행합니다.

# 이 예제의 완전한 XML UI 파일

<?xml version="1.0" encoding="UTF-8"?> <interface> <menu id="menubar"> <submenu> <attribute name="label">File</attribute> <section> <item> <attribute name="label">New</attribute> <attribute name="action">app.new</attribute> </item> <item> <attribute name="label">Quit</attribute> <attribute name="action">app.quit</attribute> </item> </section> </submenu> <submenu> <attribute name="label">Edit</attribute> <section> <item> <attribute name="label">Copy</attribute> <attribute name="action">win.copy</attribute> </item> <item> <attribute name="label">Paste</attribute> <attribute name="action">win.paste</attribute> </item> </section> </submenu> <submenu> <attribute name="label">Choices</attribute> <submenu> <attribute name="label">Shapes</attribute> <section> <item> <attribute name="label">Line</attribute> <attribute name="action">win.shape</attribute> <attribute name="target">line</attribute> </item> <item> <attribute name="label">Triangle</attribute> <attribute name="action">win.shape</attribute> <attribute name="target">triangle</attribute> </item> <item> <attribute name="label">Square</attribute> <attribute name="action">win.shape</attribute> <attribute name="target">square</attribute> </item> <item> <attribute name="label">Polygon</attribute> <attribute name="action">win.shape</attribute> <attribute name="target">polygon</attribute> </item> <item> <attribute name="label">Circle</attribute> <attribute name="action">win.shape</attribute> <attribute name="target">circle</attribute> </item> </section> </submenu> <section> <item> <attribute name="label">On</attribute> <attribute name="action">app.state</attribute> <attribute name="target">on</attribute> </item> <item> <attribute name="label">Off</attribute> <attribute name="action">app.state</attribute> <attribute name="target">off</attribute> </item> </section> <section> <item> <attribute name="label">Awesome</attribute> <attribute name="action">app.awesome</attribute> </item> </section> </submenu> <submenu> <attribute name="label">Help</attribute> <section> <item> <attribute name="label">About</attribute> <attribute name="action">win.about</attribute> </item> </section> </submenu> </menu> <menu id="appmenu"> <section> <item> <attribute name="label">New</attribute> <attribute name="action">app.new</attribute> </item> <item> <attribute name="label">Quit</attribute> <attribute name="action">app.quit</attribute> </item> </section> </menu> </interface>

# 이 예제에 대한 완전한 Vala 파일입니다

public class MyWindow : Gtk.ApplicationWindow {

/* Callback functions for the window actions. */ void copy_cb (SimpleAction action, Variant? parameter) { print ("\"Copy\" activated\n"); }

void paste_cb (SimpleAction action, Variant? parameter) { print ("\"Paste\" activated\n"); }

void shape_cb (SimpleAction action, Variant? parameter) { print ("shape is set to %s\n", parameter.get_string(null)); action.set_state (parameter); }

/* Create the window actions. */ const ActionEntry[] actions = { /*{ "action name", cb to connect to "activate" signal, parameter type, initial state, cb to connect to "change-state" signal } */ { "copy", copy_cb }, { "paste", paste_cb }, { "shape", shape_cb, "s", "'line'"} };

internal MyWindow (MyApplication app) { Object (application: app, title: "MenuBar Example"); this.set_default_size (200, 200);

/* Setup window actions. */ this.add_action_entries (actions, this); } }

class MyApplication: Gtk.Application { protected override void activate () { new MyWindow (this).show (); }

/* Callback functions for the application actions. */ void new_cb (SimpleAction action, Variant? parameter) { //new MyWindow (this).show (); print ("You clicked \"New\"\n"); }

void quit_cb (SimpleAction action, Variant? parameter) { print ("You clicked \"Quit\"\n"); this.quit (); }

void awesome_cb (SimpleAction action, Variant? parameter) { var active = action.get_state ().get_boolean (); action.set_state (new Variant.boolean (!active)); if (active) print ("You unchecked \"Awesome\"\n"); else print ("You checked \"Awesome\"\n"); }

void state_cb (SimpleAction action, Variant? parameter) { print ("state is set to %s\n", parameter.get_string(null)); action.set_state (parameter); }

/* Create the application actions. */ const ActionEntry[] actions = { { "new", new_cb }, { "quit", quit_cb }, { "awesome", awesome_cb, null, "false" }, { "state", state_cb, "s", "'off'" } };

protected override void startup () { base.startup ();

/* Setup application actions. */ this.add_action_entries (actions, this);

/* Setup menubar and app_menu. */ /* Get the UI file. */ var builder = new Gtk.Builder (); try { builder.add_from_file ("menubar.ui"); } catch (Error e) { error ("Unable to load file: %s", e.message); }

/* Get the menubar from the builder. */ this.menubar = builder.get_object ("menubar") as MenuModel;

/* Get the app_menu from the builder. */ this.app_menu = builder.get_object ("appmenu") as MenuModel; } }

/* main creates and runs the application. */ public int main (string[] args) { return new MyApplication ().run (args); }

# 접근 키

레이블에는 접근 키가 들어갈 수 있습니다. 접근 키는 키보드로 탐색할 때 활용하는 레이블에 밑줄이 들어간 문자입니다. 접근 키는 접근키 문자 앞에 밑줄을 넣어 만듭니다. 예를 들어 menubar.ui 레이블 속성에서 "File" 대신 "_File"을 넣습니다.

접근키는 Alt 키를 누르면 나타납니다. Alt F 키를 누르면 파일 메뉴가 나타납니다.

# 가속키

가속키는 UI 정의에 분명하게 추가할 수 있습니다. 예를 들어 일반적으로 프로그램을 끝내는 동작은 Ctrl Q 키를 눌러 진행할 수 있고, 또는 파일을 저장하는 동작은 Ctrl S 키를 눌러 진행할 수 있습니다. UI 정의에 가속키를 추가하려면 간단하게 항목의 "accel" 속성 값을 추가하면 됩니다.

`<attribute name="accel">&lt;Primary&gt;q</attribute>` 구문은 `Quit` 레이블 항목에 추가할 Ctrl Q 키 조합 입력을 만듭니다. 여기서, "Primary"는 PC의 Ctrl키 또는 맥의 ⌘ 키를 따릅니다.

<item> <attribute name="label">_Quit</attribute> <attribute name="action">app.quit</attribute> <attribute name="accel">&lt;Primary&gt;q</attribute> </item>

# 번역할 문자열

그놈 프로그램은 다양한 언어로 번역하기에 프로그램 문자열을 번역할 수 있게 만들어 두는게 중요합니다. 레이블을 번역할 수 있게 하려면 간단히 `translatable="yes"`를 설정하십시오:

`<attribute name="label" translatable="yes">Quit</attribute>`

# 관련 API 문서

이 예제는 다음 참고자료가 필요합니다:

Glib.ActionEntry

Gtk.Builder

# 연습 예제

Add an About dialog to the Help menu.

Redo the application menu (GMenu) example using an XML UI file and Gtk.Builder.