File: gtk-drawing.lua

package info (click to toggle)
oocairo 1.4-1.2
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 712 kB
  • sloc: ansic: 3,352; makefile: 59; sh: 15
file content (212 lines) | stat: -rw-r--r-- 7,268 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
-- This example was intended to show some simple Cairo drawing in a
-- GtkDrawingArea widget, but it ended up being more of a demonstration
-- of some Gtk+ techniques.  Search for 'Cairo' in the code to find the
-- bits which directly use the Lua-oocairo library.
--
-- Quite accidentally this program also demonstrates how to create a
-- GdkPixbuf from a Cairo image surface, which is a useful way of rendering
-- icons with Cairo for display in Gtk+.  With a GdkPixmap you can draw
-- directly onto the image with Cairo, but pixmaps don't allow transparency.
-- For the line style icons shown in the menus in this example I wanted the
-- natural Gtk+ background to show through, so transparency is essential
-- (particularly when the icons are highlighted by being displayed against
-- a different background colour).

local Cairo = require "oocairo"

if not pcall(require, "gtk") then
    error("This example requires the 'gtk' module (Lua-Gnome) to be installed")
end

local LINE_CAP = { "butt", "square", "round" }
local LINE_JOIN = { "miter", "bevel", "round" }

local ui = {}
local canvas_wd, canvas_ht = 300, 300

local function refresh_canvas () ui.canvas:queue_draw() end
local event_handlers = {
    line_width_changed = refresh_canvas,
    line_cap_changed = refresh_canvas,
    line_join_changed = refresh_canvas,
    fill_color_changed = refresh_canvas,
    stroke_color_changed = refresh_canvas,

    canvas_size_set = function (widget, size)
        canvas_wd, canvas_ht = size.width, size.height
    end,
}

local function initialize_canvas_widget (canvas)
    local function handle_expose (widget, event)
        local window = widget.window
        local cr = Cairo.context_create_gdk(window)

        -- It's easier to define the path with a different scaling, so that
        -- the positions don't depend on the size or aspect ratio of the
        -- canvas, but we have to restore the original CTM before drawing or
        -- the stroke pen won't be circular.
        cr:save()
        cr:scale(canvas_wd, canvas_ht)
        cr:new_path()
        cr:move_to(.05, .95)
        cr:line_to(.5, .05)
        cr:curve_to(1.33, .3, .9, .8, .4, .8)
        cr:restore()

        local c = gtk.new"GdkColor"
        ui.fill_color:get_color(c)
        cr:set_source_gdk_color(c, ui.fill_color:get_alpha())
        cr:fill_preserve()

        ui.stroke_color:get_color(c)
        cr:set_source_gdk_color(c, ui.stroke_color:get_alpha())
        cr:set_line_width(ui.line_width:get_value())
        cr:set_line_cap(LINE_CAP[ui.line_cap:get_active() + 1])
        cr:set_line_join(LINE_JOIN[ui.line_join:get_active() + 1])
        cr:stroke_preserve()

        return false
    end

    canvas:set_size_request(canvas_wd, canvas_ht)
    canvas:connect("expose-event", handle_expose)

    return canvas
end

local function initialize_line_width (widget)
    widget:set_value(20)
    return widget
end

local function make_line_cap_icon (mode, height)
    local width = height * 4
    local surface = Cairo.image_surface_create("argb32", width, height)
    local cr = Cairo.context_create(surface)

    local halfline = height / 2
    cr:move_to(halfline, halfline)
    cr:line_to(width - halfline, halfline)

    cr:set_line_width(height)
    cr:set_line_cap(mode)
    cr:set_source_rgb(0, 0, 0)
    cr:stroke_preserve()

    local _, twopixels = cr:device_to_user_distance(2, 2)
    cr:set_line_width(twopixels)
    cr:set_line_cap("round")
    cr:set_source_rgb(1, 0, 0)
    cr:stroke()

    return surface:get_gdk_pixbuf()
end

