# TextView (JavaScript)

Taryn Fox
jewelfox@fursona.net 2012
다중 행 텍스트 편집기
조성호
shcho@gnome.org 2017-2019.

# TextView

TextView는 정말(아니면 최소한 보통) 세 객체의 모음으로 이루어져있습니다.

하단에는 TextBuffer가 있습니다. TextBuffer는 텍스트를 저장해둡니다.

가운데에는 TextView가 있는데 버퍼의 텍스트를 보고 편집할 수 있게 하는 위젯입니다. 텍스트가 얼마나 있느냐에 따라 자동으로 크기 조절 합니다.

자동으로 크기 조절을 해서 TextView를 이상하게 보이지 않게 할 수 있으므로, 보통 ScrolledWindow의 안에 둡니다. 이름이 있는데도 불구, 제목 표시줄과 X 단추가 있는 실제 창이 아닙니다. 더 관리하기 쉬운 TextView에서 창처럼 동작하며, 여러분이 만들려는 프로그램에 넣을 수 있는 위젯입니다. 버퍼의 텍스트가 공간을 맞추기에 너무 많다면, 스크롤 표시줄이 나타납니다.

TextView에 나타난 텍스트를 바꾸려면 TextBuffer가 실제로 텍스트 내용을 가지고 있으니 TextBuffer에서 처리하십시오. 누가 어떤 텍스트를 입력하는지 여러분이 보려고 할 떄도 동일합니다. 여기 예제 프로그램에서는 펭귄으로 가장한 누군가와 대화하고, "fish" 단어가 어딘가에 있는지 TextBuffer를 확인합니다.

실제로 수많은 펭귄의 수가 빠르게 줄어가고 있는데, 기후가 바뀌어 펭귄이 살아갈 터전의 빙하가 녹고 펭귄이 먹는 물고기가 줄어가기 때문입니다. 이 전제를 기반으로 만든 (약간은 바보 같을지도 모르는) 그놈 게임을 즐겨보고 싶으시다면 핑구스를 찾아보십시오.

# 가져올 라이브러리

#!/usr/bin/gjs

const Gtk = imports.gi.Gtk; const Lang = imports.lang;

이 프로그램을 실행할 때 가져올 라이브러리입니다. 시작 부분에 항상 gjs가 필요함을 알리는 줄을 작성해야 함을 기억하십시오.

# 프로그램 창 만들기

