demo-changedisplay.lua 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. return function(parent, dir)
  2. local lgi = require 'lgi'
  3. local GLib = lgi.GLib
  4. local GObject = lgi.GObject
  5. local Gtk = lgi.Gtk
  6. local Gdk = lgi.Gdk
  7. -- Create main dialog window
  8. local window = Gtk.Dialog {
  9. title = "Change Screen or Display",
  10. transient_for = parent,
  11. default_width = 300,
  12. default_height = 400,
  13. buttons = {
  14. { Gtk.STOCK_CLOSE, Gtk.ResponseType.CLOSE },
  15. { "Change", Gtk.ResponseType.OK },
  16. },
  17. }
  18. -- Define column names for 'display' model.
  19. local DisplayColumn = {
  20. NAME = 1,
  21. DISPLAY = 2,
  22. }
  23. -- Define column numbers for 'screens' model.
  24. local ScreenColumn = {
  25. NUMBER = 1,
  26. SCREEN = 2,
  27. }
  28. -- Add content of the main dialog.
  29. window:get_content_area():add(
  30. Gtk.Box {
  31. orientation = 'VERTICAL',
  32. spacing = 5,
  33. border_width = 8,
  34. Gtk.Frame {
  35. label = "Display",
  36. Gtk.Box {
  37. orientation = 'HORIZONTAL',
  38. spacing = 8,
  39. border_width = 8,
  40. Gtk.ScrolledWindow {
  41. shadow_type = 'IN',
  42. hscrollbar_policy = 'NEVER',
  43. expand = true,
  44. Gtk.TreeView {
  45. id = 'displays',
  46. headers_visible = false,
  47. model = Gtk.ListStore.new {
  48. [DisplayColumn.NAME] = GObject.Type.STRING,
  49. [DisplayColumn.DISPLAY] = Gdk.Display,
  50. },
  51. Gtk.TreeViewColumn {
  52. title = "Name",
  53. { Gtk.CellRendererText(),
  54. { text = DisplayColumn.NAME } },
  55. },
  56. },
  57. },
  58. Gtk.Box {
  59. id = 'displays_box',
  60. orientation = 'VERTICAL',
  61. spacing = 5,
  62. Gtk.Button {
  63. id = 'display_open',
  64. label = "_Open...",
  65. use_underline = true,
  66. },
  67. Gtk.Button {
  68. id = 'display_close',
  69. label = "Close",
  70. use_underline = true,
  71. },
  72. },
  73. },
  74. },
  75. Gtk.Frame {
  76. label = "Screen",
  77. Gtk.Box {
  78. orientation = 'HORIZONTAL',
  79. spacing = 8,
  80. border_width = 8,
  81. Gtk.ScrolledWindow {
  82. shadow_type = 'IN',
  83. hscrollbar_policy = 'NEVER',
  84. expand = true,
  85. Gtk.TreeView {
  86. id = 'screens',
  87. headers_visible = false,
  88. model = Gtk.ListStore.new {
  89. [ScreenColumn.NUMBER] = GObject.Type.INT,
  90. [ScreenColumn.SCREEN] = Gdk.Screen,
  91. },
  92. Gtk.TreeViewColumn {
  93. title = "Number",
  94. { Gtk.CellRendererText(),
  95. { text = ScreenColumn.NUMBER } },
  96. },
  97. },
  98. },
  99. Gtk.Box {
  100. id = 'screens_box',
  101. orientation = 'VERTICAL',
  102. spacing = 5,
  103. },
  104. },
  105. },
  106. })
  107. local current_display, current_screen
  108. local display_selection = window.child.displays:get_selection()
  109. local screen_selection = window.child.screens:get_selection()
  110. display_selection.mode = 'BROWSE'
  111. function display_selection:on_changed()
  112. local model, iter = self:get_selected()
  113. if model then
  114. current_display = model[iter][DisplayColumn.DISPLAY]
  115. local screens = window.child.screens.model
  116. screens:clear()
  117. for i = 0, current_display:get_n_screens() - 1 do
  118. local iter = screens:append {
  119. [ScreenColumn.NUMBER] = i,
  120. [ScreenColumn.SCREEN] = current_display:get_screen(i),
  121. }
  122. if i == 0 then
  123. screen_selection:select_iter(iter)
  124. end
  125. end
  126. else
  127. current_display = nil
  128. end
  129. end
  130. screen_selection.mode = 'BROWSE'
  131. function screen_selection:on_changed()
  132. local model, iter = self:get_selected()
  133. current_screen = model and model[iter][ScreenColumn.SCREEN]
  134. end
  135. window.child.display_open:get_child().halign = 'START'
  136. window.child.display_close:get_child().halign = 'START'
  137. local size_group = Gtk.SizeGroup()
  138. size_group:add_widget(window.child.displays_box)
  139. size_group:add_widget(window.child.screens_box)
  140. function window.child.display_open:on_clicked()
  141. local dialog = Gtk.Dialog {
  142. title = "Open Display",
  143. transient_for = window,
  144. modal = true,
  145. buttons = {
  146. { Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL },
  147. { Gtk.STOCK_OK, Gtk.ResponseType.OK },
  148. },
  149. }
  150. dialog:set_default_response(Gtk.ResponseType.OK)
  151. local label = Gtk.Label {
  152. label = "Please enter the name of\nthe new display\n",
  153. }
  154. local entry = Gtk.Entry { activates_default = true }
  155. local content = dialog:get_content_area()
  156. content:add(label)
  157. content:add(entry)
  158. entry.has_focus = true
  159. dialog:show_all()
  160. while true do
  161. if dialog:run() ~= Gtk.ResponseType.OK then break end
  162. local name = entry.text
  163. if name ~= '' then
  164. local display = Gdk.Display.open(name)
  165. if display then break end
  166. label.label = ("Can't open display :\n\t%s\n"
  167. .. "please try another one\n"):format(name)
  168. end
  169. end
  170. dialog:destroy()
  171. end
  172. local function change_display()
  173. local screen = window:get_screen()
  174. local display = screen:get_display()
  175. local popup = Gtk.Window {
  176. type = 'POPUP',
  177. window_position = 'CENTER',
  178. modal = true,
  179. transient_for = window,
  180. Gtk.Frame {
  181. shadow_type = 'OUT',
  182. Gtk.Label {
  183. margin = 10,
  184. label = "Please select the toplevel\n"
  185. .. "to move to the new screen",
  186. },
  187. },
  188. }
  189. popup:set_screen(screen)
  190. popup:show_all()
  191. local cursor = Gdk.Cursor.new_for_display(display, 'CROSSHAIR')
  192. local toplevel
  193. local device = Gtk.get_current_event_device()
  194. local grab_status = device:grab(
  195. popup.window, 'APPLICATION', false,
  196. 'BUTTON_RELEASE_MASK', cursor, Gdk.CURRENT_TIME)
  197. if grab_status == 'SUCCESS' then
  198. -- Process events until user clicks.
  199. local clicked
  200. function popup:on_button_release_event()
  201. clicked = true
  202. return true
  203. end
  204. while not clicked do
  205. GLib.MainContext.default():iteration(true)
  206. end
  207. -- Find toplevel at current pointer position.
  208. local gdk_window = device:get_window_at_position()
  209. local widget = gdk_window and gdk_window:get_widget()
  210. toplevel = widget and widget:get_toplevel()
  211. if toplevel == popup then toplevel = nil end
  212. end
  213. popup:destroy()
  214. -- Make sure that grab is really broken.
  215. Gdk.flush()
  216. -- Switch target window to selected screen.
  217. if toplevel then
  218. toplevel:set_screen(current_screen)
  219. else
  220. display:beep()
  221. end
  222. end
  223. function window:on_response(response_id)
  224. if response_id == Gtk.ResponseType.OK then
  225. change_display()
  226. else
  227. self:destroy()
  228. end
  229. end
  230. -- Adds new display into the list and hooks it do that it is
  231. -- automatically removed when the display is closed.
  232. local function add_display(display)
  233. local store = window.child.displays.model
  234. local iter = store:append {
  235. [DisplayColumn.NAME] = display:get_name(),
  236. [DisplayColumn.DISPLAY] = display,
  237. }
  238. local path = store:get_path(iter)
  239. local handler_id = display.on_closed:connect(
  240. function(is_error)
  241. store:remove(store:get_iter(path))
  242. end)
  243. function window:on_destroy()
  244. GObject.signal_handler_disconnect(display, handler_id)
  245. end
  246. end
  247. -- Populate initial list of displays.
  248. local display_manager = Gdk.DisplayManager.get()
  249. for _, display in ipairs(display_manager:list_displays()) do
  250. add_display(display)
  251. end
  252. local handler_id = display_manager.on_display_opened:connect(
  253. function(display)
  254. add_display(display)
  255. end)
  256. function window:on_destroy()
  257. GObject.signal_handler_disconnect(display_manager, handler_id)
  258. end
  259. window:show_all()
  260. return window
  261. end,
  262. "Change Display",
  263. table.concat {
  264. [[Demonstrates migrating a window between different displays ]],
  265. [[and screens. A display is a mouse and keyboard with some number ]],
  266. [[of associated monitors. A screen is a set of monitors grouped ]],
  267. [[into a single physical work area. The neat thing about having ]],
  268. [[multiple displays is that they can be on a completely separate ]],
  269. [[computers, as long as there is a network connection to ]],
  270. [[the computer where the application is running.
  271. ]],
  272. [[Only some of the windowing systems where GTK+ runs have the concept ]],
  273. [[of multiple displays and screens. (The X Window System is the main ]],
  274. [[example.) Other windowing systems can only handle one keyboard ]],
  275. [[and mouse, and combine all monitors into a single screen.
  276. ]],
  277. [[This is a moderately complex example, and demonstrates:
  278. - Tracking the currently open displays and screens
  279. - Changing the screen for a window
  280. - Letting the user choose a window by clicking on it
  281. - Using Gtk.ListStore and Gtk.TreeView
  282. - Using Gtk.Dialog]]
  283. }