local function make_line_join_icon (mode, height)
    local width = height * 4
    height = height * 2
    local linewd = height / 2
    local halfline = linewd / 2
    local surface = Cairo.image_surface_create("argb32", width, height)
    local cr = Cairo.context_create(surface)

    cr:move_to(halfline, height - halfline)
    cr:line_to(width / 2, halfline)
    cr:line_to(width - halfline, height - halfline)

    cr:set_line_width(linewd)
    cr:set_line_cap("round")
    cr:set_line_join(mode)
    cr:set_source_rgb(0, 0, 0)
    cr:stroke_preserve()

    local _, twopixels = cr:device_to_user_distance(2, 2)
    cr:set_line_width(twopixels)
    cr:set_line_cap("round")
    cr:set_source_rgb(1, 0, 0)
    cr:stroke()

    return surface:get_gdk_pixbuf()
end

local function make_icon_combo (combo, items, default_item, make_icon)
    -- Try to decide on a suitable height for the icons, based on the size
    -- of the whole combo box.  This is a bit arbitrary, but seems to come
    -- out as a good match for the font size.
    combo:map()     -- size won't be right until a real window is created
    local size = gtk.new"GtkRequisition"
    combo:size_request(size)
    local icon_height = size.height

    -- Create a list store to contain the icons and labels, and add them.
    local store = gtk.list_store_new(2, gtk.gdk_pixbuf_get_type(),
                                     gtk.G_TYPE_STRING)
    local iter = gtk.new("GtkTreeIter")
    local default_item_idx
    for i, style in ipairs(items) do
        store:append(iter)
        store:set(iter, 0, make_icon(style, icon_height), 1, style, -1)
        if style == default_item then default_item_idx = i - 1 end
    end
    combo:set_model(store)

    -- Set up renderers to display the icon next to the label.
    local renderer = gtk.cell_renderer_pixbuf_new()
    renderer:set_property("xpad", 6)
    renderer:set_property("ypad", 3)
    combo:pack_start(renderer, false)
    combo:set_attributes(renderer, "pixbuf", 0, nil)

    renderer = gtk.cell_renderer_text_new()
    combo:pack_start(renderer, true)
    combo:set_attributes(renderer, "text", 1, nil)

    combo:set_active(default_item_idx)
    return combo
end

local function initialize_line_cap_combo (combo)
    return make_icon_combo(combo, LINE_CAP, "butt", make_line_cap_icon)
end

local function initialize_line_join_combo (combo)
    return make_icon_combo(combo, LINE_JOIN, "miter", make_line_join_icon)
end

local function initialize_color (widget, r, g, b, a)
    local c = gtk.new"GdkColor"
    c.red   = r * 0xFFFF
    c.green = g * 0xFFFF
    c.blue  = b * 0xFFFF
    widget:set_color(c)
    widget:set_alpha(a * 0xFFFF)
    return widget
end

local function create_app_window ()
    -- Load GUI window layout.
    local filename = arg[0]:gsub("%.lua$", ".ui", 1)
    local builder = gtk.builder_new()
    local rc, err = builder:add_from_file(filename, nil)
    if rc == 0 then error(err.message) end

    ui.canvas = initialize_canvas_widget(builder:get_object("canvas"))
    ui.line_width = initialize_line_width(builder:get_object("line_width"))
    ui.line_cap = initialize_line_cap_combo(builder:get_object("line_cap"))
    ui.line_join = initialize_line_join_combo(builder:get_object("line_join"))
    ui.fill_color = initialize_color(builder:get_object("fill_color"),
                                     1, 1, 0, 1)
    ui.stroke_color = initialize_color(builder:get_object("stroke_color"),
                                       .5, 0, .5, 0.6)

    builder:connect_signals_full(event_handlers)
    return builder:get_object("appwindow")
end

local window = create_app_window()
window:connect("destroy", gtk.main_quit)
window:show()
gtk.main()

-- vi:ts=4 sw=4 expandtab