crimson 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/gempush.yml +44 -0
  3. data/.gitignore +9 -0
  4. data/.travis.yml +4 -0
  5. data/.vscode/launch.json +14 -0
  6. data/CODE_OF_CONDUCT.md +49 -0
  7. data/Gemfile +11 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +41 -0
  10. data/Rakefile +10 -0
  11. data/bin/console +14 -0
  12. data/bin/setup +8 -0
  13. data/crimson.gemspec +25 -0
  14. data/example/ets.rb +22 -0
  15. data/example/example.rb +66 -0
  16. data/lib/crimson.js +10 -0
  17. data/lib/crimson.rb +5 -0
  18. data/lib/crimson/client.rb +86 -0
  19. data/lib/crimson/icons/close.png +0 -0
  20. data/lib/crimson/icons/hide.png +0 -0
  21. data/lib/crimson/icons/resize.png +0 -0
  22. data/lib/crimson/mash.rb +9 -0
  23. data/lib/crimson/model.rb +98 -0
  24. data/lib/crimson/model_change.rb +20 -0
  25. data/lib/crimson/notification_bus.rb +35 -0
  26. data/lib/crimson/object.rb +196 -0
  27. data/lib/crimson/server.rb +69 -0
  28. data/lib/crimson/utilities.rb +13 -0
  29. data/lib/crimson/version.rb +3 -0
  30. data/lib/crimson/webserver.rb +17 -0
  31. data/lib/crimson/widgets/bottom_resizer.rb +22 -0
  32. data/lib/crimson/widgets/desktop.rb +44 -0
  33. data/lib/crimson/widgets/form.rb +12 -0
  34. data/lib/crimson/widgets/input.rb +10 -0
  35. data/lib/crimson/widgets/left_resizer.rb +27 -0
  36. data/lib/crimson/widgets/resizer.rb +38 -0
  37. data/lib/crimson/widgets/right_resizer.rb +22 -0
  38. data/lib/crimson/widgets/taskbar.rb +0 -0
  39. data/lib/crimson/widgets/titlebar.rb +75 -0
  40. data/lib/crimson/widgets/top_resizer.rb +27 -0
  41. data/lib/crimson/widgets/window.rb +134 -0
  42. data/lib/html/template.html +13 -0
  43. data/lib/javascript/Application.js +13 -0
  44. data/lib/javascript/Logger.js +11 -0
  45. data/lib/javascript/MessageHandler.js +142 -0
  46. data/lib/javascript/ObjectManager.js +33 -0
  47. data/lib/javascript/ServerInteractor.js +42 -0
  48. data/lib/javascript/Utilities.js +26 -0
  49. metadata +135 -0
