Skip to content

Instantly share code, notes, and snippets.

@ALANVF
Last active June 17, 2022 03:15
Show Gist options
  • Save ALANVF/79250fa7741021999d1401aab57d5b6c to your computer and use it in GitHub Desktop.
Save ALANVF/79250fa7741021999d1401aab57d5b6c to your computer and use it in GitHub Desktop.
Text editor demo
Red [
Title: "Text editor"
Author: "ALANVF"
Date: 16-Jun-2022
File: %text-editor.red
Needs: 'View
Purpose: {
Basic text editor
}
]
num-untitled: 0
title-text: none
edited?: no
current-file: none
current-untitled: "Untitled"
confirm: func [
msg [string!]
return: [logic!]
/local
result
][
view compose/only [
title "Confirm choice"
on-close [
result: none
]
below center
text center (msg)
panel [
button 50 "Yes" with [set-focus self] [
result: yes
unview
]
button 50 "No" [
result: no
unview
]
]
]
return result
]
save-current: does [
if none? current-file [
current-file: request-file/save/file rejoin [current-untitled ".txt"]
]
unless none? current-file [
write current-file editor/text
]
]
ask-to-save: [
switch confirm "Do you want to save first?" [
#[true] [ save-current ]
#[none] [ exit ]
]
]
do-new: does [
if edited? [
do ask-to-save
edited?: no
]
clear editor/text
num-untitled: num-untitled + 1
append clear title-text current-untitled: rejoin ["Untitled" num-untitled]
]
do-open: does [
if edited? [
do ask-to-save
unless none? current-file [
edited?: no
]
]
unless none? current-file: request-file [
insert clear editor/text read current-file
append clear title-text to-local-file current-file
]
]
do-save: does [
if edited? [
save-current
unless none? current-file [
remove back tail title-text
edited?: no
]
]
]
do-save-as: does [
current-file: request-file/save/file
either none? current-file [rejoin [current-untitled ".txt"]][current-file]
unless none? current-file [
write current-file editor/text
if edited? [
append clear title-text to-local-file current-file
edited?: no
]
]
]
do-exit: does [
if edited? [
do ask-to-save
remove back tail title-text
edited?: no
]
unview/all
]
find-text: func [
text [string!]
pos [integer!]
case? [logic!]
reverse? [logic!]
return: [pair! none!]
/local
found
][
found: either case? [
either reverse? [
find/reverse/case at editor/text pos text
][
find/case at editor/text pos text
]
][
either reverse? [
find/reverse at editor/text pos text
][
find at editor/text pos text
]
]
either none? found [none][
as-pair
index? found
(index? found) + (length? text) - 1
]
]
no-more-matches: func [
title [string!]
top-or-bottom [string! none!]
/local text
][
text: either none? top-or-bottom [
"No more matches available"
][
rejoin ["No more matches, starting from the " top-or-bottom]
]
view compose [
title (title)
below center
text (text)
button focus "Ok" [ unview ]
]
]
do-find: has [to-find next-btn prev-btn selection current-pos found][
current-pos: 1
view [
title "Find text"
below left
panel [
across middle
text 50 center "Find:"
to-find: field 150 "" with [
set-focus self
selection: editor/selected
unless none? selection [
text: copy/part (at editor/text selection/x) (selection/y - selection/x)
selected: as-pair 1 length? text
current-pos: selection/x
]
]
]
panel [
across middle
next-btn: button "Find next" [
found: find-text to-find/text current-pos case-check/data no
if none? found [
no-more-matches "Find text" "top"
current-pos: 1
found: find-text to-find/text current-pos case-check/data no
]
unless none? found [
editor/selected: found
current-pos: found/y
]
]
prev-btn: button "Find previous" [
found: find-text to-find/text current-pos case-check/data yes
if none? found [
no-more-matches "Find text" "bottom"
current-pos: length? editor/text
found: find-text to-find/text current-pos case-check/data yes
]
unless none? found [
editor/selected: found
current-pos: found/x
;-- if we are on the first match, don't try matching it again
if none? find-text to-find/text current-pos case-check/data yes [
current-pos: found/y
]
]
]
case-check: check "Match case" false
]
]
]
do-replace: has [
to-find replace-with next-btn prev-btn replace-btn replace-all-btn
selection current-pos found find-replace num-replaced
][
current-pos: 1
find-replace: [
found: find-text to-find/text current-pos case-check/data no
if none? found [
current-pos: 0
found: find-text to-find/text current-pos case-check/data no
either none? found [
no-more-matches "Replace text" none
][
no-more-matches "Replace text" "top"
]
]
unless none? found [
editor/selected: found
current-pos: found/x
]
]
view [
title "Replace text"
below left
panel [
across middle
text 50 center "Find:"
to-find: field 150 "" with [
selection: editor/selected
either none? selection [
set-focus self
][
text: copy/part (at editor/text selection/x) (selection/y - selection/x)
selected: as-pair 1 length? text
current-pos: selection/x
]
]
return
text 50 center "Replace:"
replace-with: field 150 "" with [
unless none? selection [
set-focus self
]
]
]
panel [
across middle
next-btn: button "Find next" [
found: find-text to-find/text current-pos case-check/data no
if none? found [
no-more-matches "Replace text" "top"
current-pos: 1
found: find-text to-find/text current-pos case-check/data no
]
unless none? found [
editor/selected: found
current-pos: found/y
]
]
prev-btn: button "Find previous" [
found: find-text to-find/text current-pos case-check/data yes
if none? found [
no-more-matches "Replace text" "bottom"
current-pos: length? editor/text
found: find-text to-find/text current-pos case-check/data yes
]
unless none? found [
editor/selected: found
current-pos: found/x
;-- if we are on the first match, don't try matching it again
if none? find-text to-find/text current-pos case-check/data yes [
current-pos: found/y
]
]
]
]
panel [
across middle
replace-btn: button "Replace" [
if none? editor/selected [ do find-replace ]
unless none? selection: editor/selected [
change/part
at editor/text selection/x
replace-with/text
at editor/text selection/y + 1
current-pos: selection/x + length? replace-with/text
do find-replace
]
]
replace-all-btn: button "Replace all" [
num-replaced: 0
current-pos: 1
while [not none? found: find-text to-find/text current-pos case-check/data no][
change/part
at editor/text found/x
replace-with/text
at editor/text found/y + 1
num-replaced: num-replaced + 1
current-pos: found/x + length? replace-with/text
]
alert rejoin [
num-replaced
" match"
either num-replaced = 1 [""]["es"]
" replaced"
]
]
case-check: check "Match case" false
]
]
]
view/flags [
size 500x300
title "Text editor - Untitled"
on-resizing [
editor/size: face/size - 20x20
]
on-menu [
switch event/picked [
new [ do-new ]
open [ do-open ]
save [ do-save ]
save-as [ do-save-as ]
exit [ do-exit ]
find [ do-find ]
replace [ do-replace ]
font [
font: request-font
unless none? font [
editor/font: font
]
]
]
]
on-close [
if edited? [
do ask-to-save
]
]
do [
title-text: find/tail text " - "
menu: [
"File" [
"New Ctrl+N" new
"Open Ctrl+O" open
"Save Ctrl+S" save
"Save as Ctrl+Shift+S" save-as
---
"Exit Ctrl+E" exit
]
"Edit" [
"Find Ctrl+F" find
"Replace Ctrl+H" replace
]
"View" [
"Choose font" font
]
]
]
editor: area "" on-key [
case [
event/ctrl? [
switch/default event/key [
#"^N" [ do-new ]
#"^O" [ do-open ]
#"^S" [ either event/shift? [do-save-as][do-save] ]
#"^E" [ do-exit ]
#"^F" [ do-find ]
#"^R" [ do-replace ]
; ignore these since they don't edit anything
#"^A" #"^C" []
][
unless edited? [
edited?: yes
append title-text "*"
]
]
]
event/key = #"^[" [
editor/selected: none
]
not edited? [
edited?: yes
append title-text "*"
]
]
]
] [resize]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment