NotePad apps

This commit is contained in:
Xuan Sang LE 2018-01-28 02:01:21 +01:00
parent ea074d9690
commit c048824ce4
13 changed files with 210 additions and 60 deletions

3
TODO Normal file
View File

@ -0,0 +1,3 @@
VFS mountpoints object (selected) is changed between apps, which is not a good ideal
API handler readfile function return JSON object in reading JSON file, need to be text
Some error in event handle using observable

View File

@ -3,7 +3,6 @@ class BaseApplication extends this.OS.GUI.BaseModel
super name, args super name, args
_OS.setting.applications[@name] = {} if not _OS.setting.applications[@name] _OS.setting.applications[@name] = {} if not _OS.setting.applications[@name]
@setting = _OS.setting.applications[@name] @setting = _OS.setting.applications[@name]
init: -> init: ->
me = @ me = @
# first register some base event to the app # first register some base event to the app

View File

@ -179,7 +179,7 @@ class YesNoDialog extends BasicDialog
}, },
{ {
label: "No", onclick: (d) -> label: "No", onclick: (d) ->
d handler false if dhandler d.handler false if d.handler
d.quit() d.quit()
} }
], ],
@ -234,4 +234,42 @@ class AboutDialog extends BaseDialog
rows.push [ { value: k }, { value: v } ] for k, v of mt.info rows.push [ { value: k }, { value: v } ] for k, v of mt.info
(@find "mygrid").set "rows", rows (@find "mygrid").set "rows", rows
this.OS.register "AboutDialog", AboutDialog this.OS.register "AboutDialog", AboutDialog
class FileDiaLog extends BaseDialog
constructor: () ->
super "FileDiaLog"
init: () ->
@render "resources/schemes/filedialog.html"
main: () ->
fileview = @find "fileview"
location = @find "location"
me = @
@scheme.set "apptitle", "#{@title}"
fileview.set "fetch", (e, f) ->
return unless e.child
e.child.path.asFileHandler().read (d) ->
return me.error "Resource not found #{e.child.path}" if d.error
f d.result
location.set "onlistselect", (e) ->
return unless e and e.data.path
e.data.path.asFileHandler().read (d) ->
if(d.error)
return me.error "Resource not found #{e.data.path}"
fileview.set "path", e.data.path
fileview.set "data", d.result
location.set "items", ( i for i in @systemsetting.VFS.mountpoints when i.type isnt "app" )
location.set "selected", 0 unless location.get "selected"
(@find "bt-ok").set "onbtclick", (e) ->
f = fileview.get "selectedFile"
return unless f
return unless f.type is "file" or ( me.data and me.data.seldir )
me.handler f if me.handler
me.quit()
(@find "bt-cancel").set "onbtclick", (e) ->
me.quit()
this.OS.register "FileDiaLog", FileDiaLog

View File

@ -3,6 +3,7 @@ class BaseModel
@observable = riot.observable() @observable = riot.observable()
@_api = self.OS.API @_api = self.OS.API
@_gui = self.OS.GUI @_gui = self.OS.GUI
@systemsetting = self.OS.setting
me = @ me = @
@on "exit", () -> me.quit() @on "exit", () -> me.quit()
@host = "#desktop" @host = "#desktop"

View File

@ -13,7 +13,7 @@ self.OS.API =
url: p, url: p,
contentType: 'application/json', contentType: 'application/json',
data: JSON.stringify d, data: JSON.stringify d,
dataType: 'json', dataType: 'json', # data type need to be configurable
success: null success: null
} }
#$.getJSON p, d #$.getJSON p, d
@ -89,7 +89,7 @@ self.OS.API =
get: (p, c, f) -> get: (p, c, f) ->
q = _courrier.getMID() q = _courrier.getMID()
_API.loading q, p _API.loading q, p
$.get p $.get p # TODO add return type setting support
.done (data) -> .done (data) ->
_API.loaded q, p, "OK" _API.loaded q, p, "OK"
c(data) c(data)

View File

@ -10,6 +10,7 @@ self.OS or=
applications: {} applications: {}
desktop: {} desktop: {}
appearance: {} appearance: {}
VFS: {}
courrier: courrier:
observable: riot.observable() observable: riot.observable()
quota: 0 quota: 0

View File

