View.hx 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. package hide.ui;
  2. enum DisplayPosition {
  3. Left;
  4. Center;
  5. Right;
  6. Bottom;
  7. }
  8. typedef ViewOptions = { ?position : DisplayPosition, ?width : Int }
  9. @:keepSub @:allow(hide.Ide)
  10. class View<T> extends hide.comp.Component {
  11. var container : golden.Container;
  12. var watches : Array<{ keep : Bool, path : String, callb : Void -> Void }> = [];
  13. public var fullScreen(get,set) : Bool;
  14. public var keys(get,null) : Keys;
  15. public var state(default, null) : T;
  16. public var undo(default, null) = new hide.ui.UndoHistory();
  17. public var config(get, null) : Config;
  18. public var viewClass(get, never) : String;
  19. public var defaultOptions(get,never) : ViewOptions;
  20. var contentWidth(get,never) : Int;
  21. var contentHeight(get,never) : Int;
  22. var needRebuild : Bool;
  23. public function new(state:T) {
  24. super(null,null);
  25. element = null;
  26. this.state = state;
  27. ide = Ide.inst;
  28. @:privateAccess ide.currentFullScreen = null;
  29. }
  30. public function watch( filePath : String, onChange : Void -> Void, ?opts : { ?checkDelete : Bool, ?keepOnRebuild : Bool } ) {
  31. if( opts == null ) opts = {};
  32. ide.fileWatcher.register(filePath, onChange, opts.checkDelete);
  33. var w = { keep : opts.keepOnRebuild, path : filePath, callb : onChange };
  34. watches.push(w);
  35. return w;
  36. }
  37. public function unwatch(w) {
  38. if( watches.remove(w) )
  39. ide.fileWatcher.unregister(w.path, w.callb);
  40. }
  41. function get_config() {
  42. if( config == null )
  43. config = ide.currentConfig;
  44. return config;
  45. }
  46. function get_keys() {
  47. if( keys == null ) {
  48. keys = new Keys(null);
  49. keys.register("view.fullScreen", function() fullScreen = !fullScreen);
  50. }
  51. return keys;
  52. }
  53. public function getTitle() {
  54. return viewClass.split(".").pop();
  55. }
  56. public function onBeforeClose() {
  57. return true;
  58. }
  59. function get_viewClass() {
  60. return Type.getClassName(Type.getClass(this));
  61. }
  62. public function setClipboard( v : Dynamic, ?type : String ) {
  63. nw.Clipboard.get().set(ide.toJSON({ type : type == null ? viewClass : type, value : v }));
  64. }
  65. public function hasClipboard( ?type : String ) {
  66. if( type == null ) type = viewClass;
  67. var v : Dynamic = try haxe.Json.parse(nw.Clipboard.get().get()) catch( e : Dynamic ) null;
  68. return v != null && v.type == type;
  69. }
  70. public function getClipboard( ?type : String ) : Dynamic {
  71. if( type == null ) type = viewClass;
  72. var v : Dynamic = try haxe.Json.parse(nw.Clipboard.get().get()) catch( e : Dynamic ) null;
  73. return v == null || v.type != type ? null : v.value;
  74. }
  75. function syncTitle() {
  76. container.setTitle(getTitle());
  77. }
  78. function isKeysLocked( e : js.jquery.Event ) {
  79. var active = js.Browser.document.activeElement;
  80. if( active == null || e.altKey )
  81. return false;
  82. if( e.ctrlKey && e.keyCode != "A".code && e.keyCode != "C".code && e.keyCode != "V".code && e.keyCode != "X".code )
  83. return false;
  84. if( active.nodeName == "TEXTAREA" )
  85. return true;
  86. if( active.nodeName == "INPUT" ) {
  87. var type = (""+active.getAttribute("type")).toLowerCase();
  88. if( type == "text" || type == "null" || type == "number" || type == "password" || type == "date" )
  89. return true;
  90. }
  91. return false;
  92. }
  93. public function processKeyEvent( e : js.jquery.Event ) {
  94. if( isKeysLocked(e) ) {
  95. e.stopPropagation();
  96. return true;
  97. }
  98. var active = js.Browser.document.activeElement;
  99. for( el in element.find("[haskeys=true]").add(element).elements() ) {
  100. if(el.has(active).length == 0 && el[0] != active)
  101. continue;
  102. var keys = hide.ui.Keys.get(el);
  103. if( keys == null ) continue;
  104. if( keys.processEvent(e,config) )
  105. return true;
  106. }
  107. // global keys
  108. return keys.processEvent(e,config);
  109. }
  110. public function setContainer(cont) {
  111. this.container = cont;
  112. @:privateAccess ide.views.push(this);
  113. syncTitle();
  114. container.on("resize",function(_) {
  115. container.getElement().find('*').trigger('resize');
  116. onResize();
  117. });
  118. container.on("destroy",function(e) {
  119. destroy();
  120. });
  121. container.on("show", function(_) {
  122. // we are dragging the container tab
  123. if( container.parent.parent.config == null ) return;
  124. haxe.Timer.delay(onActivate,0);
  125. });
  126. container.getElement().keydown(function(e) {
  127. processKeyEvent(e);
  128. });
  129. container.on("tab", function(e) {
  130. container.tab.onClose = function() {
  131. return onBeforeClose();
  132. };
  133. container.tab.element.contextmenu(function(e) {
  134. var menu = buildTabMenu();
  135. if( menu.length > 0 ) new hide.comp.ContextMenu(menu);
  136. e.preventDefault();
  137. });
  138. });
  139. untyped cont.parent.__view = this;
  140. element = cont.getElement();
  141. }
  142. public final function rebuild() {
  143. function checkRebuild() {
  144. if( container == null || !needRebuild ) return;
  145. if( !isActive() ) {
  146. haxe.Timer.delay(checkRebuild,200);
  147. return;
  148. }
  149. needRebuild = false;
  150. onRebuild();
  151. }
  152. if( !needRebuild ) {
  153. needRebuild = true;
  154. checkRebuild();
  155. }
  156. }
  157. function onRebuild() {
  158. for( w in watches.copy() )
  159. if( !w.keep ) {
  160. ide.fileWatcher.unregister(w.path, w.callb);
  161. watches.remove(w);
  162. }
  163. syncTitle();
  164. element.empty();
  165. element.off();
  166. onDisplay();
  167. }
  168. function onDisplay() {
  169. element.text(viewClass+(state == null ? "" : " "+state));
  170. }
  171. public function onResize() {
  172. }
  173. public function onActivate() {
  174. }
  175. public function isActive() {
  176. return container != null && !container.isHidden;
  177. }
  178. public function onDragDrop(items : Array<String>, isDrop : Bool) {
  179. return false;
  180. }
  181. function toString() {
  182. return Type.getClassName(Type.getClass(this)) + (this.state == null ? "" : "("+haxe.Json.stringify(this.state)+")");
  183. }
  184. /**
  185. Gives focus if part of a tab group
  186. **/
  187. public function activate() {
  188. if( container != null ) {
  189. var cur = container.parent.parent.getActiveContentItem();
  190. if( cur != container.parent )
  191. container.parent.parent.setActiveContentItem(container.parent);
  192. }
  193. }
  194. function saveState() {
  195. container.setState(state);
  196. }
  197. public function close() {
  198. if( container != null )
  199. container.close();
  200. }
  201. function destroy() {
  202. for( w in watches.copy() )
  203. ide.fileWatcher.unregister(w.path, w.callb);
  204. watches = [];
  205. @:privateAccess ide.views.remove(this);
  206. for( c in container.getElement().find("canvas") ) {
  207. var s : hide.comp.Scene = Reflect.field(c, "__scene");
  208. if( s != null )
  209. s.dispose();
  210. }
  211. element = null;
  212. }
  213. function buildTabMenu() : Array<hide.comp.ContextMenu.ContextMenuItem> {
  214. if( @:privateAccess ide.subView != null )
  215. return [];
  216. return [
  217. { label : "Close", click : close },
  218. { label : "Close Others", click : function() for( v in @:privateAccess ide.views.copy() ) if( v != this && v.container.tab.header == container.tab.header ) v.close() },
  219. { label : "Close All", click : function() for( v in @:privateAccess ide.views.copy() ) if( v.container.tab.header == container.tab.header ) v.close() },
  220. ];
  221. }
  222. function get_contentWidth() return container.width;
  223. function get_contentHeight() return container.height;
  224. function get_defaultOptions() return viewClasses.get(Type.getClassName(Type.getClass(this))).options;
  225. function get_fullScreen() return container != null && container.getElement().is(".fullScreen");
  226. function set_fullScreen(v) {
  227. if( fullScreen == v )
  228. return v;
  229. if( container != null ) {
  230. new Element(".fullScreen").removeClass("fullScreen");
  231. container.getElement().toggleClass("fullScreen", v);
  232. new Element("body").toggleClass("fullScreenMode",v);
  233. }
  234. @:privateAccess if( v ) ide.currentFullScreen = this else ide.currentFullScreen = null;
  235. if( !ide.isCDB ) ide.setFullscreen(v);
  236. return v;
  237. }
  238. public static var viewClasses = new Map<String,{ name : String, cl : Class<View<Dynamic>>, options : ViewOptions }>();
  239. public static function register<T>( cl : Class<View<T>>, ?options : ViewOptions ) {
  240. var name = Type.getClassName(cl);
  241. if( viewClasses.exists(name) )
  242. return null;
  243. if( options == null )
  244. options = {}
  245. if( options.position == null )
  246. options.position = Center;
  247. viewClasses.set(name, { name : name, cl : cl, options : options });
  248. return null;
  249. }
  250. }