Skip to content

Commit

Permalink
Merge pull request #149 from milikhin/harbour-file-list
Browse files Browse the repository at this point in the history
[sailfish] add ability to create new files
  • Loading branch information
milikhin authored Aug 25, 2022
2 parents 3cfb304 + a2fc7d7 commit 748c045
Show file tree
Hide file tree
Showing 22 changed files with 548 additions and 96 deletions.
5 changes: 4 additions & 1 deletion editor/__tests__/app/app.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ describe('SeabassApp', () => {
// Check for 'appLoaded' action
const evt = await waitForApiMessage
expect(evt.detail.action).toEqual('appLoaded')
expect(evt.detail.data).toEqual({ isToolbarOpened: true })
expect(evt.detail.data).toEqual({
isToolbarOpened: true,
directory: null
})
})
})
9 changes: 5 additions & 4 deletions editor/__tests__/app/model.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ describe('SeabassAppModel', () => {
expect(model._viewport).toEqual({
verticalHtmlOffset: 0
})
expect(model.sailfishPreferences).toEqual({ isToolbarOpened: true })
expect(model.sailfishPreferences).toEqual({
isToolbarOpened: true,
directory: null
})
})
})

Expand Down Expand Up @@ -192,9 +195,7 @@ describe('SeabassAppModel', () => {
const options = { isToolbarOpened: true }
model.setSailfishPreferences(options)

expect(model._sailfish).toEqual({
isToolbarOpened: options.isToolbarOpened
})
expect(model._sailfish.isToolbarOpened).toEqual(options.isToolbarOpened)
})
})
})
20 changes: 15 additions & 5 deletions editor/src/app/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ export interface SeabassCommonPreferences {
export interface SeabassSailfishPreferences {
/** Bottom toolbar state */
isToolbarOpened: boolean
/** Current file tree directory */
directory: string|null
}

export interface ViewportOptions {
Expand Down Expand Up @@ -65,20 +67,23 @@ export default class SeabassAppModel extends EventTarget {
/** SailfishOS-specific preferences */
_sailfish: {
isToolbarOpened: boolean
directory: string|null
}

_viewport: {
verticalHtmlOffset: number
}

SFOS_TOOLBAR_LOCAL_STORAGE_KEY = 'sailfish__isToolbarOpened'
SFOS_DIRECTORY_LOCAL_STORAGE_KEY = 'sailfish__directory'

constructor () {
super()
this._editors = new Map()
this._preferences = { isDarkTheme: false }
this._sailfish = {
isToolbarOpened: localStorage.getItem(this.SFOS_TOOLBAR_LOCAL_STORAGE_KEY) === 'true'
isToolbarOpened: localStorage.getItem(this.SFOS_TOOLBAR_LOCAL_STORAGE_KEY) === 'true',
directory: localStorage.getItem(this.SFOS_DIRECTORY_LOCAL_STORAGE_KEY)
}
this._viewport = {
verticalHtmlOffset: 0
Expand Down Expand Up @@ -202,11 +207,16 @@ export default class SeabassAppModel extends EventTarget {
* Sets sailfish-specific app preferences
* @param options app preferences
*/
setSailfishPreferences (options: SeabassSailfishPreferences): void {
this._sailfish = {
isToolbarOpened: options.isToolbarOpened
setSailfishPreferences (options: Partial<SeabassSailfishPreferences>): void {
if (options.isToolbarOpened !== undefined) {
this._sailfish.isToolbarOpened = options.isToolbarOpened
localStorage.setItem(this.SFOS_TOOLBAR_LOCAL_STORAGE_KEY, options.isToolbarOpened.toString())
}
if (options.directory !== undefined && options.directory !== null) {
this._sailfish.directory = options.directory
localStorage.setItem(this.SFOS_DIRECTORY_LOCAL_STORAGE_KEY, options.directory)
}
localStorage.setItem(this.SFOS_TOOLBAR_LOCAL_STORAGE_KEY, options.isToolbarOpened.toString())

this.dispatchEvent(new CustomEvent('sfosPreferencesChange', { detail: this._sailfish }))
}
}
3 changes: 2 additions & 1 deletion generic/qml/EditorState.qml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import '../generic/utils.js' as QmlJs

QtObject {
// State
property string filePath: ''
property string filePath
property string directory
property bool hasChanges: false
property bool hasUndo: false
property bool hasRedo: false
Expand Down
141 changes: 141 additions & 0 deletions generic/qml/FilesModel.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import QtQuick 2.0
import io.thp.pyotherside 1.4

import "./utils.js" as QmlJs

Item {
property string rootDirectory
property string directory
property string prevDirectory
property bool showDotDot: false
property var expanded: []
property var prevExpanded: []

signal errorOccured(string error)

readonly property var model: ListModel {
Component.onCompleted: {
directoryChanged.connect(reload)
showDotDotChanged.connect(reload)
py.readyChanged.connect(function() {
load(true)
})
}
}

readonly property var py: Python {
property bool ready: false
onReceived: function(evtArgs) {
if (evtArgs[0] !== 'fs_event') {
return
}

load()
}

Component.onCompleted: {
addImportPath(Qt.resolvedUrl('../py-backend'))
importModule('fs_utils', function() {
ready = true
})
}

function listDir(path, expanded, callback) {
const directories = [path].concat(expanded)
py.call('fs_utils.list_files', [directories], function(res) {
callback(res.error, res.result)
})
py.call('fs_utils.watch_changes', [directories])
}
}

function load(ignoreError) {
if (!py.ready) {
return
}

py.listDir(directory, expanded, function(error, entries) {
if (error) {
directory = prevDirectory
// copy prevExpanded values
expanded = [].concat(prevExpanded)
if (ignoreError) {
return;
}
return errorOccured(error)
}

const hasDotDot = showDotDot && directory !== rootDirectory
if (hasDotDot) {
model.set(0, {
name: '..',
path: QmlJs.getDirPath(QmlJs.getNormalPath(directory)),
isDir: true
})
}

const startIndex = hasDotDot ? 1 : 0
const totalEntriesNumber = entries.length + startIndex
entries.forEach(function (fileEntry, i) {
var index = startIndex + i
fileEntry.isExpanded = expanded.indexOf(fileEntry.path) !== -1
if (index < model.count) {
// update non-last model entries
model.set(index, fileEntry)
} else {
// append new model entries
model.append(fileEntry)
}
})

if (totalEntriesNumber < model.count) {
model.remove(totalEntriesNumber, model.count - totalEntriesNumber)
}

prevDirectory = directory
// copy expanded values
prevExpanded = [].concat(expanded)
})
}

function reload() {
expanded = []
load()
}

function rm(path, callback) {
if (!py.ready) {
return
}

py.call('fs_utils.rm', [path], function(res) {
callback(res.error)
})
}

function rename(originalPath, newPath, callback) {
if (!py.ready) {
return
}

py.call('fs_utils.rename', [originalPath, newPath], function(res) {
callback(res.error)
})
}

function toggleExpanded(path) {
if (expanded.indexOf(path) === -1) {
expanded.push(path)
} else {
var newExpanded = []
expanded.forEach(function(expandedPath) {
if (expandedPath.indexOf(path) !== 0) {
newExpanded.push(expandedPath)
}
})
expanded = newExpanded
}

load()
}
}
11 changes: 5 additions & 6 deletions generic/qml/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ function getDefaultFilePath() {
* @returns {string} - directory path
*/
function getDirPath(filePath) {
return getNormalPath(filePath).split('/').slice(0, -1).join('/')
return getNormalPath(filePath).split('/').slice(0, -1).join('/') || '/'
}

/**
Expand All @@ -47,11 +47,10 @@ function getFileName(filePath) {
* @returns {string} - normalized path
*/
function getNormalPath(path) {
var normalizedPath = path[path.length - 1] === '/'
var normalPath = path.replace(/^file:\/\//, '')
return normalPath.length > 1 && normalPath[normalPath.length - 1] === '/'
? path.slice(0, -1)
: path

return normalizedPath.replace(/^file:\/\//, '')
}

/**
Expand All @@ -64,10 +63,10 @@ function getPrintableDirPath(dirPath, homeDir) {
var normalizedDir = getNormalPath(dirPath)
var normalizedHome = getNormalPath(homeDir)
if (normalizedDir.indexOf(normalizedHome) === 0) {
return normalizedDir.replace(normalizedHome, '~') + '/'
return normalizedDir.replace(normalizedHome, '~')
}

return normalizedDir + '/'
return normalizedDir
}

function getPrintableFilePath(filePath, homeDir) {
Expand Down
3 changes: 3 additions & 0 deletions harbour-seabass/harbour-seabass.pro
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,16 @@ DISTFILES += qml/harbour-seabass.qml \
qml/components/Toolbar.qml \
qml/generic/EditorApi.qml \
qml/generic/EditorState.qml \
qml/generic/FilesModel.qml \
qml/generic/TabsModel.qml \
qml/generic/utils.js \
qml/html/dist \
qml/cover/CoverPage.qml \
qml/pages/About.qml \
qml/pages/Editor.qml \
qml/pages/ErrorDialog.qml \
qml/pages/Files.qml \
qml/pages/NewFile.qml \
qml/pages/SaveDialog.qml \
qml/py-backend \
rpm/harbour-seabass.changes.in \
Expand Down
40 changes: 5 additions & 35 deletions harbour-seabass/html/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,41 +23,11 @@

<body>
<div id="welcome">
<p>v0.9.2:</p>
<ul>
<li>
Add support for Sailfish OS v4.4.0.68:
fix editor resizing and scroll-into-view when opening on-screen keyboard
</li>
<li>
Update codemirror to v6.0.1
</li>
<li>
Supported Sailfish OS versions: v4.4.0.68
</li>
</ul>
<p>v0.9.1:</p>
<ul>
<li>
Fix reading indentation preferences
from <a href="https://editorconfig.org/" target="_blank">.editorconfig</a> files
</li>
<li>
Fix switching between dark/light ambiances
</li>
<li>
Supported Sailfish OS versions: v4.2 &ndash; 4.4.0.64
</li>
</ul>
<p>v0.9.0:</p>
<ul>
<li>
Add asterisk to the header to indicate unsaved changes
</li>
<li>
Fix performance issues when editing large files
</li>
</ul>
<p>
v0.10 introduces new file picker component.
It has compact design, remembers last opened directory and provides possiblity
to create new files.
</p>
<p>
Please let me know if you have any issues:
<a target="_blank" href="https://github.com/milikhin/seabass2" target="_blank">Seabass on Github</a>.
Expand Down
28 changes: 28 additions & 0 deletions harbour-seabass/qml/components/FloatingButton.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import QtQuick 2.2
import Sailfish.Silica 1.0

import '../generic/utils.js' as QmlJs

Rectangle {
id: root
property bool isDarkTheme
property bool highlighed: false
property alias icon: button.icon
signal clicked()

width: childrenRect.width
height: childrenRect.height
color: isDarkTheme
? QmlJs.colors.DARK_TOOLBAR_BACKGROUND
: QmlJs.colors.LIGHT_TOOLBAR_BACKGROUND
radius: Theme.dp(2)

Button {
id: button
backgroundColor: Theme.rgba(Theme.highlightBackgroundColor,
highlighed ? Theme.highlightBackgroundOpacity : 0)
border.color: Theme.highlightBackgroundColor
icon.color: highlighed ? Theme.highlightColor : Theme.primaryColor
onClicked: root.clicked()
}
}
2 changes: 1 addition & 1 deletion harbour-seabass/qml/pages/About.qml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Page {
anchors.fill: parent
header: PageHeader {
id: header
title: qsTr('Seabass v%1').arg('0.9.2')
title: qsTr('Seabass v%1').arg('0.10.0')
}
model: ListModel {
ListElement {
Expand Down
Loading

0 comments on commit 748c045

Please sign in to comment.