@@ -0,0 +1,3 @@
1
+ module Crimson
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,17 @@
1
+ require 'sinatra/base'
2
+
3
+ module Crimson
4
+ class WebServer < Sinatra::Base
5
+ set :public_folder, "#{__dir__}/../"
6
+
7
+ def initialize(template)
8
+ super()
9
+
10
+ @template = template
11
+ end
12
+
13
+ get '/' do
14
+ @template
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'resizer'
4
+
5
+ module Crimson
6
+ class BottomResizer < Resizer
7
+ def initialize
8
+ super('ns-resize')
9
+
10
+ style.width = '100%'
11
+ style.height = '5px'
12
+ style.position = 'absolute'
13
+ style.bottom = 0
14
+ style.left = 0
15
+ end
16
+
17
+ def on_mousemove(data)
18
+ parent.style.height = "#{data.clientY - parent.style.top.delete_suffix('px').to_i - data.movementY}px"
19
+ parent.commit!
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../object'
4
+ require_relative 'window'
5
+
6
+ module Crimson
7
+ class Desktop < Crimson::Object
8
+ def initialize
9
+ super(:div)
10
+
11
+ self.style = {
12
+ "height": '100vh',
13
+ "width": '100vw',
14
+ "max-height": '100vh',
15
+ "max-width": '100vw',
16
+ "overflow": 'hidden',
17
+ "position": 'fixed',
18
+ "left": "0px",
19
+ "top": "0px"
20
+ }
21
+
22
+ self.ondragover = 'event.preventDefault();'
23
+ self.ondrop = 'event.preventDefault();'
24
+
25
+ on('drop', method(:on_drop))
26
+ end
27
+
28
+ def on_drop(data)
29
+ window = find_descendant(data.target.to_sym).parent
30
+
31
+ unless window.nil?
32
+ window.style.left = "#{data.clientX + window.offset.left}px"
33
+ window.style.top = "#{data.clientY + window.offset.top}px"
34
+ window.commit!
35
+ end
36
+ end
37
+
38
+ def create_window(*args)
39
+ window = Crimson::Window.new(*args)
40
+ add(window)
41
+ window
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../object'
4
+
5
+ module Crimson
6
+ class Form < Crimson::Object
7
+ def initialize
8
+ super(:form)
9
+ self.onsubmit = "return false"
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,10 @@
1
+ require_relative '../object'
2
+
3
+ module Crimson
4
+ class Input < Crimson::Object
5
+ def initialize(type)
6
+ super(:input)
7
+ self.type = type
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'resizer'
4
+
5
+ module Crimson
6
+ class LeftResizer < Resizer
7
+ def initialize
8
+ super('ew-resize')
9
+
10
+ style.width = '5px'
11
+ style.height = '100%'
12
+ style.position = 'absolute'
13
+ style.top = 0
14
+ style.left = 0
15
+ end
16
+
17
+ def on_mousemove(data)
18
+ min_width = parent.style.minWidth.delete_suffix('px').to_i
19
+ new_width = parent.style.width.delete_suffix('px').to_i - data.movementX
20
+ new_left = parent.style.left.delete_suffix('px').to_i + data.movementX
21
+
22
+ parent.style.width = "#{new_width}px"
23
+ parent.style.left = "#{new_left}px" unless new_width < min_width
24
+ parent.commit!
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../object'
4
+
5
+ module Crimson
6
+ class Resizer < Crimson::Object
7
+ attr_reader :cursor
8
+
9
+ def initialize(cursor)
10
+ super(:div)
11
+
12
+ @cursor = cursor
13
+ self.style = {}
14
+
15
+ enable
16
+ end
17
+
18
+ def enable
19
+ style.cursor = cursor
20
+ on(:mousedown, method(:on_mousedown))
21
+ end
22
+
23
+ def disable
24
+ style.cursor = "auto"
25
+ un(:mousedown, method(:on_mousedown))
26
+ end
27
+
28
+ def on_mousedown(_data)
29
+ parent.parent.on(:mousemove, method(:on_mousemove))
30
+ parent.parent.on(:mouseup, method(:on_mouseup))
31
+ end
32
+
33
+ def on_mouseup(_data)
34
+ parent.parent.un(:mousemove, method(:on_mousemove))
35
+ parent.parent.un(:mouseup, method(:on_mouseup))
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'resizer'
4
+
5
+ module Crimson
6
+ class RightResizer < Resizer
7
+ def initialize
8
+ super('ew-resize')
9
+
10
+ style.width = '5px'
11
+ style.height = '100%'
12
+ style.position = 'absolute'
13
+ style.top = 0
14
+ style.right = 0
15
+ end
16
+
17
+ def on_mousemove(data)
18
+ parent.style.width = "#{data.clientX - parent.style.left.delete_suffix('px').to_i - data.movementX}px"
19
+ parent.commit!
20
+ end
21
+ end
22
+ end
File without changes
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../object'
4
+
5
+ module Crimson
6
+ class Titlebar < Crimson::Object
7
+ attr_reader :title, :hide_button, :resize_button, :close_button
8
+
9
+ def initialize(window_title)
10
+ super(:div)
11
+
12
+ @title = Object.new(:div)
13
+ self.title = window_title
14
+ title.style.color = 'white'
15
+ title.style.fontFamily = 'Verdana'
16
+ title.style.fontWeight = 'bold'
17
+ title.style.textAlign = 'center'
18
+ title.style.margin = 'auto'
19
+ add(title)
20
+
21
+ @hide_button = Object.new(:img)
22
+ hide_button.src = 'crimson/icons/hide.png'
23
+ hide_button.style.padding = '10px 20px'
24
+ hide_button.on('mouseenter') do |_data|
25
+ hide_button.style.backgroundColor = 'rgba(0, 0, 0, 0.6)'
26
+ hide_button.commit!
27
+ end
28
+ hide_button.on('mouseleave') do |_data|
29
+ hide_button.style.backgroundColor = 'rgba(0, 0, 0, 0.0)'
30
+ hide_button.commit!
31
+ end
32
+ add(hide_button)
33
+
34
+ @resize_button = Object.new(:img)
35
+ resize_button.src = 'crimson/icons/resize.png'
36
+ resize_button.style.padding = '8px 16px'
37
+ resize_button.on('mouseenter') do |_data|
38
+ resize_button.style.backgroundColor = 'rgba(0, 0, 0, 0.6)'
39
+ resize_button.commit!
40
+ end
41
+ resize_button.on('mouseleave') do |_data|
42
+ resize_button.style.backgroundColor = 'rgba(0, 0, 0, 0.0)'
43
+ resize_button.commit!
44
+ end
45
+ add(resize_button)
46
+
47
+ @close_button = Object.new(:img)
48
+ close_button.src = 'crimson/icons/close.png'
49
+ close_button.style.padding = '10px 20px'
50
+ close_button.on('mouseenter') do |_data|
51
+ close_button.style.backgroundColor = 'rgba(0, 0, 0, 0.6)'
52
+ close_button.commit!
53
+ end
54
+ close_button.on('mouseleave') do |_data|
55
+ close_button.style.backgroundColor = 'rgba(0, 0, 0, 0.0)'
56
+ close_button.commit!
57
+ end
58
+ add(close_button)
59
+
60
+ style.backgroundColor = '#2ecc71'
61
+ style.width = '100%'
62
+ style.height = '35px'
63
+ style.display = 'flex'
64
+ style.userSelect = 'none'
65
+ end
66
+
67
+ def title=(window_title)
68
+ title.innerHTML = window_title
69
+ end
70
+
71
+ def buttons
72
+ [hide_button, resize_button, close_button]
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'resizer'
4
+
5
+ module Crimson
6
+ class TopResizer < Resizer
7
+ def initialize
8
+ super('ns-resize')
9
+
10
+ style.width = '100%'
11
+ style.height = '5px'
12
+ style.position = 'absolute'
13
+ style.top = 0
14
+ style.left = 0
15
+ end
16
+
17
+ def on_mousemove(data)
18
+ min_height = parent.style.minHeight.delete_suffix('px').to_i
19
+ new_height = parent.style.height.delete_suffix('px').to_i - data.movementY
20
+ new_top = parent.style.top.delete_suffix('px').to_i + data.movementY
21
+
22
+ parent.style.height = "#{new_height}px"
23
+ parent.style.top = "#{new_top}px" unless new_height < min_height
24
+ parent.commit!
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,134 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'hashie'
4
+ require_relative '../object'
5
+ require_relative 'titlebar'
6
+ require_relative 'left_resizer'
7
+ require_relative 'right_resizer'
8
+ require_relative 'top_resizer'
9
+ require_relative 'bottom_resizer'
10
+
11
+ module Crimson
12
+ class Window < Crimson::Object
13
+ attr_reader :offset, :titlebar, :previous_dimensions, :content
14
+ attr_reader :resizable, :left_resizer, :right_resizer, :top_resizer, :bottom_resizer
15
+
16
+ def initialize(title, width = '800px', height = '600px')
17
+ super(:div)
18
+
19
+ @resizable = true
20
+ @offset = Hashie::Mash.new
21
+ @previous_dimensions = Hashie::Mash.new
22
+
23
+ @titlebar = Titlebar.new(title)
24
+ titlebar.draggable = true
25
+ titlebar.on('dragstart', method(:on_dragstart))
26
+ titlebar.resize_button.on('click') do |_data|
27
+ maximized? ? minimize : maximize
28
+ commit_tree!
29
+ end
30
+ titlebar.close_button.on('click') do |_data|
31
+ old_parent = parent
32
+ old_parent.remove(self)
33
+ old_parent.commit_tree!
34
+ end
35
+ add(titlebar)
36
+
37
+ @left_resizer = LeftResizer.new
38
+ add(left_resizer)
39
+
40
+ @right_resizer = RightResizer.new
41
+ add(right_resizer)
42
+
43
+ @top_resizer = TopResizer.new
44
+ top_resizer.style.height = '10px'
45
+ add(top_resizer)
46
+
47
+ @bottom_resizer = BottomResizer.new
48
+ add(bottom_resizer)
49
+
50
+ @content = Crimson::Object.new(:div)
51
+ content.style.width = '100%'
52
+ content.style.height = "calc(100% - #{titlebar.style.height})"
53
+ content.style.padding = 0
54
+ content.style.margin = 0
55
+ add(content)
56
+
57
+ style.left = '0px'
58
+ style.top = '0px'
59
+ style.height = height
60
+ style.width = width
61
+ style.minHeight = '200px'
62
+ style.minWidth = '400px'
63
+ style.position = 'absolute'
64
+ style.backgroundColor = 'white'
65
+ style.boxShadow = '0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19)'
66
+
67
+ on('mousedown', method(:on_mousedown))
68
+ end
69
+
70
+ def content=(widget)
71
+ content.children.each { |child| content.remove(child) }
72
+ content.add(widget)
73
+ end
74
+
75
+ def on_dragstart(data)
76
+ offset.top = style.top.delete_suffix('px').to_i - data.clientY
77
+ offset.left = style.left.delete_suffix('px').to_i - data.clientX
78
+ end
79
+
80
+ def on_mousedown(_data)
81
+ parent.move(self, -1)
82
+ parent.commit_tree!(:children)
83
+ end
84
+
85
+ def maximized?
86
+ style.height == '100vh' && style.width == '100vw'
87
+ end
88
+
89
+ def minimized?
90
+ !maximized?
91
+ end
92
+
93
+ def resizable?
94
+ resizable
95
+ end
96
+
97
+ def resizable=(bool)
98
+ @resizable = bool
99
+
100
+ if resizable?
101
+ resizers.each(&:enable)
102
+ else
103
+ resizers.each(&:disable)
104
+ end
105
+ end
106
+
107
+ def resizers
108
+ [left_resizer, right_resizer, top_resizer, bottom_resizer]
109
+ end
110
+
111
+ def maximize
112
+ self.resizable = false
113
+
114
+ previous_dimensions.top = style.top.dup
115
+ previous_dimensions.left = style.left.dup
116
+ previous_dimensions.width = style.width.dup
117
+ previous_dimensions.height = style.height.dup
118
+
119
+ style.top = '0px'
120
+ style.left = '0px'
121
+ style.width = '100vw'
122
+ style.height = '100vh'
123
+ end
124
+
125
+ def minimize
126
+ style.top = previous_dimensions.top.dup
127
+ style.left = previous_dimensions.left.dup
128
+ style.width = previous_dimensions.width.dup
129
+ style.height = previous_dimensions.height.dup
130
+
131
+ self.resizable = true
132
+ end
133
+ end
134
+ end