const TextViewExample = new Lang.Class ({ Name: 'TextView Example',

// Create the application itself _init: function () { this.application = new Gtk.Application ({ application_id: 'org.example.jstextview' });

// Connect 'activate' and 'startup' signals to the callback functions this.application.connect('activate', Lang.bind(this, this._onActivate)); this.application.connect('startup', Lang.bind(this, this._onStartup)); },

// Callback function for 'activate' signal presents windows when active _onActivate: function () { this._window.present (); },

// Callback function for 'startup' signal builds the UI _onStartup: function () { this._buildUI (); },

이 예제의 모든 코드는 TextViewExample 클래스에 들어갑니다. 위 코드는 위젯과 창이 들어가는 Gtk.Application를 만듭니다.

// Build the application's UI _buildUI: function () {

// Create the application window this._window = new Gtk.ApplicationWindow ({ application: this.application, window_position: Gtk.WindowPosition.CENTER, title: "Talk to a Penguin", default_height: 400, default_width: 440, border_width: 20 });

_buildUI 함수는 프로그램 사용자 인터페이스를 만드는 모든 코드를 넣는 곳입니다. 첫 단계에서는 모든 위젯을 우겨넣을 새 Gtk.ApplicationWindow를 만듭니다.

# TextView 만들기

// Create a label for the penguin to talk to you this._penguin = new Gtk.Label ({ height_request: 180, width_request: 400, label: "Squaaaak?", wrap: true });

이 예제의 첫단계에서는 펭귄이 여러분에게 무언가 할 말을 넣을 때 쓸 Label을 만듭니다. 줄바꿈 속성을 true로 설정하여 텍스트 줄을 알아서 바꾸게 한 채로 텍스트를 설정하겠지만, TextView 자체에서 더 세밀한 설정이 가능하기에 다른 방식으로 해보겠습니다.

// Create a textview for you to talk to the penguin this.buffer = new Gtk.TextBuffer(); this._textView = new Gtk.TextView ({ buffer: this.buffer, editable: true, wrap_mode: Gtk.WrapMode.WORD });

첫 단계로 내용을 넣을 TextButter를 만듭니다. 그 다음 TextView를 만들고, 앞서 만든 TextButter를 버퍼로 활용하라고 지시하겠습니다. 또한 새로운 내용을 입력할 수 있게 할 목적으로 편집 가능하게 설정하겠습니다.

wrap_mode 속성은 네가지 각각의 WrapModes 중 하나를 선택하게 합니다. 예로, Gtk.WrapMode.CHAR는 페이지 모서리에 줄 끝이 도달하면 단어 중간을 잘라 줄을 바꿉니다. 대부분 사람은 줄이 너무 길면 단어 단위로 자동으로 줄을 바꿔주는 Gtk.WrapMode.WORD 옵션을 사용할지도 모릅니다.

// Create a "scrolled window" to put your textview in so it will scroll this._scrolled = new Gtk.ScrolledWindow ({ hscrollbar_policy: Gtk.PolicyType.AUTOMATIC, vscrollbar_policy: Gtk.PolicyType.AUTOMATIC, shadow_type: Gtk.ShadowType.ETCHED_IN, height_request: 180, width_request: 400, });

// Put the textview into the scrolled window this._scrolled.add_with_viewport (this._textView);

ScrolledWindow를 만들고 수직 수평 방향으로 너무 크면 자동으로 스크롤하게 설정하겠습니다. 또한 멋들어지게 보이도록 잘 다듬은 테두리를 설정하겠습니다. 그 다음, TextView에 넣고 ScrolledWindow에 뷰포트를 제공하라고 지시하겠습니다.

# 사용자 인터페이스 나머지 부분 만들기

// Create a grid to organize them in this._grid = new Gtk.Grid ({ halign: Gtk.Align.CENTER, valign: Gtk.Align.CENTER });

// Put the label and textview in the grid one on top of the other this._grid.attach (this._penguin, 0, 0, 1, 1); this._grid.attach (this._scrolled, 0, 1, 1, 1);

처음 Grid 부분에서 Label과 ScrolledWindow를 만들어넣겠습니다.

// Create a button to send your message to the penguin this._send = new Gtk.Button ({ halign: Gtk.Align.END, margin_top: 20, label: "Send" }); this._send.connect ('clicked', Lang.bind (this, this._chat));

// Create a grid that will have the other grid on top and the button on bottom this._mainGrid = new Gtk.Grid ({ halign: Gtk.Align.CENTER, valign: Gtk.Align.CENTER });

// Add the other grid and the button to the main grid this._mainGrid.attach (this._grid, 0, 0, 1, 1); this._mainGrid.attach (this._send, 0, 1, 1, 1);

펭귄에게 메시지를 보낼 Button를 만들고, 상단에는 다른 새 Grid를 하단에는 Button을 넣겠습니다. 이 Button에는 상단에 여백을 주어 ScrolledWindow와 찰싹 붙어있지 않게 하겠습니다.

// Attach the main grid to the window this._window.add (this._mainGrid);

// Show the window and all child widgets this._window.show_all(); },

마지막으로 창에 메인 Grid를 붙이고, 프로그램을 실행할 때 창과 창 안에 있는 모든 요소를 나타내게 하겠습니다.

# 펭귄의 응답을 처리하는 함수

_chat: function () {

// Create a random number to determine what the penguin says this.number = Math.floor ((Math.random() * 3) + 1);

// Did you actually say anything? if (this.buffer.text) {

// Did you mention fish? if (this.buffer.text.match (/fish/gi)) {

// Have the penguin squaak about fish if (this.number == 1) this._penguin.set_label ("FISH!");

else if (this.number == 2) this._penguin.set_label ("Fish fish fish fish. Fish!");

else this._penguin.set_label ("Fish? Fish fish fish. Fish fish. FISH!");

}

// I guess you didn't mention fish else {

// Have the penguin talk about penguinny stuff if (this.number == 1) this._penguin.set_label ("SQUAAK!");

else if (this.number == 2) this._penguin.set_label ("Ork ork ork ork squaak. Squaak squaak! *waves flippers*");

else this._penguin.set_label ("Ork ork ork ork ork?");

}

}

// Clear the buffer this.buffer.text = "";

// Give focus back to the textview so you don't have to click it again this._textView.has_focus = true;

}

});

여기에 펭귄이 아무렇게나 말하는 JavaScript 기본 함수를 사용합니다. 펭귄은 물고기를 좋아하니, 물고기를 언급하면 펭귄이 대답하게 하겠습니다. 따라서, TextBuffer에 내용을 반환하는 JavaScript 문자열 객체의 this.buffer.text에 있는 match 메서드를 사용하겠습니다.

