Skip to content

Commit

Permalink
Learn words (#7)
Browse files Browse the repository at this point in the history
* Can select candidates by number press, word break commit support
* Implement word learning
* Use CTRL or CMD +DEL to unlearn word
* Added recently learned words in settings
* Improved vstDir setting, RLW page has languages toggler
  • Loading branch information
subins2000 authored Nov 27, 2021
1 parent 944b713 commit 7f00045
Show file tree
Hide file tree
Showing 11 changed files with 455 additions and 64 deletions.
4 changes: 2 additions & 2 deletions Application/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ class AppDelegate: NSObject, NSApplicationDelegate {
}

@IBAction func showReleaseNotes(_ sender: NSMenuItem) {
NSWorkspace.shared.open(URL(string: "https://github.com/ratreya/varnam-ime/releases")!)
NSWorkspace.shared.open(URL(string: "https://github.com/varnamproject/varnam-macOS/releases")!)
}

@IBAction func reportIssue(_ sender: NSMenuItem) {
NSWorkspace.shared.open(URL(string: "https://github.com/ratreya/varnam-ime/issues/new")!)
NSWorkspace.shared.open(URL(string: "https://github.com/varnamproject/varnam-macOS/issues/new")!)
}
}
16 changes: 11 additions & 5 deletions Application/MainView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,23 @@ struct MainView: View {
TabView(selection: $currentTab) {
SettingsView().tabItem {
Text("Settings")
}.tag(2)
}.tag(0)
.onAppear() {
self.currentTab = 2
self.currentTab = 0
}
LanguageView().tabItem {
Text("Languages")
}.tag(3)
}.tag(1)
.onAppear() {
self.currentTab = 1
}
RecentlyLearnedWordsView().tabItem {
Text("Recently Learned Words")
}.tag(2)
.onAppear() {
self.currentTab = 3
self.currentTab = 2
}
}.padding(20)
}.padding(20)
}
}

Expand Down
127 changes: 127 additions & 0 deletions Application/RLWTable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
//
// RLWTable.swift
// VarnamApp
//
// Copyright © 2021 Subin Siby
//

import Foundation

import SwiftUI

extension Double {
func getDateTimeStringFromUTC() -> String {
let date = Date(timeIntervalSince1970: self)
let dateFormatter = DateFormatter()
dateFormatter.timeStyle = DateFormatter.Style.medium //Set time style
dateFormatter.dateStyle = DateFormatter.Style.medium //Set date style
dateFormatter.timeZone = .current
return dateFormatter.string(from: date)
}
}

struct RLWTable: NSViewControllerRepresentable {
var words: [Suggestion]
var unlearn: (String)->()
typealias NSViewControllerType = RLWTableController

func makeNSViewController(context: Context) -> RLWTableController {
return RLWTableController(self)
}

func updateNSViewController(_ nsViewController: RLWTableController, context: Context) {
nsViewController.words = words
nsViewController.table.reloadData()
}
}

