인터페이스는 현지화되어 있으며, Skill 소개와 설명 문서는 선택한 언어로 AI가 자동 번역합니다. 코드·명령어·경로·전문 용어는 원문을 유지하며 원작자 문서가 우선합니다.
Skills 둘러보기
개발 및 엔지니어링 · SKILL.md

desktop-gtk

desktop-gtk

Use when the user asks about GTK (GTK3, GTK4) application development, GtkWidget, GtkBuilder UI files, CSS styling for GTK, or cross-platform GTK development. Do NOT use for: GNOME-specific patterns (desktop-gnome), or KDE/Qt (desktop-kde, desktop-qt).

Independent GitHub snapshot · Automated classification · Original documentation governs

Skill 문서

GitHub

name: desktop-gtk description: > Use when the user asks about GTK (GTK3, GTK4) application development, GtkWidget, GtkBuilder UI files, CSS styling for GTK, or cross-platform GTK development. Do NOT use for: GNOME-specific patterns (desktop-gnome), or KDE/Qt (desktop-kde, desktop-qt). version: "2.0.0" author: "j4flmao" license: "MIT" compatibility: claude-code: true cursor: true codex: true windsurf: true tags: [desktop, gtk, linux, cross-platform]

GTK

Purpose

Build applications using GTK (GIMP Toolkit) — the primary widget toolkit for GNOME and a cross-platform toolkit supporting Linux, Windows, and macOS. GTK4 provides a modern rendering model with CSS styling, hardware-accelerated rendering via Vulkan/OpenGL, and a robust widget hierarchy.

Agent Protocol

Trigger

Exact user phrases: "GTK", "GTK3", "GTK4", "GtkWidget", "GtkBuilder", "Gtk CSS", "gtk-rs", "PyGObject", "GTK application", "GtkWindow", "GtkBox", "GtkListView".

Input Context

  • GTK version (3.x or 4.x — 4.x preferred for new projects)
  • Language (C, Rust via gtk4-rs, Python via PyGObject, Vala, JS via GJS)
  • Display backend (Wayland, X11, Windows GDK, macOS Quartz)
  • CSS styling approach (Adwaita theme, custom CSS, libadwaita)
  • Performance needs (heavy rendering via GtkGLArea, large data via GtkListView)
  • Platform target (Linux native, cross-platform)

Output Artifact

GTK application architecture with widget tree, signal flow, data binding, and styling strategy.

Completion Criteria

  • Widget hierarchy designed (container types, layout management)
  • UI definition format chosen (GtkBuilder XML vs programmatic)
  • Signal and callback architecture established
  • Data display strategy (GtkListView/ColumnView vs GtkTreeView)
  • CSS styling approach defined (Adwaita + custom CSS)
  • GtkApplication startup flow configured
  • Keyboard navigation and mnemonics enabled
  • Accessibility (ATK/AT-SPI) integrated
  • Internationalization via gettext configured
  • Window geometry and state persistence implemented

Max Response Length

250 lines.

Framework/Methodology

GTK Version Decision

FeatureGTK3GTK4
Widget hierarchyGtkBox, GtkGrid, GtkPanedSame + GtkListView, GtkColumnView
Event handlingsignal_connect per eventEvent controllers (GtkEventControllerKey)
RenderingCairo (CPU)Vulkan/OpenGL (GPU, ngl subsystem)
CSS animationLimitedFull CSS transitions + animations
AccessibilityATKAT-SPI directly integrated
List widgetsGtkTreeView (complex, outdated)GtkListView, GtkColumnView (modern, flexible)
OpenGLGtkGLAreaGtkGLArea + GtkMediaFile
File chooserGtkFileChooserGtkFileDialog (portal-based, async)
LifecycleManual disposeGObject ref-counted, easier memory management

Recommendation: GTK4 for all new projects. GTK3 only for legacy maintenance.

GTK App Architecture

GtkApplication
  ├── activate() → create windows
  ├── GActionMap (menus, shortcuts)
  ├── GMenuModel (app menu)
  └── GSimpleAction (actions)
      ↓