@ -61,16 +61,19 @@ self.OS.GUI =
_courrier.osfail "Cannot read service script: #{srv} ", e, s _courrier.osfail "Cannot read service script: #{srv} ", e, s
appsByMime: (mime) -> appsByMime: (mime) ->
metas = ( a.meta for k, a of _OS.APP when a.type is 1) metas = ( a.meta for k, a of _OS.APP when a and a.type is 1)
mimes = ( m.mimes for m in metas ) mimes = ( m.mimes for m in metas when m)
apps = [] apps = []
# search app by mimes # search app by mimes
f = ( arr, idx ) -> f = ( arr, idx ) ->
arr.filter (m, i) -> try
if mime.match (new RegExp m, "g") arr.filter (m, i) ->
apps.push metas[idx] if mime.match (new RegExp m, "g")
apps.push metas[idx]
return false
return false return false
return false catch e
_courrier.osfail "Find app by mimes #{mime}", e, mime
( f m, i if m ) for m, i in mimes ( f m, i if m ) for m, i in mimes
return apps return apps
@ -265,6 +268,14 @@ self.OS.GUI =
_OS.setting.applications = conf.applications if conf.applications _OS.setting.applications = conf.applications if conf.applications
_OS.setting.appearance = conf.appearance if conf.appearance _OS.setting.appearance = conf.appearance if conf.appearance
_OS.setting.user = conf.user _OS.setting.user = conf.user
_OS.setting.VFS = conf.VFS if conf.VFS
_OS.setting.VFS.mountpoints = [ #TODO: multi app try to write to this object, it neet to be cloned
{ text: "Applications", path: 'app:///', iconclass: "fa fa-adn", type: "app" },
{ text: "Home", path: 'home:///', iconclass: "fa fa-home", type: "fs" },
{ text: "OS", path: 'os:///', iconclass: "fa fa-inbox", type: "fs" },
{ text: "Desktop", path: 'home:///.desktop', iconclass: "fa fa-desktop", type: "fs"},
] if not _OS.setting.VFS.mountpoints
_OS.setting.desktop.path = "home:///.desktop" unless _OS.setting.desktop.path _OS.setting.desktop.path = "home:///.desktop" unless _OS.setting.desktop.path
_OS.setting.appearance.theme = "antos" unless _OS.setting.appearance.theme _OS.setting.appearance.theme = "antos" unless _OS.setting.appearance.theme
# load theme # load theme
@ -279,5 +290,5 @@ self.OS.GUI =
# startup application here # startup application here
_courrier.observable.one "desktoploaded", () -> _courrier.observable.one "desktoploaded", () ->
_GUI.launch "DummyApp" #_GUI.launch "DummyApp"
#_GUI.launch "NotePad" _GUI.launch "NotePad"

View File

@ -0,0 +1,12 @@
<afx-app-window data-id = 'file-dialog-window' width='400' height='250'>
<afx-vbox>
<afx-list-view data-id = "location" dropdown = "false" data-width = "120"></afx-list-view>
<afx-hbox>
<afx-file-view data-id = "fileview" view='tree' status = false></afx-file-view>
<div data-height = '30' style=' text-align:right;padding-top:3px;'>
<afx-button data-id = "bt-ok" text = "Ok"></afx-button>
<afx-button data-id = "bt-cancel" text = "Cancel"></afx-button>
</div>
</afx-hbox>
</afx-vbox>
</afx-app-window>

View File

