
ç§ã¯RustãåŠã³ç¶ããŠããŸãã ç§ã¯ãŸã ããŸãç¥ããªãã®ã§ãå€ãã®ééããç¯ããŸãã ååãç§ã¯ã¹ããŒã¯ã²ãŒã ãäœãããšããŸããã ãµã€ã¯ã«ãã³ã¬ã¯ã·ã§ã³ã3D Three.rsã§ã®äœæ¥ãè©ŠããŸãã ã ggezãšAmethystã«ã€ããŠåŠã³ãŸããã ä»åã¯ããã£ããçšã®ã¯ã©ã€ã¢ã³ããšãµãŒããŒãäœæããããšããŸããã GUIã«ã¯Azulã䜿çšããŸããã Conrod ã Yew ã OrbtkãèŠãŸãã ã ãã«ãã¹ã¬ããããã£ãã«ããããã¯ãŒã¯ãè©ŠããŸããã åã®èšäºã®ééããèæ ®ã«å ¥ããŠãããããã詳现ã«ããããšããŸããã 詳现ã«ã€ããŠã¯ãç«ãžããããã
â ãœãŒã¹ãWindows 10 x64ã§åäœ
ãããã¯ãŒãã³ã°ã§ã¯ããã®ãããã³ã«ã䜿çšããŠæ¬¡ã®ãããžã§ã¯ããäœæããããã§ãã¬ãŒãã³ã°ãããã£ãã®ã§ãUDPã䜿çšããŸããã GUIã«ã€ããŠã¯ãRustã®ãããžã§ã¯ããããã«èŠãŠããããã®åºæ¬çãªäŸãèŠãŠãAzulã¯Document Object ModelãšCSSã®ãããªã¹ã¿ã€ã«ãšã³ãžã³ã䜿çšããŠããã®ã§ãç§ã¯é·ãéWebéçºã«åŸäºããŠããŸããã äžè¬çã«ããã¬ãŒã ã¯ãŒã¯ã¯äž»èŠ³çã«éžæããŸããã ãããŸã§ã®ãšããããã£ãŒãã¢ã«ãã¡ã§ã¯ãã¹ã¯ããŒã«ã¯æ©èœãããå ¥åãã©ãŒã«ã¹ã¯æ©èœãããã«ãŒãœã«ã¯ãããŸããã ããã¹ããã£ãŒã«ãã«ããŒã¿ãå ¥åããã«ã¯ãå ¥åäžã«ãã®äžã«ã«ãŒãœã«ã眮ãããã®äžã«çŽæ¥ä¿æããå¿ èŠããããŸãã 詳现
å®éãèšäºã®ã»ãšãã©ã¯ã³ãŒãã³ã¡ã³ãã§ãã
ã¢ãºãŒã«
æ©èœã¹ã¿ã€ã«ãDOMãCSSã䜿çšããGUIãã¬ãŒã ã¯ãŒã¯ã ã€ã³ã¿ãŒãã§ã€ã¹ã¯ã«ãŒãèŠçŽ ã§æ§æãããŸããã«ãŒãèŠçŽ ã«ã¯å€ãã®åå«ããããHTMLãXMLãªã©ã§ç¬èªã®åå«ãæã€ããšãã§ããŸãã ã€ã³ã¿ãŒãã§ãŒã¹å šäœã¯ãåäžã®DataModelã®ããŒã¿ã«åºã¥ããŠäœæãããŸãã ãã®äžã§ããã¹ãŠã®ããŒã¿ã¯äžè¬ã«ãã¬ãŒã³ããŒã·ã§ã³ã«è»¢éãããŸãã ASP.NETã«æ £ããŠãã人ã¯ãAzulãšãã®DataModelã¯Razorãšãã®ViewModelã«äŒŒãŠããŸãã HTMLãšåæ§ã«ãé¢æ°ãDOMèŠçŽ ã®ã€ãã³ãã«ãã€ã³ãã§ããŸãã CSSãã¬ãŒã ã¯ãŒã¯ã䜿çšããŠèŠçŽ ãã¹ã¿ã€ã«ã§ããŸãã ããã¯HTMLãšåãCSSã§ã¯ãããŸããããéåžžã«äŒŒãŠããŸãã ãŸããWangãUWPã®AngularãŸãã¯MVVMã®ãããªåæ¹åãã€ã³ãã£ã³ã°ããããŸãã ãµã€ãã®è©³çŽ°ã
æ®ãã®ãã¬ãŒã ã¯ãŒã¯ã®ç°¡åãªæŠèŠ
- Orbtk -Azulãšã»ãŒåãã§ããã£ãŒãã¢ã«ãã¡ã§ã
- Conrod- ãããªã¯ãã¹ãã©ãããã©ãŒã ã®ãã¹ã¯ãããã¢ããªã±ãŒã·ã§ã³ãäœæã§ããŸãã
- Yewã¯WebAssemblyã§ãããReactã«äŒŒãŠããŸãã Webéçºçšã
ã客æ§
ãœã±ããã®èªã¿åãããã³æžã蟌ã¿çšã®è£å©æ©èœãã°ã«ãŒãåãããŠããæ§é
struct ChatService {} impl ChatService { //1 fn read_data(socket: &Option<UdpSocket>) -> Option<String> { //2 let mut buf = [0u8; 4096]; match socket { Some(s) => { //3 match s.recv(&mut buf) { //4 Ok(count) => Some(String::from_utf8(buf[..count].into()) .expect("can't parse to String")), Err(e) => { //5 println!("Error {}", e); None } } } _ => None, } } //6 fn send_to_socket(message: String, socket: &Option<UdpSocket>) { match socket { //7 Some(s) => { s.send(message.as_bytes()).expect("can't send"); } _ => return, } } }
- ãœã±ããããããŒã¿ãèªã¿åã
- ãœã±ããããèªã¿åãããããŒã¿ã®ãããã¡ãŒã
- ããããã³ã°ã³ãŒã«ã ããã§ãå®è¡ã¹ã¬ããã¯ãããŒã¿ãèªã¿åãããããã¿ã€ã ã¢ãŠããçºçãããŸã§åæ¢ããŸãã
- UTF8ãšã³ã³ãŒãã£ã³ã°ã®ãã€ãé åããæååãååŸããŸãã
- æ¥ç¶ãã¿ã€ã ã¢ãŠãã«ãã£ãŠäžæãããããå¥ã®ãšã©ãŒãçºçããå Žåãããã«å°éããŸãã
- æååããœã±ããã«éä¿¡ããŸãã
- æååãUTF8ãšã³ã³ãŒãã£ã³ã°ã®ãã€ãã«å€æããããŒã¿ããœã±ããã«éä¿¡ããŸãã ãœã±ãããžã®ããŒã¿ã®æžã蟌ã¿ã¯ãããã¯ãããŸãããã€ãŸãã å®è¡ã®ã¹ã¬ããã¯äœæ¥ãç¶ç¶ããŸãã ããŒã¿ãéä¿¡ã§ããªãã£ãå Žåããéä¿¡ã§ããŸããããšããã¡ãã»ãŒãžã§ããã°ã©ã ãäžæããŸãã
ãŠãŒã¶ãŒããã®ã€ãã³ããåŠçããDataModelãå€æŽããããã®é¢æ°ãã°ã«ãŒãåããæ§é
struct Controller {} //1 const TIMEOUT_IN_MILLIS: u64 = 2000; impl Controller { //2 fn send_pressed(app_state: &mut azul::prelude::AppState<ChatDataModel>, _event: azul::prelude::WindowEvent<ChatDataModel>) -> azul::prelude::UpdateScreen { //3 let data = app_state.data.lock().unwrap(); //4 let message = data.messaging_model.text_input_state.text.clone(); data.messaging_model.text_input_state.text = "".into(); //5 ChatService::send_to_socket(message, &data.messaging_model.socket); //6 azul::prelude::UpdateScreen::Redraw } //7 fn login_pressed(app_state: &mut azul::prelude::AppState<ChatDataModel>, _event: azul::prelude::WindowEvent<ChatDataModel>) -> azul::prelude::UpdateScreen { //8 use std::time::Duration; //9 if let Some(ref _s) = app_state.data.clone().lock().unwrap().messaging_model.socket { return azul::prelude::UpdateScreen::DontRedraw; } //10 app_state.add_task(Controller::read_from_socket_async, &[]); //11 app_state.add_daemon(azul::prelude::Daemon::unique(azul::prelude::DaemonCallback(Controller::redraw_daemon))); //12 let mut data = app_state.data.lock().unwrap(); //13 let local_address = format!("127.0.0.1:{}", data.login_model.port_input.text.clone().trim()); //14 let socket = UdpSocket::bind(&local_address) .expect(format!("can't bind socket to {}", local_address).as_str()); //15 let remote_address = data.login_model.address_input.text.clone().trim().to_string(); //16 socket.connect(&remote_address) .expect(format!("can't connect to {}", &remote_address).as_str()); //17 socket.set_read_timeout(Some(Duration::from_millis(TIMEOUT_IN_MILLIS))) .expect("can't set time out to read"); // 18 data.logged_in = true; // 19 data.messaging_model.socket = Option::Some(socket); //20 azul::prelude::UpdateScreen::Redraw } //21 fn read_from_socket_async(app_data: Arc<Mutex<ChatDataModel>>, _: Arc<()>) { //22 let socket = Controller::get_socket(app_data.clone()); loop { //23 if let Some(message) = ChatService::read_data(&socket) { //24 app_data.modify(|state| { //25 state.messaging_model.has_new_message = true; //26 state.messaging_model.messages.push(message); }); } } } //27 fn redraw_daemon(state: &mut ChatDataModel, _repres: &mut azul::prelude::Apprepres) -> (azul::prelude::UpdateScreen, azul::prelude::TerminateDaemon) { //28 if state.messaging_model.has_new_message { state.messaging_model.has_new_message = false; (azul::prelude::UpdateScreen::Redraw, azul::prelude::TerminateDaemon::Continue) } else { (azul::prelude::UpdateScreen::DontRedraw, azul::prelude::TerminateDaemon::Continue) } } //29 fn get_socket(app_data: Arc<Mutex<ChatDataModel>>) -> Option<UdpSocket> { //30 let ref_model = &(app_data.lock().unwrap().messaging_model.socket); //31 match ref_model { Some(s) => Some(s.try_clone().unwrap()), _ => None } } }
- ãœã±ããããã®èªã¿åãã®ãããã¯æäœãäžæããããŸã§ã®ã¿ã€ã ã¢ãŠãïŒããªç§åäœïŒã
- ãã®æ©èœã¯ããŠãŒã¶ãŒããµãŒããŒã«æ°ããã¡ãã»ãŒãžãéä¿¡ãããšãã«æ©èœããŸãã
- ããŒã¿ã¢ãã«ã§ãã¥ãŒããã¯ã¹ãååŸããŸãã ããã«ããããã¥ãŒããã¯ã¹ã解æŸããããŸã§ãã€ã³ã¿ãŒãã§ã€ã¹ã®åæç»ã¹ã¬ããããããã¯ãããŸãã
- ãŠãŒã¶ãŒãå ¥åããããã¹ãã®ã³ããŒãäœæããŠããã«è»¢éããããã¹ãå ¥åãã£ãŒã«ããã¯ãªã¢ããŸãã
- ã¡ãã»ãŒãžãéä¿¡ããŠããŸãã
- ãã®ã€ãã³ããåŠçããåŸãã€ã³ã¿ãŒãã§ã€ã¹ãåæç»ããå¿ èŠãããããšããã¬ãŒã ã¯ãŒã¯ã«éç¥ããŸãã
- ãã®æ©èœã¯ããŠãŒã¶ãŒããµãŒããŒã«æ¥ç¶ãããšãã«æ©èœããŸãã
- æšæºã©ã€ãã©ãªããã®æéã®é·ããè¡šãããã«æ§é ãæ¥ç¶ããŸãã
- ãã§ã«ãµãŒããŒã«æ¥ç¶ããŠããå Žåã¯ãé¢æ°ã®å®è¡ãäžæããã€ã³ã¿ãŒãã§ã€ã¹ãåæç»ããå¿ èŠããªãããšããã¬ãŒã ã¯ãŒã¯ã«éç¥ããŸãã
- Azulãã¬ãŒã ã¯ãŒã¯ã®ã¹ã¬ããããŒã«ããã¹ã¬ããã§éåæã«å®è¡ãããã¿ã¹ã¯ãè¿œå ããŸãã ããŒã¿ã¢ãã«ã䜿çšããmutexã®åŒã³åºãã¯ãmutexã解æŸããããŸã§UIã®æŽæ°ããããã¯ããŸãã
- ã¡ã€ã³ã¹ã¬ããã§å®è¡ãããå®æçãªã¿ã¹ã¯ãè¿œå ããŸãã ãã®ããŒã¢ã³ã§ã®é·æéã®èšç®ã¯ãã€ã³ã¿ãŒãã§ãŒã¹ã®æŽæ°ã«ãã£ãŠãããã¯ãããŸãã
- ãã¥ãŒããã¯ã¹ãååŸããŸãã
- ãŠãŒã¶ãŒãå ¥åããããŒããèªã¿åããããã«åºã¥ããŠããŒã«ã«ã¢ãã¬ã¹ãäœæãããªãã¹ã³ããŸãã
- ããŒã«ã«ã¢ãã¬ã¹ã«å°çãããã±ãããèªã¿åãUDPãœã±ãããäœæããŸãã
- ãŠãŒã¶ãŒãå ¥åãããµãŒããŒã¢ãã¬ã¹ãèªã¿åããŸãã
- ãã®ãµãŒããŒããã®ã¿ãã±ãããèªã¿åãããã«UDPãœã±ããã«æ瀺ããŸãã
- ãœã±ããããã®èªã¿åãæäœã®ã¿ã€ã ã¢ãŠããèšå®ããŸãã ãœã±ãããžã®æžã蟌ã¿ã¯åŸ æ©ããã«è¡ãããŸããã€ãŸããããŒã¿ãæžã蟌ãã ãã§äœãåŸ ããããœã±ããããã®èªã¿åãæäœã¯ã¹ããªãŒã ããããã¯ããèªã¿åãå¯èœãªããŒã¿ãå°çãããŸã§åŸ æ©ããŸãã ã¿ã€ã ã¢ãŠããèšå®ããªãå Žåããœã±ããããã®èªã¿åãæäœã¯ç¡æéã«åŸ æ©ããŸãã
- ãŠãŒã¶ãŒããµãŒããŒã«æ¢ã«æ¥ç¶ããŠããããšã瀺ããã©ã°ãèšå®ããŸãã
- äœæãããœã±ãããããŒã¿ã¢ãã«ã«æž¡ããŸãã
- ãã®ã€ãã³ããåŠçããåŸãã€ã³ã¿ãŒãã§ã€ã¹ãåæç»ããå¿ èŠãããããšããã¬ãŒã ã¯ãŒã¯ã«éç¥ããŸãã
- Azulãã¬ãŒã ã¯ãŒã¯ã®ã¹ã¬ããããŒã«ã§å®è¡ãããéåææäœã
- ããŒã¿ã¢ãã«ãããœã±ããã®ã³ããŒãååŸããŸãã
- ãœã±ããããããŒã¿ãèªã¿åãããšããŠããŸãã ãœã±ããã®ã³ããŒãäœæãããããŒã¿ã¢ãã«ã®ãã¥ãŒããã¯ã¹ã«ãããœã±ããããã¡ãã»ãŒãžãå°çãããŸã§ããã§çŽæ¥åŸ æ©ããå Žåããã¥ãŒããã¯ã¹ã解æŸãããŸã§ã€ã³ã¿ãŒãã§ã€ã¹å šäœã®æŽæ°ãåæ¢ããŸãã
- äœããã®ã¡ãã»ãŒãžã衚瀺ãããå ŽåãããŒã¿ã¢ãã«ãå€æŽãããšãmodifyã¯lockïŒïŒãšåãããšãè¡ããŸããçµæãã©ã ãã«æž¡ããã©ã ãã³ãŒãã®çµäºåŸã«ãã¥ãŒããã¯ã¹ã解æŸããŠãã©ããã解é€ããŸãã
- æ°ããã¡ãã»ãŒãžãããããšã瀺ããã©ã°ãèšå®ããŸãã
- ãã¹ãŠã®ãã£ããã¡ãã»ãŒãžã®é åã«ã¡ãã»ãŒãžãè¿œå ããŸãã
- ã¡ã€ã³ã¹ã¬ããã§å®è¡ãããç¹°ãè¿ãåææäœã
- æ°ããã¡ãã»ãŒãžãããå Žåã¯ãã€ã³ã¿ãŒãã§ã€ã¹ãæåããåæç»ããŠãã®ããŒã¢ã³ã§äœæ¥ãç¶ããå¿ èŠãããããšããã¬ãŒã ã¯ãŒã¯ã«éç¥ããŸããããã§ãªãå Žåã¯ãæåããã€ã³ã¿ãŒãã§ã€ã¹ãæç»ããã«ã次ã®ãµã€ã¯ã«ã§ãã®é¢æ°ãåŒã³åºããŸãã
- ãã¥ãŒããã¯ã¹ãããŒã¿ã¢ãã«ã§ããã¯ãããªãããã«ããœã±ããã®ã³ããŒãäœæããŸãã
- ãã¥ãŒããã¯ã¹ãååŸãããœã±ãããžã®ãªã³ã¯ãååŸããŸãã
- ãœã±ããã®ã³ããŒãäœæããŸãã é¢æ°ãçµäºãããšããã¥ãŒããã¯ã¹ã¯èªåçã«è§£æŸãããŸãã
Azulã®éåæããŒã¿åŠçãšããŒã¢ã³
// Problem - blocks UI :( fn start_connection(app_state: &mut AppState<MyDataModel>, _event: WindowEvent<MyDataModel>) -> UpdateScreen { // app_state.add_task(start_async_task, &[]); // app_state.add_daemon(Daemon::unique(DaemonCallback(start_daemon))); UpdateScreen::Redraw } fn start_daemon(state: &mut MyDataModel, _repres: &mut Apprepres) -> (UpdateScreen, TerminateDaemon) { // UI thread::sleep(Duration::from_secs(10)); state.counter += 10000; (UpdateScreen::Redraw, TerminateDaemon::Continue) } fn start_async_task(app_data: Arc<Mutex<MyDataModel>>, _: Arc<()>) { // simulate slow load app_data.modify(|state| { // UI thread::sleep(Duration::from_secs(10)); state.counter += 10000; }); }
ããŒã¢ã³ã¯åžžã«ã¡ã€ã³ã¹ã¬ããã§å®è¡ãããããããããã¯ã¯é¿ããããŸããã éåæã¿ã¹ã¯ã®å Žåãããšãã°ãã®ããã«ãããšã10ç§éããã¯ãããªããªããŸãã
fn start_async_task(app_data: Arc<Mutex<MyDataModel>>, _: Arc<()>) { // UI. . thread::sleep(Duration::from_secs(10)); app_data.modify(|state| { state.counter += 10000; }); }
modifyé¢æ°ã¯lockïŒïŒãåŒã³åºããããŒã¿ã¢ãã«ã§mutexãå®è¡ãããéãã€ã³ã¿ãŒãã§ã€ã¹ã®æŽæ°ããããã¯ããŸãã
ç§ãã¡ã®ã¹ã¿ã€ã«
const CUSTOM_CSS: &str = " .row { height: 50px; } .orange { background: linear-gradient(to bottom, #f69135, #f37335); font-color: white; border-bottom: 1px solid #8d8d8d; }";
å®éããŠãŒã¶ãŒã«è¡šç€ºããDOMãäœæããããã®é¢æ°
impl azul::prelude::Layout for ChatDataModel { //1 fn layout(&self, info: azul::prelude::WindowInfo<Self>) -> azul::prelude::Dom<Self> { //2 if self.logged_in { self.chat_form(info) } else { self.login_form(info) } } } impl ChatDataModel { //3 fn login_form(&self, info: azul::prelude::WindowInfo<Self>) -> azul::prelude::Dom<Self> { //4 let button = azul::widgets::button::Button::with_label("Login") //5 .dom() //6 .with_class("row") //7 .with_class("orange") //8 .with_callback( azul::prelude::On::MouseUp, azul::prelude::Callback(Controller::login_pressed)); //9 let port_label = azul::widgets::label::Label::new("Enter port to listen:") .dom() .with_class("row"); //10 let port = azul::widgets::text_input::TextInput::new() //11 .bind(info.window, &self.login_model.port_input, &self) .dom(&self.login_model.port_input) .with_class("row"); // 9 let address_label = azul::widgets::label::Label::new("Enter server address:") .dom() .with_class("row"); //10 let address = azul::widgets::text_input::TextInput::new() //11 .bind(info.window, &self.login_model.address_input, &self) .dom(&self.login_model.address_input) .with_class("row"); //12 azul::prelude::Dom::new(azul::prelude::NodeType::Div) .with_child(port_label) .with_child(port) .with_child(address_label) .with_child(address) .with_child(button) } //13 fn chat_form(&self, info: azul::prelude::WindowInfo<Self>) -> azul::prelude::Dom<Self> { //14 let button = azul::widgets::button::Button::with_label("Send") .dom() .with_class("row") .with_class("orange") .with_callback(azul::prelude::On::MouseUp, azul::prelude::Callback(Controller::send_pressed)); //15 let text = azul::widgets::text_input::TextInput::new() .bind(info.window, &self.messaging_model.text_input_state, &self) .dom(&self.messaging_model.text_input_state) .with_class("row"); //12 let mut dom = azul::prelude::Dom::new(azul::prelude::NodeType::Div) .with_child(text) .with_child(button); //16 for i in &self.messaging_model.messages { dom.add_child(azul::widgets::label::Label::new(i.clone()).dom().with_class("row")); } dom } }
- æçµçãªDOMãäœæããã€ã³ã¿ãŒãã§ã€ã¹ãåæç»ããå¿ èŠããããã³ã«åŒã³åºãããé¢æ°ã
- ãã§ã«ãµãŒããŒã«æ¥ç¶ããŠããå Žåã¯ãã¡ãã»ãŒãžãéä¿¡ããã³èªã¿åãããã®ãã©ãŒã ã衚瀺ããŸãããã以å€ã®å Žåã¯ããµãŒããŒã«æ¥ç¶ããããã®ãã©ãŒã ã衚瀺ããŸãã
- ãµãŒããŒãžã®æ¥ç¶ã«å¿ èŠãªããŒã¿ãå ¥åããããã®ãã©ãŒã ãäœæããŸãã
- ãã°ã€ã³ã®ããã¹ãç¢æã§ãã¿ã³ãäœæããŸãã
- ãããDOMãªããžã§ã¯ãã«å€æããŸãã
- è¡ã¯ã©ã¹ãè¿œå ããŸãã
- CSSã¯ã©ã¹ãªã¬ã³ãžãè¿œå ããŸãã
- ãã¿ã³ãã¯ãªãã¯ããã€ãã³ããã³ãã©ãŒãè¿œå ããŸãã
- ãŠãŒã¶ãŒãšcssã¯ã©ã¹ã®è¡ã«è¡šç€ºããããã¹ããå«ãããã¹ãã©ãã«ãäœæããŸãã
- ã¢ãã«ã®ããããã£ãšcssã¯ã©ã¹ã®è¡ã®ããã¹ãã䜿çšããŠããã¹ããå ¥åããããã®ããã¹ãããã¯ã¹ãäœæããŸãã
- ããã¹ããã£ãŒã«ããDataModelã®ããããã£ã«ãã€ã³ãããŸãã ããã¯åæ¹åãã€ã³ãã£ã³ã°ã§ãã TextInputãç·šéãããšãã¢ãã«ã®ããããã£å ã®ããã¹ããèªåçã«å€æŽãããŸããéãåæ§ã§ãã ã¢ãã«ã®ããã¹ããå€æŽãããšãTextInputã®ããã¹ããå€æŽãããŸãã
- UIèŠçŽ ãé 眮ããã«ãŒãDOMèŠçŽ ãäœæããŸãã
- ã¡ãã»ãŒãžãéåä¿¡ããããã®ãã©ãŒã ãäœæããŸãã
- ã¯ãªãã¯ãããšãããã¹ããéä¿¡ããšã¯ã©ã¹ãè¡ããããªã¬ã³ãžããCSSãå«ããã¿ã³ãäœæããŸãã
- ã¢ãã«ããããã£self.messaging_model.text_input_stateã®åæ¹åãã€ã³ãã£ã³ã°ãšã¯ã©ã¹ãrowãã®cssã§ããã¹ãå ¥åãã£ãŒã«ããäœæããŸãã
- ãã£ããã§äœæãããã¡ãã»ãŒãžã衚瀺ããããã¹ãã©ãã«ãè¿œå ããŸãã
ã€ã³ã¿ãŒãã§ãŒã¹ã®ç¶æ ãä¿åããã¢ãã«
Azulã®ããã¥ã¡ã³ãã«ã¯ãããŒã¿ããŒã¹ãžã®æ¥ç¶ãå«ããã¹ãŠã®ã¢ããªã±ãŒã·ã§ã³ããŒã¿ãæ ŒçŽããå¿ èŠããããšæžãããŠãããããUDPãœã±ãããããã«é 眮ããŸãã
//1 #[derive(Debug)] //2 struct ChatDataModel { //3 logged_in: bool, //4 messaging_model: MessagingDataModel, //5 login_model: LoginDataModel, } #[derive(Debug, Default)] struct LoginDataModel { //6 port_input: azul::widgets::text_input::TextInputState, //7 address_input: azul::widgets::text_input::TextInputState, } #[derive(Debug)] struct MessagingDataModel { //8 text_input_state: azul::widgets::text_input::TextInputState, //9 messages: Vec<String>, //10 socket: Option<UdpSocket>, //11 has_new_message: bool, }
- ããã«ããã{:?}ã®åœ¢åŒã®ãã³ãã¬ãŒãã«æååãšããŠæ§é ã衚瀺ã§ããŸãã
- ããŒã¿ã¢ãã«ã Azulã§äœ¿çšã§ããããã«ã 圌女ã¯ã¬ã€ã¢ãŠãç¹æ§ãå®è£ ããå¿ èŠããããŸãã
- ãŠãŒã¶ãŒããµãŒããŒã«æ¥ç¶ãããŠãããã©ããã確èªãããã©ã°ã
- ãµãŒããŒã«ã¡ãã»ãŒãžãéä¿¡ãããµãŒããŒããåä¿¡ããã¡ãã»ãŒãžãä¿åããããã®ãã©ãŒã ã衚瀺ããããã®ã¢ãã«ã
- ãµãŒããŒã«æ¥ç¶ããããã®ãã©ãŒã ã衚瀺ããããã®ã¢ãã«ã
- ãŠãŒã¶ãŒãå ¥åããããŒãã ãœã±ããã§ããããªãã¹ã³ããŸãã
- ãŠãŒã¶ãŒãå ¥åãããµãŒããŒã®ã¢ãã¬ã¹ã æ¥ç¶ããŸãã
- ãŠãŒã¶ãŒã¡ãã»ãŒãžã ãµãŒããŒã«éä¿¡ããŸãã
- ãµãŒããŒããéä¿¡ãããã¡ãã»ãŒãžã®é åã
- ãµãŒããŒãšéä¿¡ããããã®ãœã±ããã
- ãµãŒããŒããæ°ããã¡ãã»ãŒãžãå°çãããã©ããã確èªãããã©ã°ã
æåŸã«ãã¢ããªã±ãŒã·ã§ã³ãžã®ã¡ã€ã³ãšã³ããªãã€ã³ãã GUIæç»ãšãŠãŒã¶ãŒå ¥ååŠçãããµã€ã¯ã«ãéå§ããŸã
pub fn run() { //1 let app = azul::prelude::App::new(ChatDataModel { logged_in: false, messaging_model: MessagingDataModel { text_input_state: azul::widgets::text_input::TextInputState::new(""), messages: Vec::new(), socket: None, has_new_message: false, }, login_model: LoginDataModel::default(), }, azul::prelude::AppConfig::default()); // 2 let mut style = azul::prelude::css::native(); //3 style.merge(azul::prelude::css::from_str(CUSTOM_CSS).unwrap()); //4 let window = azul::prelude::Window::new(azul::prelude::WindowCreateOptions::default(), style).unwrap(); //5 app.run(window).unwrap(); }
- éå§ããŒã¿ã䜿çšããŠã¢ããªã±ãŒã·ã§ã³ãäœæããŸãã
- ããã©ã«ãã§ã¢ããªã±ãŒã·ã§ã³ã䜿çšããã¹ã¿ã€ã«ã
- ç¬èªã®ã¹ã¿ã€ã«ãè¿œå ããŸãã
- ã¢ããªã±ãŒã·ã§ã³ã衚瀺ãããŠã£ã³ããŠãäœæããŸãã
- ãã®ãŠã£ã³ããŠã§ã¢ããªã±ãŒã·ã§ã³ãèµ·åããŸãã
ãµãŒããŒ
ã¢ããªã±ãŒã·ã§ã³ãžã®ã¡ã€ã³ãšã³ããªãã€ã³ã
éåžžãããã«ã¯ã³ã³ãœãŒã«ã¢ããªã±ãŒã·ã§ã³ããããŸãã
pub fn run() { //1 let socket = create_socket(); //2 let (sx, rx) = mpsc::channel(); //3 start_sender_thread(rx, socket.try_clone().unwrap()); loop { //4 sx.send(read_data(&socket)).unwrap(); } }
- ãœã±ãããäœæããŸãã
- 1ã€ã®sxã¡ãã»ãŒãžéä¿¡è ãšå€ãã®rxåä¿¡è ã§äžæ¹åã®ãã£ãã«ãäœæããŸãã
- å¥ã®ã¹ããªãŒã ã§ãã¹ãŠã®åä¿¡è ã«ã¡ãã»ãŒãžãéä¿¡ãå§ããŸãã
- ãœã±ããããããŒã¿ãèªã¿åã£ãŠã¹ããªãŒã ã«éä¿¡ãããµãŒããŒã«æ¥ç¶ãããŠããã¯ã©ã€ã¢ã³ãã«ã¡ãã»ãŒãžãé ä¿¡ããŸãã
ã¯ã©ã€ã¢ã³ãã«ã¡ãã»ãŒãžãéä¿¡ããããã®ã¹ããªãŒã ãäœæããæ©èœ
fn start_sender_thread(rx: mpsc::Receiver<(Vec<u8>, SocketAddr)>, socket: UdpSocket) { //1 thread::spawn(move || { //2 let mut addresses = Vec::<SocketAddr>::new(); //3 loop { //4 let (bytes, pre) = rx.recv().unwrap(); // 5 if !addresses.contains(&pre) { println!(" {} connected to server", pre); addresses.push(pre.clone()); } //6 let result = String::from_utf8(bytes) .expect("can't parse to String") .trim() .to_string(); println!("received {} from {}", result, pre); //7 let message = format!("FROM: {} MESSAGE: {}", pre, result); let data_to_send = message.as_bytes(); //8 addresses .iter() .for_each(|s| { //9 socket.send_to(data_to_send, s) //10 .expect(format!("can't send to {}", pre).as_str()); }); } }); }
- æ°ããã¹ã¬ãããéå§ããŸãã moveã¯ãå€æ°ãããããã©ã ããšãããŒãåŒãç¶ãããšãæå³ããŸãã ããå ·äœçã«ã¯ãæ°ããã¹ã¬ããã¯rxããã³ãœã±ããå€æ°ããåžåãããŸãã
- 顧客ã«ãã£ãŠç§ãã¡ã«æ¥ç¶ãããã¢ãã¬ã¹ã®ã³ã¬ã¯ã·ã§ã³ã ããããã¹ãŠã«ã¡ãã»ãŒãžãéä¿¡ããŸãã äžè¬çã«ãå®éã®ãããžã§ã¯ãã§ã¯ãã¯ã©ã€ã¢ã³ããç§ãã¡ããåæãããã®é åããã¢ãã¬ã¹ãåé€ããããšãåŠçããå¿ èŠããããŸãã
- ç¡éã«ãŒããéå§ããŸãã
- ãã£ãã«ããããŒã¿ãèªã¿åããŸãã ããã§ã¯ãæ°ããããŒã¿ãå°çãããŸã§ã¹ããªãŒã ããããã¯ãããŸãã
- é åã«ãã®ãããªã¢ãã¬ã¹ããªãå Žåã¯ãããã«è¿œå ããŸãã
- ãã€ãé åããUTF8æååããã³ãŒãããŸãã
- ãã¹ãŠã®é¡§å®¢ã«éä¿¡ãããã€ãã®é åãäœæããŸãã
- ã¢ãã¬ã¹ã®ã³ã¬ã¯ã·ã§ã³ã調ã¹ãŠãå šå¡ã«ããŒã¿ãéä¿¡ããŸãã
- UDPãœã±ãããžã®æžã蟌ã¿æäœã¯ãããã¯ãããªããããããã§ã¯ãé¢æ°ã¯åä¿¡è ã«ã¡ãã»ãŒãžãå°çããŠã»ãŒå³åº§ã«å®è¡ããããŸã§åŸ æ©ããŸããã
- ãšã©ãŒãçºçããå Žåã¯ãæå®ãããã¡ãã»ãŒãžã䜿çšããŠããã°ã©ã ãç·æ¥çµäºããŸãã
ãã®é¢æ°ã¯ããŠãŒã¶ãŒå ¥åã«åºã¥ããŠãœã±ãããäœæããŸã
const TIMEOUT_IN_MILLIS: u64 = 2000; fn create_socket() -> UdpSocket { println!("Enter port to listen"); //1 let local_port: String = read!("{}\n"); let local_address = format!("127.0.0.1:{}", local_port.trim()); println!("server address {}", &local_address); //2 let socket = UdpSocket::bind(&local_address.trim()) .expect(format!("can't bind socket to {}", &local_address).as_str()); //3 socket.set_read_timeout(Some(Duration::from_millis(TIMEOUT_IN_MILLIS))) .expect("can't set time out to read"); //4 socket }
- ãµãŒããŒããªãã¹ã³ããããŒããèªã¿åããããã«åºã¥ããŠããŒã«ã«ãµãŒããŒã¢ãã¬ã¹ãäœæããŸãã
- ãã®ã¢ãã¬ã¹ã§ãªãã¹ã³ããUDPãœã±ãããäœæããŸãã
- èªã¿åãæäœã®ã¿ã€ã ã¢ãŠããèšå®ããŸãã èªã¿åãæäœã¯ãããã¯ãããŠãããæ°ããããŒã¿ãå°çããããã¿ã€ã ã¢ãŠããçºçãããŸã§ãããŒããããã¯ããŸãã
- Functionããäœæããããœã±ãããè¿ããŸãã
- ãã®é¢æ°ã¯ããœã±ããããããŒã¿ãèªã¿åããéä¿¡è ã¢ãã¬ã¹ãšãšãã«ãããè¿ããŸãã
ãœã±ããããããŒã¿ãèªã¿åãé¢æ°
fn read_data(socket: &UdpSocket) -> (Vec<u8>, SocketAddr) { //1 let mut buf = [0u8; 4096]; //2 loop { match socket.recv_from(&mut buf) { //3 Ok((count, address)) => { //4 return (buf[..count].into(), address); } //5 Err(e) => { println!("Error {}", e); continue; } }; } }
- ãããã¡ãŒã¯ãããŒã¿ãèªã¿åãå Žæã§ãã
- æå¹ãªããŒã¿ãèªã¿åããããŸã§å®è¡ãããã«ãŒããéå§ããŸãã
- èªã¿åããããã€ãæ°ãšéä¿¡è ã¢ãã¬ã¹ãååŸããŸãã
- é åã®å é ããèªã¿åã£ããã€ãæ°ãŸã§ã®é åã®ã¹ã©ã€ã¹ãäœæããããããã€ããã¯ãã«ã«å€æããŸãã
- ã¿ã€ã ã¢ãŠããŸãã¯ãã®ä»ã®ãšã©ãŒãçºçããå Žåã¯ãã«ãŒãã®æ¬¡ã®å埩ã«é²ã¿ãŸãã
ã¢ããªã±ãŒã·ã§ã³ã®ã¬ã€ã€ãŒã«ã€ããŠ
ãªããããã¯ïŒ6æ2æ¥ã®è·å Žã§ã®å°ããªæè²ããã°ã©ã ã ããã«çœ®ãããšã«ããŸããã誰ãã圹ã«ç«ã€ãããããŸããã 6æã®ã·ã£ãŒããªè©©äººã¯CïŒã®äŸã§ãããASP.NETã«ã€ããŠè©±ããŠãã
ããã§ãäœããããŸããã§ãããããã¯å€æ¹ã§ããããããŠãç§ã¯ã¢ã«ãã ãšãã¯ã¿ãŒã®ããã«å»ºç¯ã«é¢ããå°ããªæè²ããã°ã©ã ãæžãããšã«æ±ºããŸããã ãããè¡ããŸãããã
å®éãããã«è¿œå ããã®ã¯ãã¢ãŒãã®å埩ãš1é±éã«1åããèšäºãæžãããšãã§ãããè³æãæ¢ã«ããã«ãããæ¥é±ã¯Habrã«äœãä»ã®ãã®ãã¢ããããŒããããã£ãããã§ãã
éåžžãã¢ããªã±ãŒã·ã§ã³ã¯éå±€åãããŸãã åã¬ã€ã€ãŒã«ã¯ãããããé 眮ãããŠããã¬ã€ã€ãŒã®åäœç¹æ§ãå®è£ ãããªããžã§ã¯ããå«ãŸããŠããŸãã ãªã©ã ãããã¯ã¬ã€ã€ãŒã§ãã
åã¬ã€ã€ãŒã«ã¯ãç¬èªã®DTOãšãä»»æã®ã¡ãœãããæã€å®å šã«ä»»æã®ã¯ã©ã¹ãå«ããããšãã§ããŸãã äž»ãªããšã¯ãããããé 眮ãããŠããã¬ã€ã€ãŒã«é¢é£ããæ©èœãå®è¡ããããšã§ãã åçŽãªã¢ããªã±ãŒã·ã§ã³ã§ã¯ãäžéšã®ã¬ã€ã€ãŒãæ¬ èœããŠããå ŽåããããŸãã ããšãã°ãã¬ã€ã€ãŒãã¥ãŒã¯ãMVCãMVPãMVVMãã¿ãŒã³ãä»ããŠå®è£ ã§ããŸãã ããã¯å®å šã«ãªãã·ã§ã³ã§ãã äž»ãªããšã¯ããã®ã¬ã€ã€ãŒã«ããã¯ã©ã¹ãã¬ã€ã€ãŒã«å²ãåœãŠãããæ©èœãå®è£ ããããšã§ãã ãã¿ãŒã³ãšã¢ãŒããã¯ãã£ã¯åãªãæšå¥šäºé ã§ãããæ瀺ã§ã¯ãªãããšã«æ³šæããŠãã ããã ãã¿ãŒã³ãšã¢ãŒããã¯ãã£ã¯æ³åŸã§ã¯ãããŸãããããã¯ã¢ããã€ã¹ã§ãã
ãããã£ãŠãæšæºã®Entity Frameworkã䜿çšããæšæºã®ASP.NETã¢ããªã±ãŒã·ã§ã³ã®äŸã䜿çšããŠãåã¬ã€ã€ãŒãèŠãŠã¿ãŸãããã
ããã«MVCããããŸãã ããã¯ããŠãŒã¶ãŒã€ã³ã¿ã©ã¯ã·ã§ã³ãæäŸããã¬ã€ã€ãŒã§ãã ã³ãã³ãã¯ããã«ããããŠãŒã¶ãŒã¯ããããããŒã¿ãååŸããŸãã å¿ ããã人ã ã§ã¯ãããŸãããAPIãããã°ããŠãŒã¶ãŒã¯å¥ã®ããã°ã©ã ã§ãã è»ã¯è»ãšéä¿¡ããŸãã
ããã§ã¯ãéåžžãã¯ã©ã¹ã¯UserServiceãªã©ã®ServiceãšåŒã°ããŸãããããã¯äœã§ãããŸããŸããã ã¡ãœãããæã€ã¯ã©ã¹ã®ã»ããã äž»ãªããšã¯ãã¢ããªã±ãŒã·ã§ã³ã®èšç®ãšèšç®ãããã§è¡ãããããšã§ãã ããã¯æãåããŠããã°ãå±€ã§ãã ã»ãšãã©ã®ã³ãŒããšããŸããŸãªã¯ã©ã¹ããããŸãã ããã¯å®éãç§ãã¡ã®ã¢ããªã±ãŒã·ã§ã³ã§ãã
éåžžãããã§EFã¯äœæ¥åäœãšãªããžããªã®ãã¿ãŒã³ãå®è£ ããŸãã ãã®ãããDbContextã¯äœæ¥åäœã§ãããDBã¯ããããªããžããªãšèšå®ããŸãã å®éãããã¯ããŒã¿ãé 眮ããããããããŒã¿ãååŸããå Žæã§ãã ããŒã¿ãœãŒã¹ãããŒã¿ããŒã¹ãå¥ã®ã¢ããªã±ãŒã·ã§ã³ã®APIãã¡ã¢ãªå ã®ãã£ãã·ã¥ããŸãã¯äœããã®çš®é¡ã®ä¹±æ°ãžã§ãã¬ãŒã¿ãŒã§ãããã©ããã«é¢ä¿ãªãã ä»»æã®ããŒã¿ãœãŒã¹ã
ã¯ããããããçš®é¡ã®ãŠãŒã¶ãŒãåç©ãªã©ã§ãã 1ã€ã®éèŠãªãã€ã³ã-圌ãã¯åœŒãã ãã«ç¹åŸŽçãªããçš®ã®è¡åãæã£ãŠãããããããŸããã äŸïŒ
å®éãããã«è¿œå ããã®ã¯ãã¢ãŒãã®å埩ãš1é±éã«1åããèšäºãæžãããšãã§ãããè³æãæ¢ã«ããã«ãããæ¥é±ã¯Habrã«äœãä»ã®ãã®ãã¢ããããŒããããã£ãããã§ãã
éåžžãã¢ããªã±ãŒã·ã§ã³ã¯éå±€åãããŸãã åã¬ã€ã€ãŒã«ã¯ãããããé 眮ãããŠããã¬ã€ã€ãŒã®åäœç¹æ§ãå®è£ ãããªããžã§ã¯ããå«ãŸããŠããŸãã ãªã©ã ãããã¯ã¬ã€ã€ãŒã§ãã
- ãã¬ãŒã³ããŒã·ã§ã³å±€ã
- ããžãã¹ããžãã¯ã®ã¬ã€ã€ãŒã
- ããŒã¿ã¢ã¯ã»ã¹å±€ã
- ãšã³ãã£ãã£ïŒãŠãŒã¶ãŒãåç©ãªã©ïŒ
åã¬ã€ã€ãŒã«ã¯ãç¬èªã®DTOãšãä»»æã®ã¡ãœãããæã€å®å šã«ä»»æã®ã¯ã©ã¹ãå«ããããšãã§ããŸãã äž»ãªããšã¯ãããããé 眮ãããŠããã¬ã€ã€ãŒã«é¢é£ããæ©èœãå®è¡ããããšã§ãã åçŽãªã¢ããªã±ãŒã·ã§ã³ã§ã¯ãäžéšã®ã¬ã€ã€ãŒãæ¬ èœããŠããå ŽåããããŸãã ããšãã°ãã¬ã€ã€ãŒãã¥ãŒã¯ãMVCãMVPãMVVMãã¿ãŒã³ãä»ããŠå®è£ ã§ããŸãã ããã¯å®å šã«ãªãã·ã§ã³ã§ãã äž»ãªããšã¯ããã®ã¬ã€ã€ãŒã«ããã¯ã©ã¹ãã¬ã€ã€ãŒã«å²ãåœãŠãããæ©èœãå®è£ ããããšã§ãã ãã¿ãŒã³ãšã¢ãŒããã¯ãã£ã¯åãªãæšå¥šäºé ã§ãããæ瀺ã§ã¯ãªãããšã«æ³šæããŠãã ããã ãã¿ãŒã³ãšã¢ãŒããã¯ãã£ã¯æ³åŸã§ã¯ãããŸãããããã¯ã¢ããã€ã¹ã§ãã
ãããã£ãŠãæšæºã®Entity Frameworkã䜿çšããæšæºã®ASP.NETã¢ããªã±ãŒã·ã§ã³ã®äŸã䜿çšããŠãåã¬ã€ã€ãŒãèŠãŠã¿ãŸãããã
ãã¬ãŒã³ããŒã·ã§ã³å±€
ããã«MVCããããŸãã ããã¯ããŠãŒã¶ãŒã€ã³ã¿ã©ã¯ã·ã§ã³ãæäŸããã¬ã€ã€ãŒã§ãã ã³ãã³ãã¯ããã«ããããŠãŒã¶ãŒã¯ããããããŒã¿ãååŸããŸãã å¿ ããã人ã ã§ã¯ãããŸãããAPIãããã°ããŠãŒã¶ãŒã¯å¥ã®ããã°ã©ã ã§ãã è»ã¯è»ãšéä¿¡ããŸãã
ããžãã¹ããžãã¯å±€
ããã§ã¯ãéåžžãã¯ã©ã¹ã¯UserServiceãªã©ã®ServiceãšåŒã°ããŸãããããã¯äœã§ãããŸããŸããã ã¡ãœãããæã€ã¯ã©ã¹ã®ã»ããã äž»ãªããšã¯ãã¢ããªã±ãŒã·ã§ã³ã®èšç®ãšèšç®ãããã§è¡ãããããšã§ãã ããã¯æãåããŠããã°ãå±€ã§ãã ã»ãšãã©ã®ã³ãŒããšããŸããŸãªã¯ã©ã¹ããããŸãã ããã¯å®éãç§ãã¡ã®ã¢ããªã±ãŒã·ã§ã³ã§ãã
ããŒã¿ã¢ã¯ã»ã¹å±€
éåžžãããã§EFã¯äœæ¥åäœãšãªããžããªã®ãã¿ãŒã³ãå®è£ ããŸãã ãã®ãããDbContextã¯äœæ¥åäœã§ãããDBã¯ããããªããžããªãšèšå®ããŸãã å®éãããã¯ããŒã¿ãé 眮ããããããããŒã¿ãååŸããå Žæã§ãã ããŒã¿ãœãŒã¹ãããŒã¿ããŒã¹ãå¥ã®ã¢ããªã±ãŒã·ã§ã³ã®APIãã¡ã¢ãªå ã®ãã£ãã·ã¥ããŸãã¯äœããã®çš®é¡ã®ä¹±æ°ãžã§ãã¬ãŒã¿ãŒã§ãããã©ããã«é¢ä¿ãªãã ä»»æã®ããŒã¿ãœãŒã¹ã
ãšã³ãã£ãã£
ã¯ããããããçš®é¡ã®ãŠãŒã¶ãŒãåç©ãªã©ã§ãã 1ã€ã®éèŠãªãã€ã³ã-圌ãã¯åœŒãã ãã«ç¹åŸŽçãªããçš®ã®è¡åãæã£ãŠãããããããŸããã äŸïŒ
class User { public string FirstName { get; set; } public string LastName { get; set; } public string FullName { get { return FirstName + " " + LastName; } } public bool Equal(User user) { return this.FullName == user.FullName; } }
ããŠãéåžžã«ç°¡åãªäŸã§ãã ãããã°
using System; using System.Collections.Generic; using System.Text; //Entities class User { public int Id { get; set; } public string Name { get; set; } } //Data Access Layer class UserRepository { private readonly Dictionary<int, User> _db; public UserRepository() { _db = new Dictionary<int, User>(); } public User Get(int id) { return _db[id]; } public void Save(User user) { _db[user.Id] = user; } } //Business Logic Layer class UserService { private readonly UserRepository _repo; private int _currentId = 0; public UserService() { _repo = new UserRepository(); } public void AddNew() { _currentId++; var user = new User { Id = _currentId, Name = _currentId.ToString() }; _repo.Save(user); } public string GetAll() { StringBuilder sb = new StringBuilder(); for (int i = 1; i <= _currentId; i++) { sb.AppendLine($"Id: {i} Name: {_repo.Get(i).Name}"); } return sb.ToString(); } } //presentation Layer aka Application Layer class UserController { private readonly UserService _service; public UserController() { _service = new UserService(); } public string RunExample() { _service.AddNew(); _service.AddNew(); return _service.GetAll(); } } namespace ConsoleApp1 { class Program { static void Main(string[] args) { var controller = new UserController(); Console.WriteLine(controller.RunExample()); Console.ReadLine(); } } }
PS
ããŠãèšäºã®ææ³äžã®èª€ããä¿®æ£ããŠãããNastyaã«æè¬ããŸããããã§ããNastyaã¯ããªããèµ€ãåæ¥èšŒæžã§ç¡é§ã«ãªãããäžè¬çã«ã¯ã¯ãŒã«ã§ããæããŠã<3ã