보내기를 누를 떄마다 TextBuffer를 지울테니, 과정이 끝나면 this.buffer.text 값을 빈 문자열로 넣겠습니다. 그 다음 TextView에 포커스를 반환하여 입력하기 전에 굳이 누르지 않아도 계속 입력할 수 있게 하겠습니다.

// Run the application let app = new TextViewExample (); app.application.run (ARGV);

마지막으로 마무리한 TextViewExample 클래스의 새 인스턴스를 만들고 프로그램을 실행하도록 설정하겠습니다.

# 완전한 코드 예제

#!/usr/bin/gjs

imports.gi.versions.Gtk = '3.0'; const Gtk = imports.gi.Gtk;

class TextViewExample {

// Create the application itself constructor() { this.application = new Gtk.Application({ application_id: 'org.example.jstextview' });

// Connect 'activate' and 'startup' signals to the callback functions this.application.connect('activate', this._onActivate.bind(this)); this.application.connect('startup', this._onStartup.bind(this)); }

// Callback function for 'activate' signal presents windows when active _onActivate() { this._window.present(); }

// Callback function for 'startup' signal builds the UI _onStartup() { this._buildUI(); }

// Build the application's UI _buildUI() {

// Create the application window this._window = new Gtk.ApplicationWindow ({ application: this.application, window_position: Gtk.WindowPosition.CENTER, title: "Talk to a Penguin", default_height: 400, default_width: 440, border_width: 20 });

// Create a label for the penguin to talk to you this._penguin = new Gtk.Label ({ height_request: 180, width_request: 400, label: "Squaaaak?", wrap: true });

// Create a textview for you to talk to the penguin this.buffer = new Gtk.TextBuffer(); this._textView = new Gtk.TextView ({ buffer: this.buffer, editable: true, wrap_mode: Gtk.WrapMode.WORD });

// Create a "scrolled window" to put your textview in so it will scroll this._scrolled = new Gtk.ScrolledWindow ({ hscrollbar_policy: Gtk.PolicyType.AUTOMATIC, vscrollbar_policy: Gtk.PolicyType.AUTOMATIC, shadow_type: Gtk.ShadowType.ETCHED_IN, height_request: 180, width_request: 400, });

// Put the textview into the scrolled window this._scrolled.add_with_viewport (this._textView);

// Create a grid to organize them in this._grid = new Gtk.Grid ({ halign: Gtk.Align.CENTER, valign: Gtk.Align.CENTER });

// Put the label and textview in the grid one on top of the other this._grid.attach (this._penguin, 0, 0, 1, 1); this._grid.attach (this._scrolled, 0, 1, 1, 1);

// Create a button to send your message to the penguin this._send = new Gtk.Button ({ halign: Gtk.Align.END, margin_top: 20, label: "Send" }); this._send.connect ('clicked', this._chat.bind(this));

// Create a grid that will have the other grid on top and the button on bottom this._mainGrid = new Gtk.Grid ({ halign: Gtk.Align.CENTER, valign: Gtk.Align.CENTER });

// Add the other grid and the button to the main grid this._mainGrid.attach (this._grid, 0, 0, 1, 1); this._mainGrid.attach (this._send, 0, 1, 1, 1);

// Attach the main grid to the window this._window.add (this._mainGrid);

// Show the window and all child widgets this._window.show_all(); }

_chat() {

// Create a random number to determine what the penguin says this.number = Math.floor ((Math.random() * 3) + 1);

// Did you actually say anything? if (this.buffer.text) {

// Did you mention fish? if (this.buffer.text.match (/fish/gi)) {

// Have the penguin squaak about fish if (this.number == 1) this._penguin.set_label ("FISH!");

else if (this.number == 2) this._penguin.set_label ("Fish fish fish fish. Fish!");

else this._penguin.set_label ("Fish? Fish fish fish. Fish fish. FISH!");

}

// I guess you didn't mention fish else {

// Have the penguin talk about penguinny stuff if (this.number == 1) this._penguin.set_label ("SQUAAK!");

else if (this.number == 2) this._penguin.set_label ("Ork ork ork ork squaak. Squaak squaak! *waves flippers*");

else this._penguin.set_label ("Ork ork ork ork ork?");

}

}

// Clear the buffer this.buffer.text = "";

// Give focus back to the textview so you don't have to click it again this._textView.has_focus = true; } };

// Run the application let app = new TextViewExample (); app.application.run (ARGV);

# 자세한 문서

Gtk.Application

Gtk.ApplicationWindow

Gtk.Button

Gtk.Grid

Gtk.Label

Gtk.RadioButton

Gtk.ScrolledWindow

Gtk.TextBuffer

Gtk.TextView