본문 바로가기
오픈소스 읽기 (OLD)/리눅스 어플리케이션 만들기 - Gtk4

3-4. CSS로 어플리케이션 꾸미기

by 커널패닉 2021. 4. 24.
반응형

앱 개발에서 디자인은 정말 중요한 요소이다. 때로는 앱의 본질적인 기능, 성능보다도 디자인이 우선시 되는 경우들이 종종 있는 것 같다. Gtk4 역시 개발자들의 디자인을 돕기 위한 강력한 기능을 제공한다.

2016년에 Gtk에는 중대한 변경점이 있었다. 기존 Gtk(당시는 Gtk3)는 테마를 통해서 어플리케이션의 디자인을 변경했었다. 테마는 데스크탑 전체 어플리케이션에 적용되기 때문에, 데스크탑의 통일성을 유지하는데 용이했다. 하지만 개별 어플리케이션 입장에서는 자기만의 특별한 디자인을 적용하기는 어려웠다. 이에 Gtk에서 찾은 해결책이 Css를 사용한 앱 디자인이다. 이번 포스트에서는 어떻게 CSS를 사용해서 Gtk 앱을 디자인 하는지 다룬다.

관련 블로그: blog.gtk.org/tag/css/

 

3.4.1 CSS란?

Css(Cascading Style Sheets)는 웹페이지를 꾸미는데 사용하는 마크업 언어이다. 보통 HTML, Javascript와 함께 웹 개발 핵심 요소로 뽑힌다. Css는 대략 아래와 같이 작성한다.

body { /* 특정 Type 지정 */
  text-align: center; /* 속성 설정 */
  color: red;
}

.label { /* 특정 Class 지정 */
  text-align: center;
  color: red;
}

#mainlabel { /* 특정 Id 지정 */
  text-align: center;
  color: red;
}

body.label { /* 조합해서 사용 가능 */
  text-align: center;
  color: red;
}

좀 더 자세한 Css 문법은 w3schools이나 생활코딩을 참고하면 배울 수 있다.

 

3.4.2 Gtk용 Css 작성하기

Gtk에서는 Css를 이용해서 앱을 꾸밀 수 있다. Gtk는 기존 Css의 문법을 따르는데, 일부 독특한 문법을 가지고 있다. 대표적으로 *이 있는데, 이 기호는 전역으로 설정을 적용하라는 의미로 사용된다. *을 사용하는 예는 아래와 같다.

/* 전체 위젯을 의미하는 기호로 "*"을 사용한다. */
* {
	color: grey;
}

/* 특정 위젯의 자식 위젯들 모두를 의미할 때는 아래와 같이 사용된다. */
button * {
	color: yellow;
}

 

다음은 예전에 만들어 두었던 BoxWidget 예제에 Css를 적용해 보았다. Box 예제는 Css를 로딩 및 적용하고, name을 설정하는 부분이 추가되었다. 변경된 부분에 집중해서 코드를 보자.

// gtk-example.c

#include <gtk/gtk.h>

/*
 * 재귀적으로 Css 적용을 위한 함수
 */
static void apply_css (GtkWidget *widget, GtkStyleProvider *provider) {
    GtkWidget *child;

    gtk_style_context_add_provider(gtk_widget_get_style_context(widget), provider, G_MAXUINT);
    for (child = gtk_widget_get_first_child(widget);
            child != NULL;
            child = gtk_widget_get_next_sibling(child))
        apply_css(child, provider);
}

static void clicked(GtkButton* button, gpointer user_data) {
    gtk_button_set_label(button, "Clicked");
}

static void activate(GtkApplication* app, gpointer user_data) {
    GtkWidget *window;
    GtkWidget *box;
    GtkWidget *label;
    GtkWidget *button;
    GtkCssProvider *provider;

    /*
     * Css 반영을 위한 provider 객체를 생성하고, css를 로딩한다.
     */
    provider = gtk_css_provider_new();
    gtk_css_provider_load_from_path(provider, "example.css");

    window = gtk_application_window_new(app);
    gtk_window_set_title(GTK_WINDOW(window), "Window");
    gtk_window_set_default_size(GTK_WINDOW(window), 200, 200);

    label = gtk_label_new("I am label");
    gtk_widget_set_vexpand(label, true);
    button = gtk_button_new_with_label("I am button");
    gtk_widget_set_vexpand(button, true);
    g_signal_connect(button, "clicked", G_CALLBACK(clicked), NULL);

    int spacing = 10;
    box = gtk_box_new(GTK_ORIENTATION_VERTICAL, spacing);
    gtk_widget_set_vexpand(box, true);
    gtk_box_append(GTK_BOX(box), label);
    gtk_box_append(GTK_BOX(box), button);

    gtk_window_set_child(GTK_WINDOW(window), box);

    /*
     * 재귀적으로 css를 적용한다. 
     */
    apply_css(window, GTK_STYLE_PROVIDER(provider));

    gtk_widget_show(window);
}