GtkApplicationWindow
  ├── Title bar (CSD or SSD)
  ├── GtkBox layout
  │   ├── GtkHeaderBar / GtkMenuBar
  │   ├── Content area (GtkListView, GtkBox, GtkNotebook)
  │   └── GtkStatusBar / Footer
  ├── GtkOverlay (popovers, tooltips)
  └── GtkShortcutsWindow

Workflow

Step 1: Set Up GtkApplication

// Rust with gtk4-rs
use gtk::prelude::*;
use gtk::{Application, ApplicationWindow, Button, Box, Orientation};

fn main() {
    let app = Application::builder()
        .application_id("com.example.app")
        .build();

    app.connect_activate(|app| {
        let window = ApplicationWindow::builder()
            .application(app)
            .title("My GTK App")
            .default_width(600)
            .default_height(400)
            .build();

        let vbox = Box::new(Orientation::Vertical, 8);
        let button = Button::with_label("Click Me");
        button.connect_clicked(|_| {
            println!("Clicked!");
        });
        vbox.append(&button);

        window.set_child(Some(&vbox));
        window.present();
    });

    app.run();
}
# Python with PyGObject
import gi
gi.require_version('Gtk', '4.0')
from gi.repository import Gtk, GLib, Gio

class MyApp(Gtk.Application):
    def __init__(self):
        super().__init__(application_id='com.example.app')
    def do_activate(self):
        window = Gtk.ApplicationWindow(application=self)
        box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8)
        button = Gtk.Button(label='Click Me')
        button.connect('clicked', lambda b: print('Clicked!'))
        box.append(button)
        window.set_child(box)
        window.present()

app = MyApp()
app.run(None)

Step 2: Build UI with GtkBuilder XML

<!-- interface.ui -->
<interface>
  <template class="MyAppWindow" parent="GtkApplicationWindow">
    <property name="title">My GTK App</property>
    <property name="default-width">600</property>
    <property name="default-height">400</property>
    <child>
      <object class="GtkBox">
        <property name="orientation">vertical</property>
        <property name="spacing">8</property>
        <property name="margin-top">12</property>
        <property name="margin-bottom">12</property>
        <property name="margin-start">12</property>
        <property name="margin-end">12</property>
        <child>
          <object class="GtkLabel" id="main_label">
            <property name="label">Hello, World!</property>
            <property name="xalign">0.0</property>
          </object>
        </child>
        <child>
          <object class="GtkButton" id="action_button">
            <property name="label">_Click Me</property>
            <property name="use-underline">True</property>
            <signal name="clicked" handler="on_button_clicked"/>
          </object>
        </child>
      </object>
    </child>
  </template>
</interface>
// Load UI from builder
let builder = gtk::Builder::from_resource("/com/example/app/ui/interface.ui");
let window: ApplicationWindow = builder.object("my_app_window").expect("Window not found");
window.set_application(Some(&app));
window.present();

Step 3: Modern Lists with GtkListView

// GTK4's GtkListView replaces GtkTreeView
// 1. Create model
let model = gio::ListStore::new::<MyItem>();

// Add items
model.append(&MyItem::new("Item 1", "Description 1"));
model.append(&MyItem::new("Item 2", "Description 2"));

// 2. Create factory for item widgets
let factory = gtk::SignalListItemFactory::new();
factory.connect_setup(|_, list_item| {
    let hbox = gtk::Box::new(gtk::Orientation::Horizontal, 8);
    let label = gtk::Label::new(Some(""));
    label.set_xalign(0.0);
    label.set_hexpand(true);
    hbox.append(&label);
    list_item.set_child(Some(&hbox));
});
factory.connect_bind(|_, list_item| {
    let item = list_item.item().unwrap().downcast::<MyItem>().unwrap();
    let child = list_item.child().unwrap();
    let label = child.first_child().unwrap().downcast::<gtk::Label>().unwrap();
    label.set_text(&item.name());
});

// 3. Create view
let list_view = gtk::ListView::new(Some(gtk::SingleSelection::new(Some(model))), Some(factory));

