├── .github └── FUNDING.yml ├── LICENSE ├── README.md ├── default_env.tres ├── export_presets.cfg ├── icon.png ├── icon.png.import ├── project.godot ├── rss_reader.gd └── rss_reader.tscn /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: triptych 4 | ko_fi: triptych 5 | custom: ["https://paypal.me/AndrewWooldridge?country.x=US&locale.x=en_US"] 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Andrew Wooldridge 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Make an RSS reader in Godot! 2 | 3 | Learn how to make this RSS reader yourself using my online tutorial ( Godot 3.1 and up ) 4 | * [Part one](https://andreww.xyz/tutorials/2019-02-25-creating-an-rss-reader-in-godot-part-1/) 5 | * [Part two](https://andreww.xyz/tutorials/2019-03-05-creating-an-rss-reader-in-godot-part-2/) 6 | * [Part three](https://andreww.xyz/tutorials/2019-03-17-creating-an-rss-reader-in-godot-part-3/) 7 | -------------------------------------------------------------------------------- /default_env.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Environment" load_steps=2 format=2] 2 | 3 | [sub_resource type="ProceduralSky" id=1] 4 | 5 | [resource] 6 | background_mode = 2 7 | background_sky = SubResource( 1 ) 8 | -------------------------------------------------------------------------------- /export_presets.cfg: -------------------------------------------------------------------------------- 1 | [preset.0] 2 | 3 | name="Mac OSX" 4 | platform="Mac OSX" 5 | runnable=true 6 | custom_features="" 7 | export_filter="all_resources" 8 | include_filter="" 9 | exclude_filter="" 10 | export_path="/Users/awooldridge/Documents/RSS_Reader_Tutorial.dmg" 11 | patch_list=PoolStringArray( ) 12 | script_export_mode=1 13 | script_encryption_key="" 14 | 15 | [preset.0.options] 16 | 17 | custom_package/debug="" 18 | custom_package/release="" 19 | application/name="" 20 | application/info="Made with Godot Engine" 21 | application/icon="" 22 | application/identifier="" 23 | application/signature="" 24 | application/short_version="1.0" 25 | application/version="1.0" 26 | application/copyright="" 27 | display/high_res=false 28 | codesign/identity="" 29 | codesign/entitlements="" 30 | texture_format/s3tc=true 31 | texture_format/etc=false 32 | texture_format/etc2=false 33 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/triptych/godot_reader_tutorial/f779d24479d68f07532ea7e109a5e3b1793e15cc/icon.png -------------------------------------------------------------------------------- /icon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://icon.png" 13 | dest_files=[ "res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=4 10 | 11 | _global_script_classes=[ ] 12 | _global_script_class_icons={ 13 | 14 | } 15 | 16 | [application] 17 | 18 | config/name="RSS_Reader_Tutorial" 19 | run/main_scene="res://rss_reader.tscn" 20 | config/icon="res://icon.png" 21 | 22 | [rendering] 23 | 24 | environment/default_environment="res://default_env.tres" 25 | -------------------------------------------------------------------------------- /rss_reader.gd: -------------------------------------------------------------------------------- 1 | extends Control 2 | 3 | # Declare member variables here. 4 | 5 | var title_arr = [] 6 | var desc_arr = [] 7 | var link_arr = [] 8 | 9 | # Called when the node enters the scene tree for the first time. 10 | func _ready(): 11 | load_data() 12 | 13 | # Called every frame. 'delta' is the elapsed time since the previous frame. 14 | #func _process(delta): 15 | # pass 16 | 17 | 18 | func _on_OpenButton_pressed(): 19 | print("Button pressed!") 20 | clearFields() 21 | populateEdit() 22 | 23 | 24 | func populateEdit(): 25 | #pass 26 | var url = $SettingsDialog/RSSURLText.text 27 | $HTTPRequest.request(url) 28 | 29 | func clearFields(): 30 | title_arr.clear() 31 | desc_arr.clear() 32 | link_arr.clear() 33 | $ItemList.clear() 34 | $DescriptionField.text = "" 35 | $LinkButton.text = "" 36 | 37 | func _on_HTTPRequest_request_completed(result, response_code, headers, body): 38 | $TextEdit.set_text(body.get_string_from_utf8()) 39 | 40 | #lets parse this body content 41 | var p = XMLParser.new() 42 | var in_item_node = false 43 | var in_title_node = false 44 | var in_description_node = false 45 | var in_link_node = false 46 | 47 | p.open_buffer(body) 48 | 49 | while p.read() == OK: 50 | var node_name = p.get_node_name() 51 | var node_data = p.get_node_data() 52 | var node_type = p.get_node_type() 53 | 54 | # print("node_name: " + node_name) 55 | # print("node_data: " + node_data) 56 | # print("node_type: " + node_data) 57 | 58 | if(node_name == "item"): 59 | in_item_node = !in_item_node #toggle item mode 60 | 61 | if (node_name == "title") and (in_item_node == true): 62 | in_title_node = !in_title_node 63 | continue 64 | 65 | if(node_name == "description") and (in_item_node == true): 66 | in_description_node = !in_description_node 67 | continue 68 | 69 | if(node_name == "link") and (in_item_node == true): 70 | in_link_node = !in_link_node 71 | continue 72 | 73 | if(in_description_node == true): 74 | # print("description-data" + node_data) 75 | if(node_data != ""): 76 | desc_arr.append(node_data) 77 | else: 78 | # print("description:" + node_name) 79 | desc_arr.append(node_name) 80 | 81 | if(in_title_node == true): 82 | # print("Title-data:"+ node_data) 83 | if(node_data !=""): 84 | title_arr.append(node_data) 85 | else: 86 | # print("Title:" + node_name) 87 | title_arr.append(node_name) 88 | 89 | if(in_link_node == true): 90 | # print("link-desc" + node_data) 91 | if(node_data != ""): 92 | link_arr.append(node_data) 93 | else: 94 | # print("link" + node_name) 95 | link_arr.append(node_name) 96 | 97 | # print("Titles:") 98 | for i in title_arr: 99 | # print("TITLE: " + i) 100 | $ItemList.add_item(i,null,true) 101 | 102 | func _on_ItemList_item_selected(index): 103 | $DescriptionField.text = desc_arr[index] 104 | $LinkButton.text = link_arr[index] 105 | 106 | 107 | func _on_LinkButton_pressed(): 108 | OS.shell_open($LinkButton.text) 109 | 110 | 111 | 112 | func _on_SettingsButton_pressed(): 113 | $SettingsDialog.popup() 114 | 115 | 116 | func _on_ClearButton_pressed(): 117 | $SettingsDialog/RSSURLText.text = "" 118 | 119 | func save_data(): 120 | print('saving data') 121 | print(OS.get_user_data_dir()) 122 | var save_config = File.new() 123 | var save_data = { 124 | "url": $SettingsDialog/RSSURLText.text 125 | } 126 | 127 | save_config.open("user://save_config.save", File.WRITE) 128 | save_config.store_line(to_json(save_data)) 129 | save_config.close() 130 | 131 | 132 | 133 | func _on_SaveButton_pressed(): 134 | save_data() 135 | 136 | func load_data(): 137 | print('loading data') 138 | var save_config = File.new() 139 | if not save_config.file_exists("user://save_config.save"): 140 | return #error no save game! 141 | save_config.open("user://save_config.save", File.READ) 142 | var text = save_config.get_as_text() 143 | var url = parse_json(text)['url'] 144 | print('Loading JSON: ' + text) 145 | print('URL: ' + url) 146 | 147 | $SettingsDialog/RSSURLText.text = url 148 | save_config.close() 149 | 150 | 151 | -------------------------------------------------------------------------------- /rss_reader.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [ext_resource path="res://rss_reader.gd" type="Script" id=1] 4 | 5 | [node name="RSSReader" type="Control"] 6 | anchor_right = 1.0 7 | anchor_bottom = 1.0 8 | script = ExtResource( 1 ) 9 | 10 | [node name="OpenButton" type="Button" parent="."] 11 | margin_left = 17.1204 12 | margin_top = 14.7426 13 | margin_right = 64.1204 14 | margin_bottom = 34.7426 15 | text = "Open" 16 | __meta__ = { 17 | "_edit_use_anchors_": false 18 | } 19 | 20 | [node name="TextEdit" type="TextEdit" parent="."] 21 | visible = false 22 | margin_left = 11.1466 23 | margin_top = 285.321 24 | margin_right = 342.147 25 | margin_bottom = 411.321 26 | 27 | [node name="HTTPRequest" type="HTTPRequest" parent="."] 28 | 29 | [node name="ItemList" type="ItemList" parent="."] 30 | margin_left = 19.0 31 | margin_top = 54.0 32 | margin_right = 278.0 33 | margin_bottom = 206.0 34 | 35 | [node name="DescriptionField" type="TextEdit" parent="."] 36 | margin_left = 293.573 37 | margin_top = 54.1955 38 | margin_right = 876.573 39 | margin_bottom = 200.195 40 | wrap_enabled = true 41 | 42 | [node name="LinkButton" type="LinkButton" parent="."] 43 | margin_left = 27.0 44 | margin_top = 222.0 45 | margin_right = 868.0 46 | margin_bottom = 236.0 47 | 48 | [node name="SettingsButton" type="Button" parent="."] 49 | margin_left = 803.709 50 | margin_top = 9.03579 51 | margin_right = 866.709 52 | margin_bottom = 29.0358 53 | text = "Settings" 54 | 55 | [node name="SettingsDialog" type="WindowDialog" parent="."] 56 | margin_left = 131.0 57 | margin_top = 148.0 58 | margin_right = 609.0 59 | margin_bottom = 314.0 60 | 61 | [node name="Label" type="Label" parent="SettingsDialog"] 62 | margin_left = 25.6806 63 | margin_top = 13.7915 64 | margin_right = 65.6806 65 | margin_bottom = 27.7915 66 | text = "The RSS Feed URL" 67 | 68 | [node name="RSSURLText" type="LineEdit" parent="SettingsDialog"] 69 | margin_left = 34.0 70 | margin_top = 46.0 71 | margin_right = 442.0 72 | margin_bottom = 70.0 73 | 74 | [node name="ClearButton" type="Button" parent="SettingsDialog"] 75 | margin_left = 393.77 76 | margin_top = 85.6022 77 | margin_right = 437.77 78 | margin_bottom = 105.602 79 | text = "Clear" 80 | 81 | [node name="SaveButton" type="Button" parent="SettingsDialog"] 82 | margin_left = 327.666 83 | margin_top = 84.651 84 | margin_right = 339.666 85 | margin_bottom = 104.651 86 | text = "Save" 87 | [connection signal="pressed" from="OpenButton" to="." method="_on_OpenButton_pressed"] 88 | [connection signal="request_completed" from="HTTPRequest" to="." method="_on_HTTPRequest_request_completed"] 89 | [connection signal="item_selected" from="ItemList" to="." method="_on_ItemList_item_selected"] 90 | [connection signal="pressed" from="LinkButton" to="." method="_on_LinkButton_pressed"] 91 | [connection signal="pressed" from="SettingsButton" to="." method="_on_SettingsButton_pressed"] 92 | [connection signal="pressed" from="SettingsDialog/ClearButton" to="." method="_on_ClearButton_pressed"] 93 | [connection signal="pressed" from="SettingsDialog/SaveButton" to="." method="_on_SaveButton_pressed"] 94 | --------------------------------------------------------------------------------