class RLWTableController: NSViewController, NSTableViewDelegate, NSTableViewDataSource, NSTextFieldDelegate {
private var wrapper: RLWTable

var table = NSTableView()
var words: [Suggestion];

init(_ wrapper: RLWTable) {
self.wrapper = wrapper
self.words = wrapper.words

super.init(nibName: nil, bundle: nil)
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

override func loadView() {
self.view = NSView()
view.autoresizesSubviews = true

let columns = [
(title: "Learned On", width: 200.0, tooltip: "Factory name for language"),
(title: "Word", width: 250.0, tooltip: "Custom name for language"),
(title: "", width: 10.0, tooltip: "Shortcut to select language"),
]
for column in columns {
let tableColumn = NSTableColumn()
tableColumn.headerCell.title = column.title
tableColumn.headerCell.alignment = .center
tableColumn.identifier = NSUserInterfaceItemIdentifier(rawValue: column.title)
tableColumn.width = CGFloat(column.width)
tableColumn.headerToolTip = column.tooltip
table.addTableColumn(tableColumn)
}
table.allowsColumnResizing = true
table.allowsColumnSelection = false
table.allowsMultipleSelection = false
table.allowsColumnReordering = false
table.allowsEmptySelection = true
table.allowsTypeSelect = false
table.usesAlternatingRowBackgroundColors = true
table.intercellSpacing = NSSize(width: 15, height: 7)

let scroll = NSScrollView()
scroll.documentView = table
scroll.hasVerticalScroller = true
scroll.autoresizingMask = [.height, .width]
scroll.borderType = .bezelBorder
view.addSubview(scroll)
}

override func viewDidLoad() {
super.viewDidLoad()

table.delegate = self
table.dataSource = self
}

// NSTableViewDataSource
func numberOfRows(in table: NSTableView) -> Int {
return words.count
}

// NSTableViewDelegate
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
switch tableColumn!.title {
case "Learned On":
return NSTextField(string: Double(words[row].LearnedOn).getDateTimeStringFromUTC())
case "Word":
return NSTextField(string: words[row].Word)
case "":
let btn = NSButton(title: "Unlearn", target: self, action: #selector(self.onChange(receiver:)))
btn.identifier = tableColumn!.identifier
return btn
default:
Logger.log.fatal("Unknown column title \(tableColumn!.title)")
fatalError()
}
}

@objc func onChange(receiver: Any) {
let row = table.row(for: receiver as! NSView)
if row == -1 {
// The view has changed under us
return
}
wrapper.unlearn(words[row].Word)
}
}
111 changes: 111 additions & 0 deletions Application/RecentlyLearnedWordsView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* VarnamApp is companion application for VarnamIME.
* Copyright (C) 2021 Subin Siby - VarnamIME
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/

import Foundation

import SwiftUI

class RLWModel: ObservableObject {
@Published public var words: [Suggestion] = [Suggestion]();

@Published var languages: [LanguageConfig];
@Published var schemeID: String = "ml";
@Published var schemeLangName: String = "Malayalam";

let config = VarnamConfig()
private (set) var varnam: Varnam! = nil;

private func closeVarnam() {
varnam.close()
varnam = nil
}

private func initVarnam() -> Bool {
if (varnam != nil) {
closeVarnam()
}
do {
varnam = try Varnam(schemeID)
} catch let error {
Logger.log.error(error.localizedDescription)
return false
}
return true
}

init() {
Varnam.setVSTLookupDir(config.vstDir)

// One language = One dictionary
// Same language can have multiple schemes
schemeID = config.schemeID
languages = config.languageConfig

if initVarnam() {
refreshWords()
}
}

func refreshWords() {
do {
words = try varnam.getRecentlyLearnedWords()
} catch let error {
Logger.log.error(error.localizedDescription)
}
}

func unlearn(_ word: String) {
do {
try varnam.unlearn(word)
} catch let error {
Logger.log.error(error.localizedDescription)
}
refreshWords()
}

func changeScheme(_ id: String) {
schemeID = id
schemeLangName = languages.first(where: { $0.identifier == schemeID })?.DisplayName ?? ""
initVarnam()
refreshWords()
}
}

struct RecentlyLearnedWordsView: View {
// Changes in model will automatically reload the table view
@ObservedObject var model = RLWModel()

var body: some View {
VStack(alignment: .leading) {
HStack {
Text("Language: ")
MenuButton(model.schemeLangName) {
ForEach(model.languages, id: \.self) { (lang) in
Button(lang.DisplayName) {
self.model.changeScheme(lang.identifier)
}
}
}
.fixedSize()
.padding(0)
}
Spacer(minLength: 5)
RLWTable(
words: model.words,
unlearn: model.unlearn
)
}.padding(16)
}
}

struct RecentlyLearnedWordsView_Previews: PreviewProvider {
static var previews: some View {
RecentlyLearnedWordsView()
}
}
2 changes: 1 addition & 1 deletion Application/VarnamApp.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<false/>
<key>com.apple.security.application-groups</key>
<array>
<string>group.varnamproject.Varnam</string>
Expand Down
26 changes: 23 additions & 3 deletions Common/VarnamConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ struct LanguageConfig: Codable, Equatable, Hashable {
var isEnabled: Bool
var shortcutKey: String?
var shortcutModifiers: UInt?

// TODO make this struct same as SchemeDetails
var Identifier: String
var DisplayName: String
var LangCode: String
}

class VarnamConfig: Config {
Expand Down Expand Up @@ -96,16 +101,27 @@ class VarnamConfig: Config {

var languageConfig: [LanguageConfig] {
get {
var langConfigs = factoryLanguageConfig
if let encoded = userDefaults.data(forKey: #function) {
do {
return try JSONDecoder().decode(Array<LanguageConfig>.self, from: encoded)
let savedLangConfigs = try JSONDecoder().decode(Array<LanguageConfig>.self, from: encoded)
for slc in savedLangConfigs {
if let row = langConfigs.firstIndex(where: {$0.identifier == slc.identifier}) {
// Only changing the setting values
// Other properties such as display name are constant,
// They are obtained from VST
langConfigs[row].isEnabled = slc.isEnabled
langConfigs[row].shortcutKey = slc.shortcutKey
langConfigs[row].shortcutModifiers = slc.shortcutModifiers
}
}
}
catch {
Logger.log.error("Exception while trying to decode languageConfig: \(error)")
resetLanguageConfig()
}
}
return factoryLanguageConfig
return langConfigs
}
set(value) {
let encodedData: Data = try! JSONEncoder().encode(value)
Expand All @@ -121,7 +137,11 @@ class VarnamConfig: Config {
configs.append(LanguageConfig(
identifier: scheme.Identifier,
language: scheme.DisplayName,
isEnabled: true
isEnabled: true,

Identifier: scheme.Identifier,
DisplayName: scheme.DisplayName,
LangCode: scheme.LangCode
))
}
return configs
Expand Down
Loading

0 comments on commit 7f00045

Please sign in to comment.