Step 4: Event Controllers (GTK4)

// GTK4 uses event controllers instead of direct signal connections
// Key controller
let key_controller = gtk::EventControllerKey::new();
key_controller.connect_key_pressed(|_, keyval, _code, _state| {
    if keyval == gdk::Key::Escape {
        // Handle escape
        return glib::Propagation::Stop;
    }
    glib::Propagation::Proceed
});
window.add_controller(&key_controller);

// Click controller (replaces button-press-event)
let click_controller = gtk::GestureClick::new();
click_controller.connect_pressed(|_, _n_press, x, y| {
    println!("Clicked at {}, {}", x, y);
});
widget.add_controller(&click_controller);

// Scroll controller
let scroll_controller = gtk::EventControllerScroll::new();
scroll_controller.set_flags(gtk::EventControllerScrollFlags::BOTH_AXES);
scroll_controller.connect_scroll(|_, _dx, dy| {
    println!("Scrolled {}", dy);
    glib::Propagation::Stop
});
widget.add_controller(&scroll_controller);

Step 5: CSS Styling in GTK4

/* style.css */
window {
    background-color: @theme_bg_color;
}

.important-label {
    font-weight: bold;
    font-size: 18px;
    color: @theme_fg_color;
}

.card-style {
    background-color: @theme_base_color;
    border-radius: 12px;
    padding: 16px;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
}

button.suggested-action {
    background-color: @accent_bg_color;
    color: @accent_fg_color;
    border-radius: 6px;
}

button.suggested-action:hover {
    background-color: shade(@accent_bg_color, 1.2);
}
// Load CSS
let provider = gtk::CssProvider::new();
provider.load_from_resource("/com/example/app/css/style.css");

gtk::style_context_add_provider_for_display(
    &gdk::Display::default().unwrap(),
    &provider,
    gtk::STYLE_PROVIDER_PRIORITY_APPLICATION,
);

Step 6: GTK Actions and Menus

<!-- menus.ui -->
<interface>
  <menu id="app-menu">
    <section>
      <item>
        <attribute name="label">_Preferences</attribute>
        <attribute name="action">app.preferences</attribute>
      </item>
      <item>
        <attribute name="label">_About</attribute>
        <attribute name="action">app.about</attribute>
      </item>
    </section>
  </menu>
</interface>
// Register actions
let action = gio::SimpleAction::new("preferences", None);
action.connect_activate(|_, _| {
    // Show preferences
});
app.add_action(&action);

let action = gio::SimpleAction::new("about", None);
action.connect_activate(|_, _| {
    // Show about dialog
});
app.add_action(&action);

// Set accelerators
app.set_accels_for_action("app.preferences", &["<Ctrl>comma"]);
app.set_accels_for_action("app.quit", &["<Ctrl>q"]);

Common Pitfalls

PitfallDescriptionPrevention
GTK3 patterns in GTK4Old signal connections, GtkTreeViewUse GTK4 APIs: event controllers, GtkListView
Main loop blockingSynchronous I/O freezes UIUse GIO async, spawn async tasks
CSS class name conflictsNames too generic, leaking stylesNamespace: .myapp-main-box, .myapp-highlight-btn
GtkTreeView complexityToo much boilerplate for simple listsUse GtkListView (GTK4) which is simpler
Missing CSS providerWidgets unstyled across themesUse theme variables: @theme_bg_color, @accent_bg_color
No keyboard navigationApp not usable without mouseMnemonics, focus chains, keyboard shortcuts
Ignoring RTLLayout broken in right-to-left localesUse GtkAlign.START/END, not left/right
Direct X11 usageApp broken on WaylandUse GDK abstractions, test on both
Memory leaksRef cycles with closuresUse weak references in signal callbacks

Best Practices

