main.lua 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. ------------------------------------------------------------------------------
  2. --
  3. -- GTK demo
  4. --
  5. -- Copyright (c) 2012 Pavel Holejsovsky
  6. -- Licensed under the MIT license:
  7. -- http://www.opensource.org/licenses/mit-license.php
  8. --
  9. -- This is a port of gtk-demo, licensed as (LGPL):
  10. --
  11. -- This library is free software; you can redistribute it and/or
  12. -- modify it under the terms of the GNU Library General Public
  13. -- License as published by the Free Software Foundation; either
  14. -- version 2 of the License, or (at your option) any later version.
  15. --
  16. -- This library is distributed in the hope that it will be useful,
  17. -- but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  19. -- Library General Public License for more details.
  20. --
  21. -- You should have received a copy of the GNU Library General Public
  22. -- License along with this library; if not, write to the
  23. -- Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  24. -- Boston, MA 02111-1307 USA.
  25. --
  26. ------------------------------------------------------------------------------
  27. local loadstring = loadstring or load
  28. local lgi = require 'lgi'
  29. local GObject = lgi.GObject
  30. local Gio = lgi.Gio
  31. local Gtk = lgi.require('Gtk', '3.0')
  32. local Pango = lgi.Pango
  33. local GdkPixbuf = lgi.GdkPixbuf
  34. local GtkSource = lgi.GtkSource
  35. -- Create package for the whole demo.
  36. local GtkDemo = lgi.package 'GtkDemo'
  37. local assert = lgi.assert
  38. local DemoListColumn = {
  39. TITLE = 1,
  40. INFO = 2,
  41. SOURCE = 3,
  42. STYLE = 4,
  43. WINDOW = 5,
  44. }
  45. local model = Gtk.TreeStore.new {
  46. [DemoListColumn.TITLE] = GObject.Type.STRING,
  47. [DemoListColumn.INFO] = GObject.Type.STRING,
  48. [DemoListColumn.SOURCE] = GObject.Type.STRING,
  49. [DemoListColumn.STYLE] = Pango.Style,
  50. [DemoListColumn.WINDOW] = Gtk.Window,
  51. }
  52. -- Enumerate all demo files in this directory and fill tree model with
  53. -- data about them.
  54. local dir = Gio.File.new_for_commandline_arg(arg[0]):get_parent()
  55. local enum = dir:enumerate_children('standard::name', 'NONE')
  56. while true do
  57. local info, err = enum:next_file()
  58. if not info then assert(not err, err) break end
  59. local name = info:get_name()
  60. if name:match('^demo-(.+)%.lua$') then
  61. -- Load source and execute it, to get title and info description.
  62. local source = tostring(assert(dir:get_child(name):load_contents()))
  63. local run, title, info = assert(loadstring(source))()
  64. -- Parse title and create an appropriate tree structure.
  65. local parent = nil
  66. for item in title:gmatch('([^/]+)/+') do
  67. -- Try to find parent in current level.
  68. local new_parent
  69. for i, row in model:pairs(parent) do
  70. if row[DemoListColumn.TITLE] == item then
  71. new_parent = i
  72. break
  73. end
  74. end
  75. if not new_parent then
  76. new_parent = model:append(parent, { [DemoListColumn.TITLE] = item })
  77. end
  78. parent = new_parent
  79. end
  80. -- Add new item from found or created parent element.
  81. model:append(parent, {
  82. [DemoListColumn.TITLE] = title:match('([^/]+)$'),
  83. [DemoListColumn.INFO] = '\n' .. info,
  84. [DemoListColumn.SOURCE] = source,
  85. [DemoListColumn.STYLE] = 'NORMAL',
  86. })
  87. end
  88. end
  89. enum:close()
  90. -- Use sorted tree model, so that demos are in alphabetical order.
  91. local sorted_model = Gtk.TreeModelSort { model = model }
  92. sorted_model:set_sort_func(
  93. DemoListColumn.TITLE,
  94. function(model, a, b)
  95. a = model[a][DemoListColumn.TITLE]
  96. b = model[b][DemoListColumn.TITLE]
  97. if a == b then return 0
  98. elseif a < b then return -1
  99. else return 1 end
  100. end)
  101. sorted_model:set_sort_column_id(DemoListColumn.TITLE, Gtk.SortType.ASCENDING)
  102. -- Create whole widget hierarchy.
  103. local window = Gtk.Window {
  104. default_width = 600, default_height = 400,
  105. title = "GTK+ LGI Code Demos",
  106. Gtk.Paned {
  107. orientation = 'HORIZONTAL',
  108. Gtk.ScrolledWindow {
  109. hscrollbar_policy = 'NEVER',
  110. shadow_type = 'IN',
  111. Gtk.TreeView {
  112. id = 'tree',
  113. model = sorted_model,
  114. Gtk.TreeViewColumn {
  115. title = "Widget (double click for demo)",
  116. { Gtk.CellRendererText {}, { text = DemoListColumn.TITLE,
  117. style = DemoListColumn.STYLE } }
  118. },
  119. },
  120. },
  121. Gtk.Notebook {
  122. {
  123. tab_label = "Info",
  124. Gtk.ScrolledWindow {
  125. shadow_type = 'IN',
  126. Gtk.TextView {
  127. id = 'info',
  128. buffer = Gtk.TextBuffer {
  129. tag_table = Gtk.TextTagTable {
  130. Gtk.TextTag { name = 'title', font = 'sans 18' }
  131. }
  132. },
  133. editable = false, cursor_visible = false,
  134. wrap_mode = 'WORD',
  135. pixels_above_lines = 2,
  136. pixels_below_lines = 2,
  137. }
  138. }
  139. },
  140. {
  141. tab_label = "Source",
  142. Gtk.ScrolledWindow {
  143. shadow_type = 'IN',
  144. GtkSource.View {
  145. id = 'source',
  146. buffer = GtkSource.Buffer {
  147. language = GtkSource.LanguageManager.get_default():
  148. get_language('lua'),
  149. },
  150. editable = false, cursor_visible = false,
  151. wrap_mode = 'NONE',
  152. }
  153. },
  154. },
  155. },
  156. }
  157. }
  158. -- Use monospace font for source view.
  159. window.child.source:override_font(
  160. Pango.FontDescription.from_string('monospace'))
  161. -- Appends text with specified tag into the buffer.
  162. local function append_text(buffer, text, tag)
  163. local end_iter = buffer:get_end_iter()
  164. local offset = end_iter:get_offset()
  165. buffer:insert(end_iter, text, -1)
  166. if tag then
  167. end_iter = buffer:get_end_iter()
  168. buffer:apply_tag_by_name(tag, buffer:get_iter_at_offset(offset),
  169. end_iter)
  170. end
  171. end
  172. -- Handle changing the treeview selection.
  173. local selection = window.child.tree:get_selection()
  174. selection.mode = 'BROWSE'
  175. function selection:on_changed()
  176. local model, iter = self:get_selected()
  177. if not model then return end
  178. -- Load source view.
  179. local source = window.child.source.buffer
  180. source.text = model[iter][DemoListColumn.SOURCE] or ''
  181. -- Load info view.
  182. local info = window.child.info.buffer
  183. info:delete(info:get_bounds())
  184. append_text(info, model[iter][DemoListColumn.TITLE], 'title')
  185. append_text(info, model[iter][DemoListColumn.INFO] or '')
  186. end
  187. -- Handle activation of the treeview - running the demo.
  188. function window.child.tree:on_row_activated(path)
  189. -- Convert both model and path to underlying model, not sorted one.
  190. local model = self.model.model
  191. local orig_path = path
  192. path = self.model:convert_path_to_child_path(path)
  193. -- Get source as string from the model.
  194. local row = model[path]
  195. local source = row[DemoListColumn.SOURCE]
  196. local child_window = row[DemoListColumn.WINDOW]
  197. if child_window then
  198. -- Window already existed, destroy it.
  199. child_window:destroy()
  200. elseif source then
  201. -- Run it, get run function from it and run it again with toplevel
  202. -- window and data directory to use.
  203. local func = (loadstring or load)(source)()
  204. child_window = func(window, dir)
  205. if child_window then
  206. -- Signalize that the window is active.
  207. row[DemoListColumn.WINDOW] = child_window
  208. row[DemoListColumn.STYLE] = 'ITALIC'
  209. -- Register destroy signal which will remove the window from
  210. -- the store and make style back to normal.
  211. function child_window:on_destroy()
  212. local row = model[path]
  213. row[DemoListColumn.STYLE] = 'NORMAL'
  214. row[DemoListColumn.WINDOW] = nil
  215. end
  216. end
  217. end
  218. end
  219. local app = Gtk.Application { application_id = 'org.lgi.gtk-demo' }
  220. function app:on_activate()
  221. -- Setup default application icon.
  222. local pixbuf, err = GdkPixbuf.Pixbuf.new_from_file(
  223. dir:get_child('gtk-logo-rgb.gif'):get_path())
  224. if pixbuf then
  225. -- Add transparency instead of white background.
  226. local alpha = pixbuf:add_alpha(true, 0xff, 0xff, 0xff)
  227. Gtk.Window.set_default_icon(alpha)
  228. else
  229. -- Report the error.
  230. local dialog = Gtk.MessageDialog {
  231. message_type = 'ERROR', buttons = 'CLOSE',
  232. text = ("Failed to read icon file: %s"):format(err),
  233. on_response = Gtk.Widget.destroy
  234. }
  235. dialog:show_all()
  236. end
  237. -- Assign the window as the application one and display it.
  238. window.application = self
  239. window:show_all()
  240. end
  241. -- Run the whole application with all commandline arguments.
  242. app:run { arg[0], ... }