Создание структуры пользовательского интерфейса
TODO: вставить ссылку на картинку
Реализация модуля пользовательского интерфейса (ui/mod.rs)
Теперь, когда относящийся к UI код перемещен в свой собственный модуль, очень важно объявить подмодули, которые данный модуль будет импортировать. Также важно объявить типы и функции, который данный модуль будет экспортировать.
#![allow(unused_variables)]
fn main() {
mod app;
mod content;
mod dialogs;
mod header;
pub mod misc;
pub mod save;
pub use self::app::App;
pub use self::content::Content;
pub use self::dialogs::{OpenDialog, SaveDialog};
pub use self::header::Header;
}
Как можно заметить выше, мы создаем следующие модули: app.rs, content.rs, dialogs.rs, save.rs, header.rs, misc.rs.
Реализация структуры программы (ui/app.rs)
Данная часть должна быть более-менее простой, так как использует код, подобный тому, что был задействован в предыдущих главах. С той разницей, что мы переместили инициализацию GTK в начало App.
#![allow(unused_variables)]
fn main() {
use gtk;
use gtk::*;
use super::Header;
use super::Content;
pub struct App {
pub window: Window,
pub header: Header,
pub content: Content,
}
impl App {
pub fn new() -> App {
// Инициализация GTK.
if gtk::init().is_err() {
eprintln!("failed to initialize GTK Application");
process::exit(1);
}
// Создать окно верхнего уровня.
let window = Window::new(WindowType::Toplevel);
// Создать заголовочную панель и связанное с ней содержимое.
let header = Header::new();
// Создать контейнер для хранения содержимого и виджетов.
let content = Content::new();
// Сделать заголовочную панель виджетом, содержащим название.
window.set_titlebar(&header.container);
// Установить заголовок окна.
window.set_title("Markdown Editor");
// Установить класс менеджера окна.
window.set_wmclass("md-editor", "Markdown Editor");
// Иконка программы.
window.set_default_size(800, 600);
Window::set_default_icon_name("iconname");
// Добавить содержимое вовнутрь окна.
window.add(&content.container);
// Запрограммировать поведение кнопки выхода.
window.connect_delete_event(move |_, _| {
main_quit();
Inhibit(false)
});
// Возвратить структуру программы.
App { window, header, content }
}
}
}
Реализация структуры заголовка (ui/header.rs)
Данную структуру будет реализовать ещё проще. Мы реализуем кнопки Open, Save, Save As, которые будут находиться в заголовке. Мы будем использовать мнемоники, так что одна из букв в названии кнопок будет становиться подчеркнутой при нажатии на клавишу Alt, так что пользователь сможет выбрать нужную кнопку.
#![allow(unused_variables)]
fn main() {
use gtk::*;
pub struct Header {
pub container: HeaderBar,
pub open: Button,
pub save: Button,
pub save_as: Button,
}
impl Header {
pub fn new() -> Header {
// Создать контейнер для хранения главной заголовочной панели.
let container = HeaderBar::new();
// Установить текст, который будет показывать в заголовке
// заголовочной панели.
container.set_title("Markdown Editor");
// Включить элементы управления окна внутри заголовочной панели.
container.set_show_close_button(true);
let open = Button::new_with_mnemonic("_Open");
let save = Button::new_with_mnemonic("_Save");
let save_as = Button::new_with_mnemonic("Save _As");
container.pack_start(&open);
container.pack_end(&save_as);
container.pack_end(&save);
// Возвратить заголовок и все его внутреннее содержимое.
Header { container, open, save, save_as }
}
}
}
Реализация структуры содержимого (ui/content.rs)
Здесь мы начнем использовать GtkWebViews, GtkSourceViews.
Создание просмотра web
Создание просмотра web очень легко создать. Выполняя следующий код, вы получите панель для просмотра web, которую вы можете внедрить в ваш пользовательский интерфейс так же, как вы можете это сделать с другими виджетами.
#![allow(unused_variables)]
fn main() {
// Create a the WebView for the preview pane.
let context = WebContext::get_default().unwrap();
let preview = WebView::new_with_context(&context);
}
Данный просмотр web прокручиваем, так что эту функциональность не нужно реализовывать самостоятельно.
Создаем и настраиваем просмотр кода
Просмотр кода являются более сложной реализацией, потому что он требуют дополнительной настройки. Мы попытаемся получить следующий результат:
- Табуляция должна быть размером в 4 пробела
- Просмотрщик должен показывать номера строк
- Будем использовать шрифт по умолчанию - monospaced, размер - 11
- Подсветка синтаксиса Markdown
- Попытка использовать тему Builder, если не получится - тему Classic
Начнем со структуры Source.
#![allow(unused_variables)]
fn main() {
pub struct Source {
pub container: ScrolledWindow,
pub view: View,
pub buff: Buffer,
}
impl Source {
fn new() -> Source {
}
}
}
После этого создадим буфер и представление для просмотра, это делается так же, как и создание простого текстового буфера и представления для просмотра. Как только это сделано, мы поместим представление вовнутрь прокручиваемого окна.
#![allow(unused_variables)]
fn main() {
// Создать SourceView для редактора на левой панели.
let buff = Buffer::new(None);
let view = View::new_with_buffer(&buff);
let container = ScrolledWindow::new(None, None);
container.add(&view);
}
Установим настройки, используя функцию, принимающую представление и буфер:
#![allow(unused_variables)]
fn main() {
fn configure_source_view(view: &View, buff: &Buffer) {
view.set_show_line_numbers(true);
view.set_monospace(true);
view.set_insert_spaces_instead_of_tabs(true);
view.set_indent_width(4);
view.set_smart_backspace(true);
view.set_right_margin(100);
view.set_left_margin(10);
view.set_show_right_margin(true);
view.set_background_pattern(BackgroundPatternType::Grid);
// TODO: следующий релиз пакета GTK
// view.set_input_hints(InputHints::SPELLCHECK + InputHints::WORD_COMPLETION);
}
}
Мы можем использовать пакет pango для изменения шрифта в представлении.
Заметьте, нам необходимо объявить типаж, чтобы понять откуда появился метод
override_font
. Возможно, будущее обновление GTK Rust API займется решением
этого вопроса.
#![allow(unused_variables)]
fn main() {
// Настраивает шрифт, используемый в панели с кодом. По умолчанию используется
// Monospaced, размер - 11. Когда будем изменять шрифт, нужно вручную указать
// типаж, в котором находится нужный метод.
let font = FontDescription::from_string("monospace 11");
WidgetExt::override_font(&view, &font);
}
Включим подсветку синтаксиса Markdown по умолчанию. Используемые языки извлекаются из LanguageManager. Язык будет присвоен напрямую буферу, а не представлению.
#![allow(unused_variables)]
fn main() {
// Включить подсветку Markdown
LanguageManager::new()
.get_language("markdown")
.map(|markdown| buff.set_language(&markdown));
}
Укажем используемую схему.
#![allow(unused_variables)]
fn main() {
let manager = StyleSchemeManager::new();
manager
.get_scheme("Builder")
.or(manager.get_scheme("Classic"))
.map(|theme| buff.set_style_scheme(&theme));
}
Полный исходный код
#![allow(unused_variables)]
fn main() {
use gtk::*;
use pango::*;
use sourceview::*;
use webkit2gtk::*;
pub struct Content {
pub container: Paned,
pub source: Source,
pub preview: WebView,
}
pub struct Source {
pub container: ScrolledWindow,
pub view: View,
pub buff: Buffer,
}
impl Content {
pub fn new() -> Content {
// Создать контейнер для хранения главного содержимого
let container = Paned::new(Orientation::Horizontal);
let source = Source::new();
// Создать WebView для предыдущей панели.
let context = WebContext::get_default().unwrap();
let preview = WebView::new_with_context(&context);
// Упакуем
container.pack1(&source.container, true, true);
container.pack2(&preview, true, true);
// Сделать так, чтобы две панели имели одинаковый размер - половина
// контейнера, в котором они находятся.
source.container.set_size_request(100, -1);
preview.set_size_request(100, -1);
Content { container, source, preview }
}
}
impl Source {
pub fn new() -> Source {
// Создать SourceView для редактора на левой панели.
let buff = Buffer::new(None);
let view = View::new_with_buffer(&buff);
let container = ScrolledWindow::new(None, None);
container.add(&view);
configure_source_view(&view, &buff);
Source { container, buff, view }
}
}
fn configure_source_view(view: &View, buff: &Buffer) {
WidgetExt::override_font(view, &FontDescription::from_string("monospace"));
LanguageManager::new()
.get_language("markdown")
.map(|markdown| buff.set_language(&markdown));
let manager = StyleSchemeManager::new();
manager
.get_scheme("Builder")
.or(manager.get_scheme("Classic"))
.map(|theme| buff.set_style_scheme(&theme));
view.set_show_line_numbers(true);
view.set_monospace(true);
view.set_insert_spaces_instead_of_tabs(true);
view.set_indent_width(4);
view.set_smart_backspace(true);
view.set_right_margin(100);
view.set_left_margin(10);
view.set_show_right_margin(true);
view.set_background_pattern(BackgroundPatternType::Grid);
// TODO: следующий релиз пакета GTK
// view.set_input_hints(InputHints::SPELLCHECK + InputHints::WORD_COMPLETION);
}
}