PracticeRationale
GTK4 for new projectsModern APIs, GPU rendering, better performance
GtkBuilder XML for UI layoutSeparates structure from logic, easier maintenance
CSS for styling, not widget attributesTheme support, easier customization
GAction for user-initiated actionsKeyboard shortcuts, menu integration, disabled state
Project structure: src/, data/, po/Standard Meson/GTK project layout
GResource for embedded assetsSingle binary, no file dependencies
GtkListView over GtkTreeViewSimpler API, better performance, type-safe
GIO async for I/O operationsNon-blocking, cancellable
Test with both light and dark themesAdwaita default and HighContrast
Use gettext from day oneWrapping strings later is disruptive

Architecture Patterns

Responsive Layout with GtkStack

let stack = gtk::Stack::new();
let sidebar_page = create_sidebar_page();
let detail_page = create_detail_page();
stack.add_titled(&sidebar_page, Some("sidebar"), "Sidebar");
stack.add_titled(&detail_page, Some("detail"), "Detail");

// Use GtkStackSwitcher or GtkStackSidebar for navigation

GtkScrolledWindow + Viewport

let scrolled = gtk::ScrolledWindow::new();
scrolled.set_policy(gtk::PolicyType::Automatic, gtk::PolicyType::Automatic);
scrolled.set_child(Some(&content_widget));
// GTK4 handles viewport automatically for scrollable widgets

Implementation Patterns

GTK4 Application Template

use gtk::prelude::*;
use gtk::{Application, ApplicationWindow, Button, Box, Orientation, Label};

fn main() {
    let app = Application::builder()
        .application_id("com.example.app")
        .build();

    app.connect_activate(|app| {
        let window = ApplicationWindow::builder()
            .application(app)
            .title("My GTK4 App")
            .default_width(600)
            .default_height(400)
            .build();

        let vbox = Box::new(Orientation::Vertical, 8);
        vbox.set_margin(16);

        let label = Label::new(Some("Hello, GTK4!"));
        label.set_css_classes(&["title"]);

        let button = Button::with_label("Click Me");
        button.connect_clicked(move |_| {
            println!("Button clicked!");
        });

        vbox.append(&label);
        vbox.append(&button);
        window.set_child(Some(&vbox));
        window.present();
    });

    app.run();
}

CSS Styling in GTK

/* style.css — GTK CSS for app */
.title {
    font-size: 24px;
    font-weight: bold;
    color: #1a1a1a;
    margin-bottom: 12px;
}

.button {
    background-color: #3584e4;
    color: white;
    border-radius: 6px;
    padding: 8px 16px;
}

.button:hover {
    background-color: #4a90e4;
}

.destructive-button {
    background-color: #e43535;
}

Architecture Decision Trees

GTK Widget Selection

What UI element?
├── Show information → Label, TextView, Picture
├── User input → Entry, TextView, SpinButton, ComboBoxText
├── Selection → CheckButton, RadioButton, Switch, DropDown
├── Layout → Box, Grid, Stack, Paned, Notebook
├── Navigation → StackSidebar, StackSwitcher, HeaderBar
└── Feedback → ProgressBar, Spinner, Statusbar, Toast

Anti-Patterns

Anti-PatternWhy It FailsCorrect Approach
Direct rendering in expose eventPoor performance, flickeringUse cairo only when necessary, prefer widgets
Long operations on main threadUI freezesUse async tasks or GTask for background work
Ignoring dpi/scale factorBlurry UI on HiDPIUse CSS units, test at multiple scale factors
No CSS classesInline style attributes everywhereDefine CSS classes, apply via add_css_class()
Imperative layout codeHard to maintainUse Blueprint UI or GtkBuilder XML for complex UIs

Performance Optimization

  • List view with GtkListView (GTK4): Use GtkListView with GtkListItemFactory for large data sets. Only creates widgets for visible items. Recycles widgets on scroll. Handles 100K+ items smoothly.
  • Async image loading: Load images in background thread with GTask. Update UI on main thread after load. Prevents UI freeze during decoding of large images.

Implementation Patterns

Observer Pattern for Event Handling

` interface EventObserver { onEvent(event: T): Promise; }

class EventBus { private observers: Set<EventObserver> = new Set(); subscribe(observer: EventObserver): void { this.observers.add(observer); } unsubscribe(observer: EventObserver): void { this.observers.delete(observer); } async emit(event: T): Promise { const results = Array.from(this.observers).map(o => o.onEvent(event)); await Promise.allSettled(results); } } `