@ -12,6 +12,7 @@
self.data = opts.data || [] self.data = opts.data || []
self.path = opts.path || "home:///" self.path = opts.path || "home:///"
self.onfileselect self.onfileselect
self.onfileopen
this.status = opts.status == undefined?true:opts.status this.status = opts.status == undefined?true:opts.status
this.selectedFile = undefined this.selectedFile = undefined
this.showhidden = opts.showhidden this.showhidden = opts.showhidden
@ -182,8 +183,11 @@
$(self.refs.stbar).html("Selected: " + e.data.filename + " (" + e.data.size + " bytes)") $(self.refs.stbar).html("Selected: " + e.data.filename + " (" + e.data.size + " bytes)")
}) })
self.root.observable.on("filedbclick", function(e){ self.root.observable.on("filedbclick", function(e){
if(e.id != self.rid || e.data.type == 'file' || !self.chdir) return if(e.id != self.rid ) return
self.chdir(e.data.path) if(e.data.type == "file" && self.onfileopen)
self.onfileopen(e.data)
else if(self.chdir)
self.chdir(e.data.path)
}) })
calibre_size() calibre_size()
self.root.observable.on("resize", function(e){ self.root.observable.on("resize", function(e){

View File

@ -22,6 +22,7 @@
{ {
if(k == "selected") if(k == "selected")
{ {
console.log("selected", v)
if(self.selidx != -1) if(self.selidx != -1)
self.items[self.selidx].selected =false self.items[self.selidx].selected =false
if(self.items[v]) self.items[v].selected = true if(self.items[v]) self.items[v].selected = true
@ -121,6 +122,7 @@
_autoselect(it,i) _autoselect(it,i)
{ {
if(!it.selected || it.selected == false) return false if(!it.selected || it.selected == false) return false
//if(self.selidx == i) return false
var data = { var data = {
id:self.rid, id:self.rid,
data:it, data:it,
@ -133,9 +135,11 @@
$(self.refs.mlist).hide() $(self.refs.mlist).hide()
$(self.refs.current).html(it.text) $(self.refs.current).html(it.text)
} }
if(self.onlistselect) if(self.onlistselect)
self.onlistselect(data) self.onlistselect(data)
this.root.observable.trigger('listselect',data) this.root.observable.trigger('listselect',data)
//console.log("list select")
return true return true
} }
_select(event) _select(event)

View File

@ -4,6 +4,7 @@
var self = this var self = this
this.closable = opts.closable || false this.closable = opts.closable || false
self.root.observable = opts.observable || riot.observable() self.root.observable = opts.observable || riot.observable()
self.ontabselect = opts.ontabselect
get_observable(){ get_observable(){
return self.root.observable return self.root.observable
} }
@ -12,6 +13,9 @@
return self.refs.list.root.get(k) return self.refs.list.root.get(k)
} }
self.root.update = function(){
self.update()
}
self.on("mount", function(){ self.on("mount", function(){
self.refs.list.root.observable = self.root.observable self.refs.list.root.observable = self.root.observable
@ -19,7 +23,10 @@
console.log("list select") console.log("list select")
})*/ })*/
self.refs.list.root.set ("onlistselect",function (e) { self.refs.list.root.set ("onlistselect",function (e) {
//console.log("tab is seleced")
self.root.observable.trigger("tabselect", e) self.root.observable.trigger("tabselect", e)
if(self.ontabselect)
self.ontabselect(e)
}) })
}) })
@ -31,6 +38,8 @@
{ {
self.closable = v self.closable = v
} }
else if(k == "ontabselect")
self.ontabselect = v
else else
{ {
if(k == "items") if(k == "items")
@ -56,5 +65,6 @@
{ {
self.refs.list.root.remove(e,u) self.refs.list.root.remove(e,u)
} }
</script> </script>
</afx-tab-container> </afx-tab-container>

View File

@ -16,10 +16,12 @@ class Files extends this.OS.GUI.BaseApplication
m.set "items", [ me.mnFile(), me.mnEdit() ] m.set "items", [ me.mnFile(), me.mnEdit() ]
m.show(e) m.show(e)
#@on "fileselect", (d) -> console.log d #@on "fileselect", (d) -> console.log d
@on "filedbclick", (e) -> @view.set "onfileopen", (e) ->
return unless e.data return unless e
return if e.data.type is "dir" return if e.type is "dir"
me._gui.openWith e.data #console.log e
me._gui.openWith e
@favo.set "onlistselect", (e) -> me.chdir e.data.path @favo.set "onlistselect", (e) -> me.chdir e.data.path
($ @find "btback").click () -> ($ @find "btback").click () ->
@ -34,23 +36,18 @@ class Files extends this.OS.GUI.BaseApplication
@view.set "chdir", (p) -> me.chdir p @view.set "chdir", (p) -> me.chdir p
@view.set "fetch", (e, f) -> @view.set "fetch", (e, f) ->
return unless e.child return unless e.child
me._api.handler.scandir e.child.path, e.child.path.asFileHandler().read (d) ->
(d) -> f d.result return me.error "Resource not found #{e.child.path}" if d.error
, (e, s) -> me.error "Cannot fetch child dir #{e.child.path}" f d.result
@setting.favorite = [
{ text: "Applications", path: 'app:///', iconclass: "fa fa-adn" },
{ text: "Home", path: 'home:///', iconclass: "fa fa-home" },
{ text: "OS", path: 'os:///', iconclass: "fa fa-inbox" },
{ text: "Desktop", path: 'home:///.desktop', iconclass: "fa fa-desktop" },
] if not @setting.favorite
@setting.sidebar = true if @setting.sidebar is undefined @setting.sidebar = true if @setting.sidebar is undefined
@setting.nav = true if @setting.nav is undefined @setting.nav = true if @setting.nav is undefined
@setting.showhidden = false if @setting.showhidden is undefined @setting.showhidden = false if @setting.showhidden is undefined
el.selected = false for el, i in @setting.favorite mntpoints = @systemsetting.VFS.mountpoints
el.selected = false for el, i in mntpoints
@favo.set "items", @setting.favorite @favo.set "items", mntpoints
#@favo.set "selected", -1 #@favo.set "selected", -1
@applySetting() @applySetting()
@chdir null @chdir null

View File

@ -9,7 +9,7 @@ class NotePad extends this.OS.GUI.BaseApplication
@fileview = @find "fileview" @fileview = @find "fileview"
div = @find "datarea" div = @find "datarea"
ace.require "ace/ext/language_tools" ace.require "ace/ext/language_tools"
@currfile = if @args and @args.length > 0 then @args[0].asFileHandler() else undefined @currfile = if @args and @args.length > 0 then @args[0].asFileHandler() else "Untitled".asFileHandler()
@.editor = ace.edit div @.editor = ace.edit div
@.editor.setOptions { @.editor.setOptions {
enableBasicAutocompletion: true, enableBasicAutocompletion: true,
@ -20,16 +20,19 @@ class NotePad extends this.OS.GUI.BaseApplication
@.editor.completers.push { getCompletions: ( editor, session, pos, prefix, callback ) -> } @.editor.completers.push { getCompletions: ( editor, session, pos, prefix, callback ) -> }
@.editor.getSession().setUseWrapMode true @.editor.getSession().setUseWrapMode true
list = @find "modelist" @mlist = @find "modelist"
@modes = ace.require "ace/ext/modelist" @modes = ace.require "ace/ext/modelist"
ldata = [] ldata = []
ldata.push { f = (m, i) ->
text: m.name, ldata.push {
mode: m.mode, text: m.name,
selected: if m.mode is 'ace/mode/text' then true else false mode: m.mode,
} for m in @modes.modes selected: if m.mode is 'ace/mode/text' then true else false
list.set "items", ldata }
list.set "onlistselect", (e) -> m.idx = i
f(m, i) for m, i in @modes.modes
@mlist.set "items", ldata
@mlist.set "onlistselect", (e) ->
me.editor.session.setMode e.data.mode me.editor.session.setMode e.data.mode
themelist = @find "themelist" themelist = @find "themelist"
@ -52,6 +55,15 @@ class NotePad extends this.OS.GUI.BaseApplication
$(stat).html "Row #{c.row}, col #{c.column}, lines: #{l}" $(stat).html "Row #{c.row}, col #{c.column}, lines: #{l}"
stup(0) stup(0)
@.editor.getSession().selection.on "changeCursor", (e) -> stup(e) @.editor.getSession().selection.on "changeCursor", (e) -> stup(e)
@editormux = false
@editor.on "input", () ->
if me.editormux
me.editormux = false
return false
if not me.currfile.dirty
me.currfile.dirty = true
me.currfile.text += "*"
me.tabarea.update()
@on "resize", () -> me.editor.resize() @on "resize", () -> me.editor.resize()
@on "focus", () -> me.editor.focus() @on "focus", () -> me.editor.focus()
@ -59,39 +71,89 @@ class NotePad extends this.OS.GUI.BaseApplication
@fileview.set "chdir", (d) -> me.chdir d @fileview.set "chdir", (d) -> me.chdir d
@fileview.set "fetch", (e, f) -> @fileview.set "fetch", (e, f) ->
return unless e.child return unless e.child
me._api.handler.scandir e.child.path, e.child.path.asFileHandler().read (d) ->
(d) -> f d.result return me.error "Resource not found #{e.child.path}" if d.error
, (e, s) -> me.error "Cannot fetch child dir #{e.child.path}" f d.result
@fileview.set "onfileopen", (e) ->
me.open e.path.asFileHandler()
@location.set "onlistselect", (e) -> me.chdir e.data.path @location.set "onlistselect", (e) -> me.chdir e.data.path
@location.set "items", [ @location.set "items", ( i for i in @systemsetting.VFS.mountpoints when i.type isnt "app" )
{ text: "Home", path: 'home:///', iconclass: "fa fa-home", selected: true }, @location.set "selected", 0 unless @location.get "selected"
{ text: "OS", path: 'os:///', iconclass: "fa fa-inbox" },
{ text: "Desktop", path: 'home:///.desktop', iconclass: "fa fa-desktop" },
]
@tabarea = @find "tabarea" @tabarea = @find "tabarea"
@tabarea.set "ontabselect", (e) ->
me.selecteTab e.idx
@tabarea.set "onitemclose", (e) ->
it = e.item.item
return false unless it
return me.closeTab it unless it.dirty
me.openDialog "YesNoDialog", (d) ->
return me.closeTab it if d
me.editor.focus()
, "Close tab", { text: "Close without saving ?" }
return false
#@tabarea.set "closable", true #@tabarea.set "closable", true
@open @currfile @open @currfile
open: (file) -> open: (file) ->
#find table #find table
@newtab "undefined".asFileHandler() unless file i = @findTabByFile file
return @newtab "undefined".asFileHandler() unless file return @tabarea.set "selected", i if i isnt -1
return @newtab file if file.path.toString() is "Untitled"
me = @ me = @
file.read (d) -> file.read (_d) ->
return unless d d = if typeof _d is "string" then _d else JSON.stringify _d
me.scheme.set "apptitle", file.basename me.scheme.set "apptitle", file.basename
file.cache = d file.cache = d or ""
me.newtab file me.newtab file
newtab: (file) -> findTabByFile: (file) ->
file.text = if file.basename then file.basename else "undefined" lst = @tabarea.get "items"
file.cache = "" unless file.cache its = ( i for d, i in lst when d.hash() is file.hash() )
return -1 if its.length is 0
return its[0]
closeTab: (it) ->
@tabarea.remove it, false
cnt = @tabarea.get "count" cnt = @tabarea.get "count"
if cnt is 0
@open "Untitled".asFileHandler()
return false
@tabarea.set "selected", cnt - 1
return false
newtab: (file) ->
file.text = if file.basename then file.basename else file.path
file.cache = "" unless file.cache
file.um = new ace.UndoManager()
@currfile.selected = false
file.selected = true
#console.log cnt
@tabarea.push file, true @tabarea.push file, true
@tabarea.set "selected", cnt #@currfile = @file
#TODO: fix problem : @tabarea.set "selected", cnt
selecteTab: (i) ->
#return if i is @tabarea.get "selidx"
file = (@tabarea.get "items")[i]
return unless file
#return if file is @currfile
if @currfile isnt file
@currfile.cache = @editor.getValue()
@currfile.cursor = @editor.selection.getCursor()
@currfile = file
m = "ace/mode/text"
m = (@modes.getModeForPath file.path) if file.path.toString() isnt "Untitled"
@mlist.set "selected", m.idx
@editormux = true
@editor.setValue file.cache, -1 @editor.setValue file.cache, -1
@editor.session.setMode m.mode
@editor.session.setUndoManager file.um
if file.cursor
@editor.renderer.scrollCursorIntoView { row: file.cursor.row, column: file.cursor.column }, 0.5
@editor.selection.moveTo file.cursor.row, file.cursor.column
@editor.focus()
chdir: (p) -> chdir: (p) ->
me = @ me = @
@ -104,14 +166,22 @@ class NotePad extends this.OS.GUI.BaseApplication
, (e, s) -> , (e, s) ->
me.error "Cannot chdir #{p}" me.error "Cannot chdir #{p}"
menu: ()-> menu: () ->
me = @
menu = [{ menu = [{
text: "File", text: "File",
child: [ child: [
{ text: "Open", dataid: "#{@name}-Open" }, { text: "Open", dataid: "#{@name}-Open" },
{ text: "Close", dataid: "#{@name}-Close" } { text: "Close", dataid: "#{@name}-Close" }
] ],
onmenuselect: (e) -> me.actionFile e
}] }]
menu menu
actionFile: (e) ->
switch e.item.data.dataid
when "#{@name}-Open"
@openDialog "FileDiaLog", null, "Select file", { seldir: true }
NotePad.singleton = false NotePad.singleton = false
this.OS.register "NotePad", NotePad this.OS.register "NotePad", NotePad