Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ organization := "io.atal"
scalaVersion := "2.11.4"

libraryDependencies += "org.scalatest" % "scalatest_2.11" % "2.2.1" % "test"

libraryDependencies += "org.scala-lang" % "scala-swing" % "2.11+"
2 changes: 1 addition & 1 deletion src/main/scala/io/atal/butterfly/Buffer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class Buffer(var content: String) {

/** Return the content as an array
*/
def lines: Array[String] = content.split("\n")
def lines: Array[String] = content.split("\n", -1)

/** Return a selected substring in the buffer
*
Expand Down
14 changes: 13 additions & 1 deletion src/main/scala/io/atal/butterfly/Editor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class Editor(var buffer: Buffer = new Buffer("")) {
def write(text: String): Unit = {
for (cursor <- cursors) {
buffer.insert(text, cursor.position)
cursor.moveRight(text.length)
moveCursorRight(cursor, text.length)
}
}

Expand Down Expand Up @@ -109,6 +109,18 @@ class Editor(var buffer: Buffer = new Buffer("")) {
/** Clear all selections
*/
def clearSelection: Unit = selections = List()


/** Return the position of the cursor in the whole string
*
* @param cursor The cursor that we want
* @retutrn The position in the buffer
*/
def getIndexPosition(cursor: Cursor): Int = cursor.position match {
case (0, y) => y
case (x, 0) => buffer.lines(x-1).length + 1 + getIndexPosition(new Cursor(x-1, 0))
case (x, y) => y + getIndexPosition(new Cursor(x, 0))
}

/** Move up all cursors
*
Expand Down
79 changes: 79 additions & 0 deletions src/main/scala/io/atal/butterfly/EditorManager.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package io.atal.butterfly

/** A manager that allow to use multiple editor
* Call function of Editor, and manage the focus upon editors
* Manage the clipboard
*/
class EditorManager {
var _editors: List[Editor] = List()
var _clipboard: Clipboard = new Clipboard()
var _currentEditor: Option[Editor] = None

def editors: List[Editor] = _editors

def clipboard: Clipboard = _clipboard

def currentEditor: Option[Editor] = _currentEditor

def currentEditor_=(editor: Editor) = _currentEditor = Some(editor)

/** Open a new Editor
*
* @TODO Add a param files to open a file text
*/
def openEditor: Unit = {
_editors = new Editor() :: _editors
_currentEditor = Some(_editors.head)
}

/** Close an Editor
*
* @param editor The editor to close
* @TODO Save the editor
*/
def closeEditor(editor: Editor): Unit = {
_editors = _editors.diff(List(editor))
if(_currentEditor == Some(editor)) {
_currentEditor = Some(_editors.head)
}
}

/** Write text into the current Editor
*
* Call the write method of Editor
* @param text The text to insert
*/
def write(text: String): Unit = _currentEditor match {
case Some(editor) => editor.write(text)
case None => Unit
}

/** Erase text from the current Editor
*
* Call the erase method of Editor
*/
def erase: Unit = _currentEditor match {
case Some(editor) => editor.erase
case None => Unit
}

/** Erase all the selections from the current Editor
*
* Call the eraseSelection method from Editor
*/
def eraseSelection: Unit = _currentEditor match {
case Some(editor) => editor.eraseSelection
case None => Unit
}

/** Get contents from the selections of the current Editor
*
* Call the getSelectionContent method from Editor
* @return String The text from the selection (or an empty string if there is no current editor)
*/
def getSelectionContent: String = _currentEditor match {
case Some(editor) => editor.getSelectionContent
case None => ""
}

}
89 changes: 89 additions & 0 deletions src/main/scala/io/atal/butterfly/Interface.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package io.atal.butterfly

import scala.swing._
import scala.swing.event._

/**
* A simple swing demo.
*/
object HelloWorld extends SimpleSwingApplication {
def top = new MainFrame {
title = "Hello, World!"

var editorManager = new EditorManager
editorManager.openEditor

var current: Editor = editorManager.currentEditor.get
var isSpec = false

object editor extends EditorPane {
text = current.buffer.content
preferredSize = new Dimension(1000,500)

editable = false
caret.visible = true

focusable = true
requestFocus

listenTo(keys)
reactions += {
/**
* KeyPressed and KeyTyped are too different event, both sent everytime.
* KeyPressed allow to do any action for a key (those which are non buguy (all accents)), for exemple erase for backspace
* KeyTyped allow to capture the character generate with a key (taking account shift, alt, ...) => no buguy accent, but buguy char for BackSpace
* It doesn't recognize a key, so no filter :(
*/

case KeyPressed(_, Key.BackSpace, _, _) => {
isSpec = true
current.erase
updateLabel
}

case KeyPressed(_, Key.Left, _, _) => {
isSpec = true
current.moveCursorsLeft()
}

case KeyPressed(_, Key.Right, _, _) => {
isSpec = true
current.moveCursorsRight()
}

case KeyPressed(_, Key.Up, _, _) => {
isSpec = true
current.moveCursorsUp()
}

case KeyPressed(_, Key.Down, _, _) => {
isSpec = true
current.moveCursorsDown()
}

case KeyPressed(_, x, _, _) => isSpec = false //Allow for non-specified KeyPressed (like Key.BackSpace) to be match with KeyTyped. It's ugly. Better way ?

case KeyTyped(_, y, _, _) => {
if(!isSpec) {
current.write(y.toString)
updateLabel
}
}
}
}

contents = new FlowPanel {
contents.append(editor)
border = Swing.EmptyBorder(10, 10, 10, 10)


}

def updateLabel: Unit = {
editor.text = current.buffer.content
editor.caret.position = current.getIndexPosition(current.cursors.head)
}
}

}

55 changes: 55 additions & 0 deletions src/test/scala/io/atal/butterfly/EditorManagerTest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package io.atal.butterfly

import org.scalatest._
import Matchers._

/** EditorManager unit tests
*/
class EditorManagerTest extends FlatSpec {

"The EditorManager accessor and mutator" should "be as expected" in {
val editorManager = new EditorManager

assert(editorManager.editors == List())
assert(editorManager.currentEditor == None)
}

"The EditorManager openEditor method" should "add a new editor and move the current editor to it" in {
val editorManager = new EditorManager

editorManager.editors should have length 0

editorManager.openEditor

editorManager.editors should have length 1
assert(editorManager.currentEditor == Some(editorManager.editors.head))
}

"The EditorManager closeEditor method" should "remove the editor and potentially move the current editor" in {
val editorManager = new EditorManager

editorManager.openEditor
editorManager.openEditor

editorManager.editors should have length 2
assert(editorManager.currentEditor == Some(editorManager.editors.head))

var old_editors = editorManager.editors

editorManager.closeEditor(editorManager.editors.head)
assert(editorManager.editors == old_editors.tail)
assert(editorManager.currentEditor == Some(editorManager.editors.head))

editorManager.openEditor

editorManager.editors should have length 2
assert(editorManager.currentEditor == Some(editorManager.editors.head))

old_editors = editorManager.editors

editorManager.closeEditor(editorManager.editors.tail.head)
assert(editorManager.editors == old_editors.head :: old_editors.tail.tail)
assert(editorManager.currentEditor == Some(editorManager.editors.head))
}

}
10 changes: 10 additions & 0 deletions src/test/scala/io/atal/butterfly/EditorTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,16 @@ class EditorTest extends FlatSpec {

editor.selections should have length 0
}

"The Editor getIndexPosition method" should "actually return the index position of the cursor" in {
val editor = new Editor
val cursor = new Cursor((1,3))

editor.buffer.content = "Hello\nthe world"

assert(editor.getIndexPosition(cursor) == 9)

}

"The Editor write method" should "write the given text at all cursors position" in {
val editor = new Editor
Expand Down