mirror of
https://github.com/mii443/akaza.git
synced 2025-08-22 14:55:31 +00:00
User dict (#286)
* refactor dict-pane * refactor * add user_dict adding feature * clippy --fix * cargo fmt * clippy fix * optimize imports * user dictionary editing menu * open dict * show surface * add row * implement delete button * refacor * save user dict * clippy fix
This commit is contained in:
16
Cargo.lock
generated
16
Cargo.lock
generated
@ -58,6 +58,21 @@ dependencies = [
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "akaza-dict"
|
||||
version = "0.1.7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"encoding_rs",
|
||||
"env_logger",
|
||||
"gtk4",
|
||||
"libakaza",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_yaml",
|
||||
"xdg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
@ -963,6 +978,7 @@ name = "ibus-akaza"
|
||||
version = "0.1.7"
|
||||
dependencies = [
|
||||
"akaza-conf",
|
||||
"akaza-dict",
|
||||
"anyhow",
|
||||
"cc",
|
||||
"chrono",
|
||||
|
@ -1,2 +1,2 @@
|
||||
[workspace]
|
||||
members = ["libakaza", "marisa-sys", "ibus-akaza", "ibus-sys", "akaza-data", "akaza-conf"]
|
||||
members = ["libakaza", "marisa-sys", "ibus-akaza", "ibus-sys", "akaza-data", "akaza-conf", "akaza-dict"]
|
||||
|
@ -1,15 +1,52 @@
|
||||
use gtk4::prelude::{
|
||||
ButtonExt, ComboBoxExt, DialogExt, FileChooserExt, FileExt, GridExt, GtkWindowExt, WidgetExt,
|
||||
};
|
||||
use gtk4::{
|
||||
Button, ComboBoxText, FileChooserAction, FileChooserDialog, Grid, Label, ResponseType,
|
||||
ScrolledWindow, Window,
|
||||
};
|
||||
use libakaza::config::{Config, DictConfig, DictEncoding, DictType, DictUsage};
|
||||
use log::info;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use gtk4::builders::MessageDialogBuilder;
|
||||
use gtk4::prelude::ButtonExt;
|
||||
use gtk4::prelude::ComboBoxExt;
|
||||
use gtk4::prelude::DialogExt;
|
||||
use gtk4::prelude::EntryBufferExt;
|
||||
use gtk4::prelude::EntryBufferExtManual;
|
||||
use gtk4::prelude::FileChooserExt;
|
||||
use gtk4::prelude::FileExt;
|
||||
use gtk4::prelude::GridExt;
|
||||
use gtk4::prelude::GtkWindowExt;
|
||||
use gtk4::prelude::WidgetExt;
|
||||
use gtk4::{
|
||||
Button, ComboBoxText, FileChooserAction, FileChooserDialog, Grid, Label, MessageType,
|
||||
ResponseType, ScrolledWindow, Text, TextBuffer, TextView, Window,
|
||||
};
|
||||
use log::info;
|
||||
|
||||
use libakaza::config::{Config, DictConfig, DictEncoding, DictType, DictUsage};
|
||||
use libakaza::dict::skk::write::write_skk_dict;
|
||||
|
||||
pub fn build_dict_pane(config: Arc<Mutex<Config>>) -> anyhow::Result<ScrolledWindow> {
|
||||
let scroll = ScrolledWindow::new();
|
||||
|
||||
let parent_grid = Grid::builder().column_spacing(10).build();
|
||||
let grid = Grid::builder().column_spacing(10).build();
|
||||
|
||||
let dicts = config.lock().unwrap().engine.dicts.clone();
|
||||
|
||||
for (i, dict_config) in dicts.iter().enumerate() {
|
||||
add_row(&grid, dict_config, &config.clone(), i);
|
||||
}
|
||||
|
||||
parent_grid.attach(&grid, 0, 0, 1, 1);
|
||||
|
||||
{
|
||||
let add_system_dict_btn = build_add_system_dict_btn(config.clone(), grid.clone());
|
||||
parent_grid.attach(&add_system_dict_btn, 0, 1, 1, 1);
|
||||
}
|
||||
{
|
||||
let add_user_dict_btn = build_add_user_dict_btn(grid, config);
|
||||
parent_grid.attach(&add_user_dict_btn, 0, 2, 1, 1);
|
||||
}
|
||||
scroll.set_child(Some(&parent_grid));
|
||||
Ok(scroll)
|
||||
}
|
||||
|
||||
// TODO ここは TreeView 使った方がすっきり書けるはずだが、僕の GTK+ 力が引くすぎて対応できていない。
|
||||
// 誰かすっきり使い易くしてほしい。
|
||||
fn add_row(grid: &Grid, dict_config: &DictConfig, config: &Arc<Mutex<Config>>, i: usize) {
|
||||
@ -114,22 +151,8 @@ pub fn build_dict_pane(config: Arc<Mutex<Config>>) -> anyhow::Result<ScrolledWin
|
||||
}
|
||||
}
|
||||
|
||||
let scroll = ScrolledWindow::new();
|
||||
|
||||
let parent_grid = Grid::builder().column_spacing(10).build();
|
||||
let grid = Grid::builder().column_spacing(10).build();
|
||||
|
||||
let dicts = config.lock().unwrap().engine.dicts.clone();
|
||||
|
||||
for (i, dict_config) in dicts.iter().enumerate() {
|
||||
add_row(&grid, dict_config, &config.clone(), i);
|
||||
}
|
||||
|
||||
parent_grid.attach(&grid, 0, 0, 1, 1);
|
||||
|
||||
{
|
||||
let add_btn = {
|
||||
let add_btn = Button::with_label("Add");
|
||||
fn build_add_system_dict_btn(config: Arc<Mutex<Config>>, grid: Grid) -> Button {
|
||||
let add_btn = Button::with_label("システム辞書の追加");
|
||||
let config = config;
|
||||
let grid = grid;
|
||||
add_btn.connect_clicked(move |_| {
|
||||
@ -183,9 +206,97 @@ pub fn build_dict_pane(config: Arc<Mutex<Config>>) -> anyhow::Result<ScrolledWin
|
||||
dialog.show();
|
||||
});
|
||||
add_btn
|
||||
}
|
||||
|
||||
fn build_add_user_dict_btn(dict_list_grid: Grid, config: Arc<Mutex<Config>>) -> Button {
|
||||
let add_btn = Button::with_label("ユーザー辞書の追加");
|
||||
let config = config;
|
||||
let dict_list_grid = dict_list_grid;
|
||||
add_btn.connect_clicked(move |_| {
|
||||
let window = Window::builder()
|
||||
.title("ユーザー辞書の追加")
|
||||
.default_width(300)
|
||||
.default_height(100)
|
||||
.destroy_with_parent(true)
|
||||
.modal(true)
|
||||
.build();
|
||||
|
||||
let grid = Grid::builder().build();
|
||||
|
||||
let label = TextView::builder()
|
||||
.buffer(&TextBuffer::builder().text("辞書名: ").build())
|
||||
.build();
|
||||
grid.attach(&label, 0, 0, 1, 1);
|
||||
|
||||
let text = Text::builder().build();
|
||||
grid.attach(&text, 1, 0, 2, 1);
|
||||
|
||||
let ok_btn = {
|
||||
let window = window.clone();
|
||||
let ok_btn = Button::with_label("OK");
|
||||
let text = text.clone();
|
||||
let config = config.clone();
|
||||
let dict_list_grid = dict_list_grid.clone();
|
||||
ok_btn.set_sensitive(false);
|
||||
ok_btn.connect_clicked(move |_| match create_user_dict(&text.buffer().text()) {
|
||||
Ok(path) => {
|
||||
let dict_config = DictConfig {
|
||||
path: path.to_string_lossy().to_string(),
|
||||
encoding: DictEncoding::Utf8,
|
||||
dict_type: DictType::SKK,
|
||||
usage: DictUsage::Normal,
|
||||
};
|
||||
parent_grid.attach(&add_btn, 0, 1, 1, 1);
|
||||
let mut locked_conf = config.lock().unwrap();
|
||||
add_row(
|
||||
&dict_list_grid,
|
||||
&dict_config,
|
||||
&config,
|
||||
locked_conf.engine.dicts.len(),
|
||||
);
|
||||
locked_conf.engine.dicts.push(dict_config);
|
||||
window.close();
|
||||
}
|
||||
scroll.set_child(Some(&parent_grid));
|
||||
Ok(scroll)
|
||||
Err(err) => {
|
||||
let dialog = MessageDialogBuilder::new()
|
||||
.message_type(MessageType::Error)
|
||||
.text(&format!("Error: {err}"))
|
||||
.build();
|
||||
dialog.show();
|
||||
}
|
||||
});
|
||||
grid.attach(&ok_btn, 1, 1, 1, 1);
|
||||
ok_btn
|
||||
};
|
||||
|
||||
{
|
||||
let window = window.clone();
|
||||
let cancel_btn = Button::with_label("Cancel");
|
||||
cancel_btn.connect_clicked(move |_| {
|
||||
window.close();
|
||||
});
|
||||
grid.attach(&cancel_btn, 2, 1, 1, 1);
|
||||
}
|
||||
|
||||
// 辞書名を入力していない場合は OK ボタンを押せない。
|
||||
text.buffer().connect_length_notify(move |t| {
|
||||
ok_btn.set_sensitive(!t.text().is_empty());
|
||||
});
|
||||
|
||||
window.set_child(Some(&grid));
|
||||
window.show();
|
||||
});
|
||||
add_btn
|
||||
}
|
||||
|
||||
fn create_user_dict(name: &str) -> anyhow::Result<PathBuf> {
|
||||
let base = xdg::BaseDirectories::with_prefix("akaza")?;
|
||||
|
||||
let userdictdir = base.create_data_directory("userdict")?;
|
||||
let path = userdictdir.join(name);
|
||||
if !path.as_path().exists() {
|
||||
// ファイルがなければカラの SKK 辞書をつくっておく。
|
||||
write_skk_dict(&path.to_string_lossy(), vec![])?;
|
||||
}
|
||||
|
||||
Ok(path)
|
||||
}
|
||||
|
20
akaza-dict/Cargo.toml
Normal file
20
akaza-dict/Cargo.toml
Normal file
@ -0,0 +1,20 @@
|
||||
[package]
|
||||
name = "akaza-dict"
|
||||
version = "0.1.7"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
||||
[dependencies]
|
||||
gtk4 = "0.5.5"
|
||||
xdg = "2.4.1"
|
||||
log = "0.4.17"
|
||||
env_logger = "0.10.0"
|
||||
libakaza = { path = "../libakaza" }
|
||||
anyhow = "1.0.68"
|
||||
serde = "1.0.152"
|
||||
serde_yaml = "0.9.17"
|
||||
encoding_rs = "0.8.31"
|
||||
|
||||
[[bin]]
|
||||
name = "akaza-dict"
|
||||
path = "src/bin/akaza-dict.rs"
|
17
akaza-dict/src/bin/akaza-dict.rs
Normal file
17
akaza-dict/src/bin/akaza-dict.rs
Normal file
@ -0,0 +1,17 @@
|
||||
use anyhow::Result;
|
||||
use log::LevelFilter;
|
||||
|
||||
use akaza_dict::conf::open_userdict_window;
|
||||
|
||||
use std::env;
|
||||
|
||||
/// デバッグ用
|
||||
fn main() -> Result<()> {
|
||||
let _ = env_logger::builder()
|
||||
.filter_level(LevelFilter::Info)
|
||||
.try_init();
|
||||
|
||||
let args: Vec<String> = env::args().collect();
|
||||
open_userdict_window(&args[1])?;
|
||||
Ok(())
|
||||
}
|
176
akaza-dict/src/conf.rs
Normal file
176
akaza-dict/src/conf.rs
Normal file
@ -0,0 +1,176 @@
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use anyhow::Result;
|
||||
use encoding_rs::UTF_8;
|
||||
use gtk::glib::signal::Inhibit;
|
||||
use gtk::prelude::*;
|
||||
use gtk::{Application, ApplicationWindow, Button, ListStore};
|
||||
use gtk4 as gtk;
|
||||
use gtk4::builders::MessageDialogBuilder;
|
||||
use gtk4::gio::ApplicationFlags;
|
||||
use gtk4::glib::Type;
|
||||
|
||||
use gtk4::{CellRendererText, Grid, MessageType, TreeView, TreeViewColumn};
|
||||
use log::{info, trace};
|
||||
|
||||
use libakaza::config::Config;
|
||||
use libakaza::dict::skk::read::read_skkdict;
|
||||
use libakaza::dict::skk::write::write_skk_dict;
|
||||
|
||||
pub fn open_userdict_window(user_dict_path: &str) -> Result<()> {
|
||||
let config = Arc::new(Mutex::new(Config::load()?));
|
||||
let app = Application::new(Some("com.github.akaza.config"), ApplicationFlags::empty());
|
||||
|
||||
let user_dict_path = user_dict_path.to_string();
|
||||
app.connect_activate(move |app| {
|
||||
connect_activate(app, config.clone(), &user_dict_path).unwrap();
|
||||
});
|
||||
|
||||
let v: Vec<String> = Vec::new();
|
||||
app.run_with_args(v.as_slice());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn connect_activate(
|
||||
app: &Application,
|
||||
_config: Arc<Mutex<Config>>,
|
||||
user_dict_path: &str,
|
||||
) -> Result<()> {
|
||||
let window = ApplicationWindow::builder()
|
||||
.application(app)
|
||||
.default_width(520)
|
||||
.default_height(500)
|
||||
.title("Akaza の設定")
|
||||
.build();
|
||||
|
||||
let grid = Grid::builder().build();
|
||||
|
||||
info!("Loading skk dict from {user_dict_path}");
|
||||
let dict = read_skkdict(Path::new(user_dict_path), UTF_8)?;
|
||||
let dict = dict
|
||||
.iter()
|
||||
.flat_map(|(yomi, surfaces)| {
|
||||
surfaces
|
||||
.iter()
|
||||
.map(|surface| (yomi.to_string(), surface.to_string()))
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let list_store = ListStore::new(&[Type::STRING, Type::STRING]);
|
||||
for (yomi, surface) in dict {
|
||||
list_store.set(&list_store.append(), &[(0, &yomi), (1, &surface)]);
|
||||
}
|
||||
// list_store.set(&list_store.append(), &[(0, &"world".to_string())]);
|
||||
let tree_view = TreeView::builder().model(&list_store).build();
|
||||
{
|
||||
let tree_view_column = build_tree_view_column("読み", 0, list_store.clone());
|
||||
tree_view.append_column(&tree_view_column);
|
||||
}
|
||||
{
|
||||
let tree_view_column = build_tree_view_column("表記", 1, list_store.clone());
|
||||
tree_view.append_column(&tree_view_column);
|
||||
}
|
||||
// https://gitlab.gnome.org/GNOME/gtk/-/issues/3561
|
||||
grid.attach(&tree_view, 0, 0, 6, 1);
|
||||
|
||||
// TODO このへんは Menu にしたい。gtk4-rs で menu を使う方法が分からん。
|
||||
let add_button = Button::with_label("追加");
|
||||
{
|
||||
let list_store = list_store.clone();
|
||||
add_button.connect_clicked(move |_| {
|
||||
info!("Add new row...");
|
||||
list_store.set(&list_store.append(), &[(0, &""), (1, &"")]);
|
||||
});
|
||||
}
|
||||
grid.attach(&add_button, 4, 1, 1, 1);
|
||||
|
||||
{
|
||||
let delete_btn = Button::with_label("削除");
|
||||
let list_store = list_store.clone();
|
||||
let tree_view = tree_view;
|
||||
delete_btn.connect_clicked(move |_| {
|
||||
let selection = tree_view.selection();
|
||||
let Some((_, tree_iter)) = selection.selected() else {
|
||||
return;
|
||||
};
|
||||
list_store.remove(&tree_iter);
|
||||
});
|
||||
grid.attach(&delete_btn, 5, 1, 1, 1);
|
||||
}
|
||||
|
||||
{
|
||||
let save_btn = Button::with_label("保存");
|
||||
let user_dict_path = user_dict_path.to_string();
|
||||
save_btn.connect_clicked(move |_| {
|
||||
let Some(iter) = list_store.iter_first() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let mut dict: HashMap<String, Vec<String>> = HashMap::new();
|
||||
|
||||
loop {
|
||||
let yomi: String = list_store.get(&iter, 0);
|
||||
let surface: String = list_store.get(&iter, 1);
|
||||
info!("Got: {}, {}", yomi, surface);
|
||||
|
||||
dict.entry(yomi).or_insert_with(Vec::new).push(surface);
|
||||
|
||||
if !list_store.iter_next(&iter) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(err) = write_skk_dict(&(user_dict_path.to_string() + ".tmp"), vec![dict]) {
|
||||
let dialog = MessageDialogBuilder::new()
|
||||
.message_type(MessageType::Error)
|
||||
.text(&format!("Error: {err}"))
|
||||
.build();
|
||||
dialog.show();
|
||||
}
|
||||
info!("Renaming file");
|
||||
if let Err(err) = fs::rename(user_dict_path.to_string() + ".tmp", &user_dict_path) {
|
||||
let dialog = MessageDialogBuilder::new()
|
||||
.message_type(MessageType::Error)
|
||||
.text(&format!("Error: {err}"))
|
||||
.build();
|
||||
dialog.show();
|
||||
}
|
||||
});
|
||||
grid.attach(&save_btn, 6, 1, 1, 1);
|
||||
}
|
||||
|
||||
window.set_child(Some(&grid));
|
||||
|
||||
window.connect_close_request(move |window| {
|
||||
if let Some(application) = window.application() {
|
||||
application.remove_window(window);
|
||||
}
|
||||
Inhibit(false)
|
||||
});
|
||||
|
||||
window.show();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn build_tree_view_column(title: &str, column: u32, list_store: ListStore) -> TreeViewColumn {
|
||||
let cell_renderer = CellRendererText::builder()
|
||||
.editable(true)
|
||||
.xpad(20)
|
||||
.ypad(20)
|
||||
.build();
|
||||
cell_renderer.connect_edited(move |_cell_renderer, _treepath, _str| {
|
||||
trace!("{:?}, {:?}", _treepath, _str);
|
||||
if _str.is_empty() {
|
||||
return;
|
||||
}
|
||||
let Some(iter) = list_store.iter(&_treepath) else {
|
||||
return;
|
||||
};
|
||||
list_store.set_value(&iter, column, &_str.to_value());
|
||||
});
|
||||
TreeViewColumn::with_attributes(title, &cell_renderer, &[("text", column as i32)])
|
||||
}
|
1
akaza-dict/src/lib.rs
Normal file
1
akaza-dict/src/lib.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod conf;
|
@ -13,6 +13,7 @@ log = "0.4.17"
|
||||
libakaza = { path = "../libakaza" }
|
||||
ibus-sys = { path = "../ibus-sys" }
|
||||
akaza-conf = { path = "../akaza-conf" }
|
||||
akaza-dict = { path = "../akaza-dict" }
|
||||
env_logger = "0.10.0"
|
||||
clap = { version = "4.1.1", features = ["derive"] }
|
||||
clap-verbosity-flag = "2.0.0"
|
||||
|
@ -2,9 +2,10 @@ use std::collections::HashMap;
|
||||
|
||||
use anyhow::Result;
|
||||
use kelp::{h2z, hira2kata, z2h, ConvOption};
|
||||
use log::{debug, error, info, trace, warn};
|
||||
use log::{error, info, trace, warn};
|
||||
|
||||
use akaza_conf::conf::open_configuration_window;
|
||||
use akaza_dict::conf::open_userdict_window;
|
||||
use ibus_sys::core::{
|
||||
IBusModifierType_IBUS_CONTROL_MASK, IBusModifierType_IBUS_HYPER_MASK,
|
||||
IBusModifierType_IBUS_META_MASK, IBusModifierType_IBUS_MOD1_MASK,
|
||||
@ -65,7 +66,7 @@ impl AkazaContext {
|
||||
current_state: CurrentState::new(input_mode, config.live_conversion, romkan, engine),
|
||||
command_map: ibus_akaza_commands_map(),
|
||||
keymap: IBusKeyMap::new(keymap)?,
|
||||
prop_controller: PropController::new(input_mode)?,
|
||||
prop_controller: PropController::new(input_mode, config)?,
|
||||
})
|
||||
}
|
||||
|
||||
@ -76,7 +77,7 @@ impl AkazaContext {
|
||||
prop_name: String,
|
||||
prop_state: guint,
|
||||
) {
|
||||
debug!("do_property_activate: {}, {}", prop_name, prop_state);
|
||||
info!("do_property_activate: {}, {}", prop_name, prop_state);
|
||||
if prop_name == "PrefPane" {
|
||||
match open_configuration_window() {
|
||||
Ok(_) => {}
|
||||
@ -86,6 +87,14 @@ impl AkazaContext {
|
||||
&& prop_name.starts_with("InputMode.")
|
||||
{
|
||||
self.input_mode_activate(engine, prop_name, prop_state);
|
||||
} else if prop_name.starts_with("UserDict.") {
|
||||
let dict_path = prop_name.replace("UserDict.", "");
|
||||
info!("Edit the {}", dict_path);
|
||||
|
||||
match open_userdict_window(&dict_path) {
|
||||
Ok(_) => {}
|
||||
Err(e) => error!("Err: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
@ -13,6 +14,7 @@ use ibus_sys::property::{
|
||||
IBusProperty,
|
||||
};
|
||||
use ibus_sys::text::{IBusText, StringExt};
|
||||
use libakaza::config::{Config, DictConfig};
|
||||
|
||||
use crate::input_mode::{get_all_input_modes, InputMode};
|
||||
|
||||
@ -25,8 +27,8 @@ pub struct PropController {
|
||||
}
|
||||
|
||||
impl PropController {
|
||||
pub fn new(initial_input_mode: InputMode) -> Result<Self> {
|
||||
let (input_mode_prop, prop_list, prop_dict) = Self::init_props(initial_input_mode);
|
||||
pub fn new(initial_input_mode: InputMode, config: Config) -> Result<Self> {
|
||||
let (input_mode_prop, prop_list, prop_dict) = Self::init_props(initial_input_mode, config)?;
|
||||
|
||||
Ok(PropController {
|
||||
prop_list,
|
||||
@ -47,11 +49,12 @@ impl PropController {
|
||||
/// * `initial_input_mode`: 初期状態の input_mode
|
||||
fn init_props(
|
||||
initial_input_mode: InputMode,
|
||||
) -> (
|
||||
config: Config,
|
||||
) -> Result<(
|
||||
*mut IBusProperty,
|
||||
*mut IBusPropList,
|
||||
HashMap<String, *mut IBusProperty>,
|
||||
) {
|
||||
)> {
|
||||
unsafe {
|
||||
let prop_list =
|
||||
g_object_ref_sink(ibus_prop_list_new() as gpointer) as *mut IBusPropList;
|
||||
@ -90,10 +93,71 @@ impl PropController {
|
||||
prop_map.insert(input_mode.prop_name.to_string(), prop);
|
||||
ibus_prop_list_append(props, prop);
|
||||
}
|
||||
|
||||
ibus_property_set_sub_props(input_mode_prop, props);
|
||||
|
||||
// ユーザー辞書
|
||||
Self::build_user_dict(prop_list, config)?;
|
||||
|
||||
// 設定ファイルを開くというやつ
|
||||
Self::build_preference_menu(prop_list);
|
||||
|
||||
Ok((input_mode_prop, prop_list, prop_map))
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn build_user_dict(prop_list: *mut IBusPropList, config: Config) -> Result<()> {
|
||||
let user_dict_prop = g_object_ref_sink(ibus_property_new(
|
||||
"UserDict\0".as_ptr() as *const gchar,
|
||||
IBusPropType_PROP_TYPE_MENU,
|
||||
"ユーザー辞書".to_ibus_text(),
|
||||
"\0".as_ptr() as *const gchar,
|
||||
"User dict".to_ibus_text(),
|
||||
to_gboolean(true),
|
||||
to_gboolean(true),
|
||||
IBusPropState_PROP_STATE_UNCHECKED,
|
||||
std::ptr::null_mut() as *mut IBusPropList,
|
||||
) as gpointer) as *mut IBusProperty;
|
||||
ibus_prop_list_append(prop_list, user_dict_prop);
|
||||
|
||||
let props = g_object_ref_sink(ibus_prop_list_new() as gpointer) as *mut IBusPropList;
|
||||
for dict in Self::find_user_dicts(config)? {
|
||||
let prop = g_object_ref_sink(ibus_property_new(
|
||||
("UserDict.".to_string() + dict.path.as_str() + "\0").as_ptr() as *const gchar,
|
||||
IBusPropType_PROP_TYPE_MENU,
|
||||
Path::new(&dict.path)
|
||||
.file_name()
|
||||
.unwrap()
|
||||
.to_string_lossy()
|
||||
.to_ibus_text(),
|
||||
"\0".as_ptr() as *const gchar,
|
||||
std::ptr::null_mut() as *mut IBusText,
|
||||
to_gboolean(true),
|
||||
to_gboolean(true),
|
||||
IBusPropState_PROP_STATE_UNCHECKED,
|
||||
std::ptr::null_mut() as *mut IBusPropList,
|
||||
) as gpointer) as *mut IBusProperty;
|
||||
// prop_map.insert(input_mode.prop_name.to_string(), prop);
|
||||
ibus_prop_list_append(props, prop);
|
||||
}
|
||||
ibus_property_set_sub_props(user_dict_prop, props);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn find_user_dicts(config: Config) -> anyhow::Result<Vec<DictConfig>> {
|
||||
let dir = xdg::BaseDirectories::with_prefix("akaza")?;
|
||||
let dir = dir.create_data_directory("userdict")?;
|
||||
let dicts = config
|
||||
.engine
|
||||
.dicts
|
||||
.iter()
|
||||
.filter(|f| f.path.contains(&dir.to_string_lossy().to_string()))
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Ok(dicts)
|
||||
}
|
||||
|
||||
unsafe fn build_preference_menu(prop_list: *mut IBusPropList) {
|
||||
let preference_prop = g_object_ref_sink(ibus_property_new(
|
||||
"PrefPane\0".as_ptr() as *const gchar,
|
||||
IBusPropType_PROP_TYPE_MENU,
|
||||
@ -106,9 +170,6 @@ impl PropController {
|
||||
std::ptr::null_mut() as *mut IBusPropList,
|
||||
) as gpointer) as *mut IBusProperty;
|
||||
ibus_prop_list_append(prop_list, preference_prop);
|
||||
|
||||
(input_mode_prop, prop_list, prop_map)
|
||||
}
|
||||
}
|
||||
|
||||
/// input_mode の切り替え時に実行される処理
|
||||
|
Reference in New Issue
Block a user