int main(int argc, char **argv) {
    GtkApplication *app;
    int status;

    app = gtk_application_new("kr.kernelpanic.gtkexample", G_APPLICATION_FLAGS_NONE);

    g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);

    status = g_application_run(G_APPLICATION(app), argc, argv);
    g_object_unref(app);

    return status;
}

이 파일에서 로딩하는 css는 example.css에 정의되어 있다. 몇 가지 Css를 로딩하면서, Css에 따라서 어플리케이션이 어떻게 변화하는지 살펴보자.

 

1. 위젯 타입을 지정한 Css

아래는 위젯의 타입을 명시해서 구성한 Css 파일이다. 위젯의 타입을 명시하는 경우에는 앱에 있는 모든 위젯에 적용이된다. Button은 내부적으로 label을 가지고 있기 때문에, Button의 안쪽은 Label의 설정을 따른다.

/* '*' 전역으로 설정 적용 */
* {
    background: green;
}

/* GtkLabel 객체에 설정 적용 
 * (Button의 경우 gtk_button_new_with_label로 생성되어서 label 설정 일부 공유)
 */
label {
    color: red;
}

/* GtkButton 객체에 설정 적용 */
button {
    background: yellow;
}

 

2. 위젯 타입을 재귀적으로 지정한 Css

앞선 예제의 경우 Button의 내부에 Label을 가지고 있기 때문에, Button의 디자인이 온전히 적용되지 않았다. 만약에 Button 내부에 일괄적으로 동일한 디자인을 적용하고 싶다면 아래와 같이 재귀적으로 위젯을 지정할 수 있다.

* {
    background: green;
}

label {
    color: red;
}

/* GtkButton 객체 및 자식 객체에 설정 적용 */
button * {
    background: yellow;
}

 

 

3. 고유한 이름을 지정한 Css

범용적으로 전체 위젯에 설정을 적용하는 대신에, 특정 오브젝트에만 설정을 적용할 수도 있다. 특정 오브젝트에 설정을 적용하려면, 그 오브젝트를 구분할 수 있는 특별한 무언가를 지정해줘야 한다. Gtk는 "이름"을 이용해서 위젯을 구분한다. 다음은 위젯에 이름을 적용하는 코드와 Css의 예제이다.

// ... < 생략 > ...

    label = gtk_label_new("I am label");
    /* 
     * 위젯의 이름을 설정한다.
     */
    gtk_widget_set_name(GTK_WIDGET(label), "thelabel");
    gtk_widget_set_vexpand(label, true);
    button = gtk_button_new_with_label("I am button");
    gtk_widget_set_vexpand(button, true);
    g_signal_connect(button, "clicked", G_CALLBACK(clicked), NULL);


/// ... < 생략 > ...
* {
    background: green;
}

/* label 중 thelabel 이름을 가진 위젯에 Css를 적용한다. */
label#thelabel {
    color: red;
}

button {
    background: yellow;
}

4. 특정 시그널에 반응하는 Css

마지막으로 Gtk의 시그널에 대응하는 Css를 설정할 수 있다. 예를 들어서 Button 위젯은 클릭 뿐 아니라, 마우스를 어플리케이션 위에 올리면 발생하는 hover란 시그널이 있다. 아래는 Button의 hover 시그널을 사용하는 Css 예제이다.

* {
    background: green;
}

label {
    color: red;
}

/* 마우스를 위에 올려두면 Css가 적용된다. */
button:hover * {
    background: yellow;
}

 

 

여기까지 Css를 이용해서 Gtk를 꾸미는 간단한 방법들을 살펴보았다. 이번 짧은 포스트에서 Gtk에서 Css를 다루는 모든 내요을 담기는 무리다. 보다 상세한 내용을 알고 싶다면 아래 링크를 참조해보자. (안타깝게도 gtk4 css 문서는 아직 부실하다...)

참조 문서: developer.gnome.org/gtk3/stable/chap-css-overview.html

반응형