Configuration-Driven Approach

config: defaults: timeout: 30s retryCount: 3 overrides: production: timeout: 60s retryCount: 5 development: timeout: 300s retryCount: 1

Production Considerations

Deployment Checklist

  • Configuration validated against schema before startup
  • Health check endpoints registered and monitored
  • Graceful shutdown with draining period (30s timeout)
  • Resource limits configured (CPU, memory, file descriptors)
  • Log level set appropriate for environment
  • Metrics endpoint secured and exposed
  • Rate limiting configured per-tier
  • TLS certificates valid and auto-renewing
  • Database migrations run as separate deployment step
  • Feature flags ready for gradual rollout

Monitoring and Alerting

MetricThresholdSeverityAction
Error rate> 1% over 5minCriticalPage on-call
p99 latency> 2s over 5minWarningInvestigate
Throughput drop> 50% over 1minCriticalCheck upstream
Queue depth> 1000 over 1minWarningScale consumers
Disk usage> 85%WarningClean or expand
Memory usage> 90% heapCriticalRestart or scale

Anti-Patterns

Anti-PatternSymptomRoot CauseSolution
Premature optimizationComplex code for no measured benefitGuessing instead of profilingMeasure first, optimize based on data
Copy-paste reuseDuplicate code across codebaseLack of abstractionExtract shared logic into libraries
Gold-platingFeatures with no current requirementOver-engineeringYAGNI — build what's needed now
Magical thinkingAssumptions without validationSkipping error handlingHandle all failure modes explicitly

Performance Optimization

Caching Strategy

Cache hierarchy: L1 (in-memory local) → L2 (distributed Redis/Memcached) → L3 (CDN/Edge). Cache invalidation: TTL-based (simple, stale), event-based (complex, fresh), write-through (consistent, higher write latency), write-behind (fast writes, eventual consistency).

Resource Pooling

  • Database connections: Pool of reusable connections (HikariCP, pgBouncer)
  • HTTP connections: Keep-alive + connection pooling for external calls
  • Thread pool: Bounded thread pools for async task execution

Profiling Methodology

  1. Establish baseline with production traffic profile
  2. Profile CPU with sampling profiler (pprof, perf, async-profiler)
  3. Profile memory with heap dumps and allocation tracking
  4. Profile I/O with strace/perf trace for syscall analysis
  5. Profile latency with distributed tracing (OpenTelemetry)
  6. Identify bottleneck, formulate hypothesis, implement fix
  7. Re-profile to verify improvement, repeat

Security Considerations

Threat Modeling (STRIDE)

  • Spoofing: Identity validation, authentication
  • Tampering: Integrity checks, digital signatures
  • Repudiation: Audit logs, non-repudiation
  • Information disclosure: Encryption, access control
  • Denial of service: Rate limiting, resource quotas
  • Elevation of privilege: Principle of least privilege

Supply Chain Security

  • Dependency scanning: Snyk, Dependabot, Trivy
  • SBOM generation: CycloneDX or SPDX format
  • Signed commits: GPG or SSH commit signing
  • Artifact verification: Checksum validation, signature verification

Secrets Management

  • Secrets never in code — always in secrets manager (Vault, AWS Secrets Manager)
  • Rotation policy: Rotate database credentials every 90 days
  • Access audit: Log every secrets access, alert on anomalies
  • Encryption at rest and in transit for all secrets
  • Principle of least privilege: each service gets only its own secrets

Rules

  • Default-deny security posture — allow only explicitly required access.
  • All inputs validated, all outputs encoded, all errors handled.
  • Defend in depth — multiple layers of security controls.
  • Fail securely — errors default to safe behavior.
  • Log security-relevant events for audit and investigation.
  • Keep dependencies updated — automate vulnerability scanning.
  • Design for observability from day one, not as an afterthought.
  • Document all architectural decisions with rationale.
  • Review code for security, performance, and correctness before merging.
저장한 Skills